]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'b71f3405606d49b9735606b479c3415a0ca9810f' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 6 May 2021 09:51:22 +0000 (11:51 +0200)
committerflip1995 <philipp.krones@embecosm.com>
Thu, 6 May 2021 10:20:44 +0000 (12:20 +0200)
64 files changed:
1  2 
src/tools/clippy/.gitignore
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/implicit_return.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs
src/tools/clippy/clippy_lints/src/misc_early/double_neg.rs
src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
src/tools/clippy/clippy_lints/src/misc_early/mod.rs
src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs
src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs
src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
src/tools/clippy/clippy_lints/src/misc_early/unseparated_literal_suffix.rs
src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/unused_unit.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/diagnostics.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
src/tools/clippy/tests/ui/builtin_type_shadow.rs
src/tools/clippy/tests/ui/builtin_type_shadow.stderr
src/tools/clippy/tests/ui/comparison_chain.rs
src/tools/clippy/tests/ui/copy_iterator.rs
src/tools/clippy/tests/ui/crashes/ice-7169.rs
src/tools/clippy/tests/ui/crashes/ice-7169.stderr
src/tools/clippy/tests/ui/eval_order_dependence.rs
src/tools/clippy/tests/ui/eval_order_dependence.stderr
src/tools/clippy/tests/ui/implicit_return.fixed
src/tools/clippy/tests/ui/implicit_return.rs
src/tools/clippy/tests/ui/implicit_return.stderr
src/tools/clippy/tests/ui/infinite_loop.rs
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_collect_indirect.stderr
src/tools/clippy/tests/ui/single_char_pattern.fixed
src/tools/clippy/tests/ui/single_char_pattern.rs
src/tools/clippy/tests/ui/single_char_pattern.stderr
src/tools/clippy/tests/ui/unnecessary_filter_map.rs
src/tools/clippy/tests/ui/unused_unit.fixed
src/tools/clippy/tests/ui/unused_unit.rs
src/tools/clippy/util/lintlib.py

index 376528e30853ad9ff166b8ad2094940b7e9cea09,0000000000000000000000000000000000000000..523bab1882836e9ddc5c0c6c81db7c0733f2bfdf
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,40 @@@
 +# Used by CI to be able to push:
 +/.github/deploy_key
 +out
 +
 +# Compiled files
 +*.o
 +*.d
 +*.so
 +*.rlib
 +*.dll
 +*.pyc
 +*.rmeta
 +
 +# Executables
 +*.exe
 +
 +# Generated by Cargo
 +*Cargo.lock
 +/target
 +/clippy_lints/target
 +/clippy_utils/target
 +/clippy_workspace_tests/target
 +/clippy_dev/target
 +/lintcheck/target
 +/rustc_tools_util/target
 +
 +# Generated by dogfood
 +/target_recur/
 +
 +# gh pages docs
 +util/gh-pages/lints.json
++**/metadata_collection.json
 +
 +# rustfmt backups
 +*.rs.bk
 +
 +helper.txt
 +*.iml
 +.vscode
 +.idea
index 02d2b63c9e199c936a7c86fbbd479f85ff924afe,0000000000000000000000000000000000000000..7265d1b832376401f9a8997a9b5c4b8e2a2a8209
mode 100644,000000..100644
--- /dev/null
@@@ -1,381 -1,0 +1,381 @@@
- Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
 +# Contributing to Clippy
 +
 +Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
 +
 +**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
 +yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
 +something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
 +
 +Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
 +explains how you can contribute and how to get started.  If you have any questions about contributing or need help with
 +anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
 +
 +All contributors are expected to follow the [Rust Code of Conduct].
 +
 +- [Contributing to Clippy](#contributing-to-clippy)
 +  - [Getting started](#getting-started)
 +    - [High level approach](#high-level-approach)
 +    - [Finding something to fix/improve](#finding-something-to-fiximprove)
 +  - [Writing code](#writing-code)
 +  - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
 +    - [IntelliJ Rust](#intellij-rust)
 +    - [Rust Analyzer](#rust-analyzer)
 +  - [How Clippy works](#how-clippy-works)
 +  - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
 +    - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
 +    - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
 +    - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
 +    - [Defining remotes](#defining-remotes)
 +  - [Issue and PR triage](#issue-and-pr-triage)
 +  - [Bors and Homu](#bors-and-homu)
 +  - [Contributions](#contributions)
 +
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
 +[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
 +
 +## Getting started
 +
 +**Note: If this is your first time contributing to Clippy, you should
 +first read the [Basics docs](doc/basics.md).**
 +
 +### High level approach
 +
 +1. Find something to fix/improve
 +2. Change code (likely some file in `clippy_lints/src/`)
 +3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
 +4. Run `cargo test` in the root directory and wiggle code until it passes
 +5. Open a PR (also can be done after 2. if you run into problems)
 +
 +### Finding something to fix/improve
 +
 +All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch
 +or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date.
 +All currently active mentors can be found [here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
 +
 +Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
 +issues. You can use `@rustbot claim` to assign the issue to yourself.
 +
 +There are also some abandoned PRs, marked with [`S-inactive-closed`].
 +Pretty often these PRs are nearly completed and just need some extra steps
 +(formatting, addressing review comments, ...) to be merged. If you want to
 +complete such a PR, please leave a comment in the PR and open a new one based
 +on it.
 +
 +Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
 +and are generally easier than [`T-middle`] issues, which involve types
 +and resolved paths.
 +
 +[`T-AST`] issues will generally need you to match against a predefined syntax structure.
 +To figure out how this syntax structure is encoded in the AST, it is recommended to run
 +`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs].
 +Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
 +But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
 +
 +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
 +first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
 +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
 +debugging to find the actual problem behind the issue.
 +
 +[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
 +lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
 +an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
 +
 +[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
 +[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
 +[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
 +[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
 +[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
 +[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
 +[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43
 +[if_chain]: https://docs.rs/if_chain/*/if_chain
 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150
 +
 +## Writing code
 +
 +Have a look at the [docs for writing lints][adding_lints] for more details.
 +
 +If you want to add a new lint or change existing ones apart from bugfixing, it's
 +also a good idea to give the [stability guarantees][rfc_stability] and
 +[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
 +quick read.
 +
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md
 +[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
 +[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
 +
 +## Getting code-completion for rustc internals to work
 +
 +### IntelliJ Rust
 +Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
 +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
 +available via a `rustup` component at the time of writing.
 +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
 +`git clone https://github.com/rust-lang/rust/`.
 +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
 +which `IntelliJ Rust` will be able to understand.
 +Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 +you just cloned.
 +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
++Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
 +Just make sure to remove the dependencies again before finally making a pull request!
 +
 +[rustc_repo]: https://github.com/rust-lang/rust/
 +[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
 +
 +### Rust Analyzer
 +As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
 +using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.`
 +You will required a `nightly` toolchain with the `rustc-dev` component installed.
 +Make sure that in the `rust-analyzer` configuration, you set
 +```
 +{ "rust-analyzer.rustcSource": "discover" }
 +```
 +and
 +```
 +{ "rust-analyzer.updates.channel": "nightly" }
 +```
 +You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
 +a lot more type hints.
 +This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
 +
 +[ra_homepage]: https://rust-analyzer.github.io/
 +[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
 +
 +## How Clippy works
 +
 +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
 +For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
 +
 +```rust
 +// ./clippy_lints/src/lib.rs
 +
 +// ...
 +pub mod else_if_without_else;
 +// ...
 +
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // ...
 +    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
 +    // ...
 +
 +    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +        // ...
 +        LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +        // ...
 +    ]);
 +}
 +```
 +
 +The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
 +[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
 +that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
 +every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
 +update_lints`. When you are writing your own lint, you can use that script to save you some time.
 +
 +```rust
 +// ./clippy_lints/src/else_if_without_else.rs
 +
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +
 +// ...
 +
 +pub struct ElseIfWithoutElse;
 +
 +// ...
 +
 +impl EarlyLintPass for ElseIfWithoutElse {
 +    // ... the functions needed, to make the lint work
 +}
 +```
 +
 +The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
 +AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
 +via the `LateContext` parameter.
 +
 +That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
 +[actual lint logic][else_if_without_else] does not depend on any type information.
 +
 +[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
 +[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
 +[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
 +[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
 +[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
 +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 +
 +## Syncing changes between Clippy and [`rust-lang/rust`]
 +
 +Clippy currently gets built with a pinned nightly version.
 +
 +In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
 +that compiler hackers modify from time to time to adapt to changes in the unstable
 +API of the compiler.
 +
 +We need to sync these changes back to this repository periodically, and the changes
 +made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
 +
 +To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
 +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
 +the Rust stable release and then every other week. That way we guarantee that we keep
 +this repo up to date with the latest compiler API, and every feature in Clippy is available
 +for 2 weeks in nightly, before it can get to beta. For reference, the first sync
 +following this cadence was performed the 2020-08-27.
 +
 +This process is described in detail in the following sections. For general information
 +about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
 +
 +### Patching git-subtree to work with big repos
 +
 +Currently there's a bug in `git-subtree` that prevents it from working properly
 +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
 +Before continuing with the following steps, we need to manually apply that fix to
 +our local copy of `git-subtree`.
 +
 +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
 +Put this file under `/usr/lib/git-core` (taking a backup of the previous file)
 +and make sure it has the proper permissions:
 +
 +```bash
 +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
 +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
 +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
 +```
 +
 +_Note:_ The first time running `git subtree push` a cache has to be built. This
 +involves going through the complete Clippy history once. For this you have to
 +increase the stack limit though, which you can do with `ulimit -s 60000`.
 +Make sure to run the `ulimit` command from the same session you call git subtree.
 +
 +_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`.
 +This shell has a hardcoded recursion limit set to 1000. In order to make this process work,
 +you need to force the script to run `bash` instead. You can do this by editing the first
 +line of the `git-subtree` script and changing `sh` to `bash`.
 +
 +### Performing the sync from [`rust-lang/rust`] to Clippy
 +
 +Here is a TL;DR version of the sync process (all of the following commands have
 +to be run inside the `rust` directory):
 +
 +1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
 +2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
 +3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
 +    ```bash
 +    # Make sure to change `your-github-name` to your github name in the following command
 +    git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
 +    ```
 +
 +    _Note:_ This will directly push to the remote repository. You can also push
 +    to your local copy by replacing the remote address with `/path/to/rust-clippy`
 +    directory.
 +
 +    _Note:_ Most of the time you have to create a merge commit in the
 +    `rust-clippy` repo (this has to be done in the Clippy repo, not in the
 +    rust-copy of Clippy):
 +    ```bash
 +    git fetch origin && git fetch upstream
 +    git checkout sync-from-rust
 +    git merge upstream/master
 +    ```
 +4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
 +   accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
 +   ~~annoy~~ ask them in the [Zulip] stream.)
 +
 +### Performing the sync from Clippy to [`rust-lang/rust`]
 +
 +All of the following commands have to be run inside the `rust` directory.
 +
 +1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous
 +section if necessary.
 +
 +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
 +    ```bash
 +    git checkout -b sync-from-clippy
 +    git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
 +    ```
 +3. Open a PR to [`rust-lang/rust`]
 +
 +### Defining remotes
 +
 +You may want to define remotes, so you don't have to type out the remote
 +addresses on every sync. You can do this with the following commands (these
 +commands still have to be run inside the `rust` directory):
 +
 +```bash
 +# Set clippy-upstream remote for pulls
 +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy
 +# Make sure to not push to the upstream repo
 +$ git remote set-url --push clippy-upstream DISABLED
 +# Set clippy-origin remote to your fork for pushes
 +$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy
 +# Set a local remote
 +$ git remote add clippy-local /path/to/rust-clippy
 +```
 +
 +You can then sync with the remote names from above, e.g.:
 +
 +```bash
 +$ git subtree push -P src/tools/clippy clippy-local sync-from-rust
 +```
 +
 +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
 +[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
 +[`rust-lang/rust`]: https://github.com/rust-lang/rust
 +
 +## Issue and PR triage
 +
 +Clippy is following the [Rust triage procedure][triage] for issues and pull
 +requests.
 +
 +However, we are a smaller project with all contributors being volunteers
 +currently. Between writing new lints, fixing issues, reviewing pull requests and
 +responding to issues there may not always be enough time to stay on top of it
 +all.
 +
 +Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
 +an ICE in a popular crate that many other crates depend on. We don't
 +want Clippy to crash on your code and we want it to be as reliable as the
 +suggestions from Rust compiler errors.
 +
 +We have prioritization labels and a sync-blocker label, which are described below.
 +- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
 +- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
 +- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
 +- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync. 
 +Or rather: before the sync this should be addressed,
 +e.g. by removing a lint again, so it doesn't hit beta/stable.
 +
 +## Bors and Homu
 +
 +We use a bot powered by [Homu][homu] to help automate testing and landing of pull
 +requests in Clippy. The bot's username is @bors.
 +
 +You can find the Clippy bors queue [here][homu_queue].
 +
 +If you have @bors permissions, you can find an overview of the available
 +commands [here][homu_instructions].
 +
 +[triage]: https://forge.rust-lang.org/release/triage-procedure.html
 +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
 +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
 +[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
 +[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
 +[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
 +[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
 +[homu]: https://github.com/rust-lang/homu
 +[homu_instructions]: https://bors.rust-lang.org/
 +[homu_queue]: https://bors.rust-lang.org/queue/clippy
 +
 +## Contributions
 +
 +Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
 +be reviewed by a core contributor (someone with permission to land patches) and either landed in the
 +main tree or given feedback for changes that would be required.
 +
 +All code in this repository is under the [Apache-2.0] or the [MIT] license.
 +
 +<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->
 +
 +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
 +[MIT]: https://opensource.org/licenses/MIT
index cade44a0a9ab4a8bc0cd4a3ad2489f86a4dded74,0000000000000000000000000000000000000000..f010e6096049170c4a89256f9308da67bdfa8760
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,59 @@@
- version = "0.1.53"
 +[package]
 +name = "clippy"
++version = "0.1.54"
 +authors = ["The Rust Clippy Developers"]
 +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"
 +clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
 +serde = { version = "1.0", features = ["derive"] }
 +derive-new = "0.5"
 +regex = "1.4"
 +quote = "1"
 +syn = { version = "1", features = ["full"] }
 +
 +# 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"
 +
 +[build-dependencies]
 +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 +
 +[features]
 +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 05cdd9d064a8e44b3d0d4d28361afe4b74ffc126,0000000000000000000000000000000000000000..7ceb1da6a6ebb6712ad48e0d6dd981ea3fdf8709
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,40 @@@
- version = "0.1.53"
 +[package]
 +name = "clippy_lints"
 +# begin automatic update
++version = "0.1.54"
 +# end automatic update
 +authors = ["The Rust Clippy Developers"]
 +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"
 +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 = []
 +# 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 42e153909ce75b29f8264d6c4982ec732e2a435b,0000000000000000000000000000000000000000..2a61d58e6537db119a1053ad4845cdf835fc0c39
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,129 @@@
- use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::implements_trait;
++use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks comparison chains written with `if` that can be
 +    /// rewritten with `match` and `cmp`.
 +    ///
 +    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
 +    /// repetitive
 +    ///
 +    /// **Known problems:** The match statement may be slower due to the compiler
 +    /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///     if x > y {
 +    ///         a()
 +    ///     } else if x < y {
 +    ///         b()
 +    ///     } else {
 +    ///         c()
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust,ignore
 +    /// use std::cmp::Ordering;
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///      match x.cmp(&y) {
 +    ///          Ordering::Greater => a(),
 +    ///          Ordering::Less => b(),
 +    ///          Ordering::Equal => c()
 +    ///      }
 +    /// }
 +    /// ```
 +    pub COMPARISON_CHAIN,
 +    style,
 +    "`if`s that can be rewritten with `match` and `cmp`"
 +}
 +
 +declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // We only care about the top-most `if` in the chain
 +        if is_else_clause(cx.tcx, expr) {
 +            return;
 +        }
 +
++        if in_constant(cx, expr.hir_id) {
++            return;
++        }
++
 +        // Check that there exists at least one explicit else condition
 +        let (conds, _) = if_sequence(expr);
 +        if conds.len() < 2 {
 +            return;
 +        }
 +
 +        for cond in conds.windows(2) {
 +            if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) =
 +                (&cond[0].kind, &cond[1].kind)
 +            {
 +                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
 +                    return;
 +                }
 +
 +                // Check that both sets of operands are equal
 +                let mut spanless_eq = SpanlessEq::new(cx);
 +                let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
 +                let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);
 +
 +                if !same_fixed_operands && !same_transposed_operands {
 +                    return;
 +                }
 +
 +                // Check that if the operation is the same, either it's not `==` or the operands are transposed
 +                if kind1.node == kind2.node {
 +                    if kind1.node == BinOpKind::Eq {
 +                        return;
 +                    }
 +                    if !same_transposed_operands {
 +                        return;
 +                    }
 +                }
 +
 +                // Check that the type being compared implements `core::cmp::Ord`
 +                let ty = cx.typeck_results().expr_ty(lhs1);
 +                let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
 +
 +                if !is_ord {
 +                    return;
 +                }
 +            } else {
 +                // We only care about comparison chains
 +                return;
 +            }
 +        }
 +        span_lint_and_help(
 +            cx,
 +            COMPARISON_CHAIN,
 +            expr.span,
 +            "`if` chain can be rewritten with `match`",
 +            None,
 +            "consider rewriting the `if` chain to use `cmp` and `match`",
 +        )
 +    }
 +}
 +
 +fn kind_is_cmp(kind: BinOpKind) -> bool {
 +    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
 +}
index 710da8fe9e037ea6bc21eceeafc3afe205c07410,0000000000000000000000000000000000000000..7a53d390bb45f00159ce682c485377c7f80b6504
mode 100644,000000..100644
--- /dev/null
@@@ -1,279 -1,0 +1,280 @@@
- use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 +use clippy_utils::source::snippet_with_macro_callsite;
++use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{Ident, Symbol};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for literal calls to `Default::default()`.
 +    ///
 +    /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
 +    /// being gotten than the generic `Default`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// // Bad
 +    /// let s: String = Default::default();
 +    ///
 +    /// // Good
 +    /// let s = String::default();
 +    /// ```
 +    pub DEFAULT_TRAIT_ACCESS,
 +    pedantic,
 +    "checks for literal calls to `Default::default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for immediate reassignment of fields initialized
 +    /// with Default::default().
 +    ///
 +    /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
 +    ///
 +    /// **Known problems:** Assignments to patterns that are of tuple type are not linted.
 +    ///
 +    /// **Example:**
 +    /// Bad:
 +    /// ```
 +    /// # #[derive(Default)]
 +    /// # struct A { i: i32 }
 +    /// let mut a: A = Default::default();
 +    /// a.i = 42;
 +    /// ```
 +    /// Use instead:
 +    /// ```
 +    /// # #[derive(Default)]
 +    /// # struct A { i: i32 }
 +    /// let a = A {
 +    ///     i: 42,
 +    ///     .. Default::default()
 +    /// };
 +    /// ```
 +    pub FIELD_REASSIGN_WITH_DEFAULT,
 +    style,
 +    "binding initialized with Default should have its fields set in the initializer"
 +}
 +
 +#[derive(Default)]
 +pub struct Default {
 +    // Spans linted by `field_reassign_with_default`.
 +    reassigned_linted: FxHashSet<Span>,
 +}
 +
 +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
 +
 +impl LateLintPass<'_> for Default {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
++            if !in_macro(expr.span);
 +            // Avoid cases already linted by `field_reassign_with_default`
 +            if !self.reassigned_linted.contains(&expr.span);
 +            if let ExprKind::Call(path, ..) = expr.kind;
 +            if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
 +            if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
 +            // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
 +            if let QPath::Resolved(None, _path) = qpath;
 +            let expr_ty = cx.typeck_results().expr_ty(expr);
 +            if let ty::Adt(def, ..) = expr_ty.kind();
 +            then {
 +                // TODO: Work out a way to put "whatever the imported way of referencing
 +                // this type in this file" rather than a fully-qualified type.
 +                let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
 +                span_lint_and_sugg(
 +                    cx,
 +                    DEFAULT_TRAIT_ACCESS,
 +                    expr.span,
 +                    &format!("calling `{}` is more clear than this expression", replacement),
 +                    "try",
 +                    replacement,
 +                    Applicability::Unspecified, // First resolve the TODO above
 +                );
 +            }
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +        // start from the `let mut _ = _::default();` and look at all the following
 +        // statements, see if they re-assign the fields of the binding
 +        let stmts_head = match block.stmts {
 +            // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
 +            [head @ .., _] if !head.is_empty() => head,
 +            _ => return,
 +        };
 +        for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
 +            // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
 +            // `default` method of the `Default` trait, and store statement index in current block being
 +            // checked and the name of the bound variable
 +            let (local, variant, binding_name, binding_type, span) = if_chain! {
 +                // only take `let ...` statements
 +                if let StmtKind::Local(local) = stmt.kind;
 +                if let Some(expr) = local.init;
 +                if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
 +                if !in_external_macro(cx.tcx.sess, expr.span);
 +                // only take bindings to identifiers
 +                if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
 +                // only when assigning `... = Default::default()`
 +                if is_expr_default(expr, cx);
 +                let binding_type = cx.typeck_results().node_type(binding_id);
 +                if let Some(adt) = binding_type.ty_adt_def();
 +                if adt.is_struct();
 +                let variant = adt.non_enum_variant();
 +                if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
 +                let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
 +                if variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
 +                then {
 +                    (local, variant, ident.name, binding_type, expr.span)
 +                } else {
 +                    continue;
 +                }
 +            };
 +
 +            // find all "later statement"'s where the fields of the binding set as
 +            // Default::default() get reassigned, unless the reassignment refers to the original binding
 +            let mut first_assign = None;
 +            let mut assigned_fields = Vec::new();
 +            let mut cancel_lint = false;
 +            for consecutive_statement in &block.stmts[stmt_idx + 1..] {
 +                // find out if and which field was set by this `consecutive_statement`
 +                if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
 +                    // interrupt and cancel lint if assign_rhs references the original binding
 +                    if contains_name(binding_name, assign_rhs) {
 +                        cancel_lint = true;
 +                        break;
 +                    }
 +
 +                    // if the field was previously assigned, replace the assignment, otherwise insert the assignment
 +                    if let Some(prev) = assigned_fields
 +                        .iter_mut()
 +                        .find(|(field_name, _)| field_name == &field_ident.name)
 +                    {
 +                        *prev = (field_ident.name, assign_rhs);
 +                    } else {
 +                        assigned_fields.push((field_ident.name, assign_rhs));
 +                    }
 +
 +                    // also set first instance of error for help message
 +                    if first_assign.is_none() {
 +                        first_assign = Some(consecutive_statement);
 +                    }
 +                }
 +                // interrupt if no field was assigned, since we only want to look at consecutive statements
 +                else {
 +                    break;
 +                }
 +            }
 +
 +            // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
 +            // construction using `Ty { fields, ..Default::default() }`
 +            if !assigned_fields.is_empty() && !cancel_lint {
 +                // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
 +                let ext_with_default = !variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
 +
 +                let field_list = assigned_fields
 +                    .into_iter()
 +                    .map(|(field, rhs)| {
 +                        // extract and store the assigned value for help message
 +                        let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
 +                        format!("{}: {}", field, value_snippet)
 +                    })
 +                    .collect::<Vec<String>>()
 +                    .join(", ");
 +
 +                // give correct suggestion if generics are involved (see #6944)
 +                let binding_type = if_chain! {
 +                    if let ty::Adt(adt_def, substs) = binding_type.kind();
 +                    if !substs.is_empty();
 +                    then {
 +                        let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
 +                        let generic_args = substs.iter().collect::<Vec<_>>();
 +                        let tys_str = generic_args
 +                            .iter()
 +                            .map(ToString::to_string)
 +                            .collect::<Vec<_>>()
 +                            .join(", ");
 +                        format!("{}::<{}>", adt_def_ty_name, &tys_str)
 +                    } else {
 +                        binding_type.to_string()
 +                    }
 +                };
 +
 +                let sugg = if ext_with_default {
 +                    if field_list.is_empty() {
 +                        format!("{}::default()", binding_type)
 +                    } else {
 +                        format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
 +                    }
 +                } else {
 +                    format!("{} {{ {} }}", binding_type, field_list)
 +                };
 +
 +                // span lint once per statement that binds default
 +                span_lint_and_note(
 +                    cx,
 +                    FIELD_REASSIGN_WITH_DEFAULT,
 +                    first_assign.unwrap().span,
 +                    "field assignment outside of initializer for an instance created with Default::default()",
 +                    Some(local.span),
 +                    &format!(
 +                        "consider initializing the variable with `{}` and removing relevant reassignments",
 +                        sugg
 +                    ),
 +                );
 +                self.reassigned_linted.insert(span);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is the `default` method belonging to the `Default` trait.
 +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
 +    if_chain! {
 +        if let ExprKind::Call(fn_expr, _) = &expr.kind;
 +        if let ExprKind::Path(qpath) = &fn_expr.kind;
 +        if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
 +        then {
 +            // right hand side of assignment is `Default::default`
 +            match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Returns the reassigned field and the assigning expression (right-hand side of assign).
 +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
 +    if_chain! {
 +        // only take assignments
 +        if let StmtKind::Semi(later_expr) = this.kind;
 +        if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
 +        // only take assignments to fields where the left-hand side field is a field of
 +        // the same binding as the previous statement
 +        if let ExprKind::Field(binding, field_ident) = assign_lhs.kind;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
 +        if let Some(second_binding_name) = path.segments.last();
 +        if second_binding_name.ident.name == binding_name;
 +        then {
 +            Some((field_ident, assign_rhs))
 +        } else {
 +            None
 +        }
 +    }
 +}
index 762f64fe37ad6862f19edf05576a747ec76e158e,0000000000000000000000000000000000000000..41acf55dd7d572ec8a75994ec4e924ad4e0c70be
mode 100644,000000..100644
--- /dev/null
@@@ -1,351 -1,0 +1,351 @@@
-         match expr.kind {
-             ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => {
-                 if let Some(var) = path_to_local(lhs) {
-                     let mut visitor = ReadVisitor {
-                         cx,
-                         var,
-                         write_expr: expr,
-                         last_expr: expr,
-                     };
-                     check_for_unsequenced_reads(&mut visitor);
-                 }
-             },
-             _ => {},
-         }
 +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. In addition, the
 +    /// sub-expression evaluation order for Rust is not well documented.
 +    ///
 +    /// **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,
 +    complexity,
 +    "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.
-                     "unsequenced read of a 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 6b379b0d59b2bb0c807d23687de2c578524cb12a,0000000000000000000000000000000000000000..30174fa2100dbbd2f259bbb7e8de3e9ac2ebed87
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,234 @@@
- use clippy_utils::diagnostics::span_lint_and_then;
- use clippy_utils::match_panic_def_id;
- use clippy_utils::source::snippet_opt;
- use if_chain::if_chain;
++use clippy_utils::{
++    diagnostics::span_lint_and_sugg,
++    get_async_fn_body, is_async_fn,
++    source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
++    visitors::visit_break_exprs,
++};
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
- use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind};
- use rustc_lint::{LateContext, LateLintPass};
++use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
++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::source_map::Span;
++use rustc_span::{Span, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for missing return statements at the end of a block.
 +    ///
 +    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
 +    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
 +    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
 +    /// code with multiple return paths having a `return` keyword makes it easier to find the
 +    /// corresponding statements.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    /// add return
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    pub IMPLICIT_RETURN,
 +    restriction,
 +    "use a return statement like `return expr` instead of an expression"
 +}
 +
 +declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 +
- static LINT_BREAK: &str = "change `break` to `return` as shown";
- static LINT_RETURN: &str = "add `return` as shown";
- fn lint(cx: &LateContext<'_>, outer_span: Span, inner_span: Span, msg: &str) {
-     let outer_span = outer_span.source_callsite();
-     let inner_span = inner_span.source_callsite();
-     span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| {
-         if let Some(snippet) = snippet_opt(cx, inner_span) {
-             diag.span_suggestion(
-                 outer_span,
-                 msg,
-                 format!("return {}", snippet),
-                 Applicability::MachineApplicable,
-             );
-         }
-     });
++fn lint_return(cx: &LateContext<'_>, span: Span) {
++    let mut app = Applicability::MachineApplicable;
++    let snip = snippet_with_applicability(cx, span, "..", &mut app);
++    span_lint_and_sugg(
++        cx,
++        IMPLICIT_RETURN,
++        span,
++        "missing `return` statement",
++        "add `return` as shown",
++        format!("return {}", snip),
++        app,
++    );
++}
++
++fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
++    let mut app = Applicability::MachineApplicable;
++    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
++    span_lint_and_sugg(
++        cx,
++        IMPLICIT_RETURN,
++        break_span,
++        "missing `return` statement",
++        "change `break` to `return` as shown",
++        format!("return {}", snip),
++        app,
++    )
++}
++
++#[derive(Clone, Copy, PartialEq, Eq)]
++enum LintLocation {
++    /// The lint was applied to a parent expression.
++    Parent,
++    /// The lint was applied to this expression, a child, or not applied.
++    Inner,
++}
++impl LintLocation {
++    fn still_parent(self, b: bool) -> Self {
++        if b { self } else { Self::Inner }
++    }
++
++    fn is_parent(self) -> bool {
++        self == Self::Parent
++    }
++}
++
++// Gets the call site if the span is in a child context. Otherwise returns `None`.
++fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
++    (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
 +}
 +
- fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) {
++fn lint_implicit_returns(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    // The context of the function body.
++    ctxt: SyntaxContext,
++    // Whether the expression is from a macro expansion.
++    call_site_span: Option<Span>,
++) -> LintLocation {
 +    match expr.kind {
-         // loops could be using `break` instead of `return`
-         ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
-             if let Some(expr) = &block.expr {
-                 expr_match(cx, expr);
-             }
-             // only needed in the case of `break` with `;` at the end
-             else if let Some(stmt) = block.stmts.last() {
-                 if_chain! {
-                     if let StmtKind::Semi(expr, ..) = &stmt.kind;
-                     // make sure it's a break, otherwise we want to skip
-                     if let ExprKind::Break(.., Some(break_expr)) = &expr.kind;
-                     then {
-                             lint(cx, expr.span, break_expr.span, LINT_BREAK);
-                     }
-                 }
-             }
-         },
-         // use `return` instead of `break`
-         ExprKind::Break(.., break_expr) => {
-             if let Some(break_expr) = break_expr {
-                 lint(cx, expr.span, break_expr.span, LINT_BREAK);
++        ExprKind::Block(
++            Block {
++                expr: Some(block_expr), ..
++            },
++            _,
++        ) => lint_implicit_returns(
++            cx,
++            block_expr,
++            ctxt,
++            call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
++        )
++        .still_parent(call_site_span.is_some()),
++
++        ExprKind::If(_, then_expr, Some(else_expr)) => {
++            // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
++            // bother checking.
++            let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
++            if res.is_parent() {
++                // The return was added as a parent of this if expression.
++                return res;
 +            }
++            lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
 +        },
-         ExprKind::If(.., if_expr, else_expr) => {
-             expr_match(cx, if_expr);
 +
-             if let Some(else_expr) = else_expr {
-                 expr_match(cx, else_expr);
++        ExprKind::Match(_, arms, _) => {
++            for arm in arms {
++                let res = lint_implicit_returns(
++                    cx,
++                    arm.body,
++                    ctxt,
++                    call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
++                )
++                .still_parent(call_site_span.is_some());
++                if res.is_parent() {
++                    // The return was added as a parent of this match expression.
++                    return res;
++                }
 +            }
++            LintLocation::Inner
 +        },
-         ExprKind::Match(.., arms, source) => {
-             let check_all_arms = match source {
-                 MatchSource::IfLetDesugar {
-                     contains_else_clause: has_else,
-                 } => has_else,
-                 _ => true,
-             };
-             if check_all_arms {
-                 for arm in arms {
-                     expr_match(cx, arm.body);
++
++        ExprKind::Loop(block, ..) => {
++            let mut add_return = false;
++            visit_break_exprs(block, |break_expr, dest, sub_expr| {
++                if dest.target_id.ok() == Some(expr.hir_id) {
++                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
++                        lint_break(cx, break_expr.span, sub_expr.unwrap().span);
++                    } else {
++                        // the break expression is from a macro call, add a return to the loop
++                        add_return = true;
++                    }
++                }
++            });
++            if add_return {
++                #[allow(clippy::option_if_let_else)]
++                if let Some(span) = call_site_span {
++                    lint_return(cx, span);
++                    LintLocation::Parent
++                } else {
++                    lint_return(cx, expr.span);
++                    LintLocation::Inner
 +                }
 +            } else {
-                 expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body);
++                LintLocation::Inner
 +            }
 +        },
-         // skip if it already has a return statement
-         ExprKind::Ret(..) => (),
-         // make sure it's not a call that panics
-         ExprKind::Call(expr, ..) => {
-             if_chain! {
-                 if let ExprKind::Path(qpath) = &expr.kind;
-                 if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id();
-                 if match_panic_def_id(cx, path_def_id);
-                 then { }
-                 else {
-                     lint(cx, expr.span, expr.span, LINT_RETURN)
-                 }
++
++        // If expressions without an else clause, and blocks without a final expression can only be the final expression
++        // if they are divergent, or return the unit type.
++        ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
++            LintLocation::Inner
++        },
++
++        // Any divergent expression doesn't need a return statement.
++        ExprKind::MethodCall(..)
++        | ExprKind::Call(..)
++        | ExprKind::Binary(..)
++        | ExprKind::Unary(..)
++        | ExprKind::Index(..)
++            if cx.typeck_results().expr_ty(expr).is_never() =>
++        {
++            LintLocation::Inner
++        },
++
++        _ =>
++        {
++            #[allow(clippy::option_if_let_else)]
++            if let Some(span) = call_site_span {
++                lint_return(cx, span);
++                LintLocation::Parent
++            } else {
++                lint_return(cx, expr.span);
++                LintLocation::Inner
 +            }
 +        },
-         // everything else is missing `return`
-         _ => lint(cx, expr.span, expr.span, LINT_RETURN),
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
-         _: FnKind<'tcx>,
-         _: &'tcx FnDecl<'_>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
-         if span.from_expansion() {
++        if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
++            || span.ctxt() != body.value.span.ctxt()
++            || in_external_macro(cx.sess(), span)
++        {
 +            return;
 +        }
-         let body = cx.tcx.hir().body(body.id());
-         if cx.typeck_results().expr_ty(&body.value).is_unit() {
++
++        let res_ty = cx.typeck_results().expr_ty(&body.value);
++        if res_ty.is_unit() || res_ty.is_never() {
 +            return;
 +        }
-         expr_match(cx, &body.value);
++
++        let expr = if is_async_fn(kind) {
++            match get_async_fn_body(cx.tcx, body) {
++                Some(e) => e,
++                None => return,
++            }
++        } else {
++            &body.value
++        };
++        lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
 +    }
 +}
index 8c74284fa46ea3695c4804752b431abdd42bc42f,0000000000000000000000000000000000000000..725aa54157ecf4a352c5cfd119f83993b0330b77
mode 100644,000000..100644
--- /dev/null
@@@ -1,2162 -1,0 +1,2154 @@@
- pub fn read_conf(args: &[rustc_ast::NestedMetaItem], sess: &Session) -> Conf {
 +// error-pattern:cargo-clippy
 +
 +#![feature(box_patterns)]
 +#![feature(box_syntax)]
 +#![feature(drain_filter)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(once_cell)]
 +#![cfg_attr(bootstrap, feature(or_patterns))]
 +#![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;
 +
 +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`, `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.
 +///     ///
 +///     /// **Known problems:** None. (Or describe where it could go wrong.)
 +///     ///
 +///     /// **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, 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
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! sym {
 +    ( $($x:tt)* ) => { clippy_utils::sym!($($x)*) }
 +}
 +
 +#[macro_export]
 +macro_rules! unwrap_cargo_metadata {
 +    ( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*) }
 +}
 +
 +macro_rules! extract_msrv_attr {
 +    ( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); }
 +}
 +
 +mod consts;
 +#[macro_use]
 +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 atomic_ordering;
 +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 derive;
 +mod disallowed_method;
 +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 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_identity;
 +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_inline;
 +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_bool;
 +mod needless_borrow;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +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 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 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 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_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 write::Write::default());
 +    store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
 +    store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
 +}
 +
 +#[doc(hidden)]
-     match utils::conf::file_from_args(args) {
-         Ok(file_name) => {
-             // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
-             // do not require the file to exist
-             let file_name = match file_name {
-                 Some(file_name) => file_name,
-                 None => 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 file_name = if file_name.is_relative() {
-                 sess.local_crate_source_file
-                     .as_deref()
-                     .and_then(Path::parent)
-                     .unwrap_or_else(|| Path::new(""))
-                     .join(file_name)
-             } else {
-                 file_name
-             };
-             let (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
-         },
-         Err((err, span)) => {
-             sess.struct_span_err(span, err)
-                 .span_note(span, "Clippy will use default configuration")
++pub fn read_conf(sess: &Session) -> Conf {
 +    use std::path::Path;
-             Conf::default()
++    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 file_name = if file_name.is_relative() {
++        sess.local_crate_source_file
++            .as_deref()
++            .and_then(Path::parent)
++            .unwrap_or_else(|| Path::new(""))
++            .join(file_name)
++    } else {
++        file_name
++    };
++
++    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",
 +    );
 +    // 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,
 +        atomic_ordering::INVALID_ATOMIC_ORDERING,
 +        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,
 +        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,
 +        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,
 +        enum_variants::PUB_ENUM_VARIANT_NAMES,
 +        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,
 +        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_identity::MAP_IDENTITY,
 +        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::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::MAP_COLLECT_RESULT_UNIT,
 +        methods::MAP_FLATTEN,
 +        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::UNINIT_ASSUMED_INIT,
 +        methods::UNNECESSARY_FILTER_MAP,
 +        methods::UNNECESSARY_FOLD,
 +        methods::UNNECESSARY_LAZY_EVALUATIONS,
 +        methods::UNWRAP_USED,
 +        methods::USELESS_ASREF,
 +        methods::WRONG_PUB_SELF_CONVENTION,
 +        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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +        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_bool::BOOL_COMPARISON,
 +        needless_bool::NEEDLESS_BOOL,
 +        needless_borrow::NEEDLESS_BORROW,
 +        needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +        needless_continue::NEEDLESS_CONTINUE,
 +        needless_for_each::NEEDLESS_FOR_EACH,
 +        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,
 +        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,
 +        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,
 +        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::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_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`
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal-lints")]
 +    {
 +        store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
 +        store.register_early_pass(|| box utils::internal_lints::ProduceIce);
 +        store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
 +        store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
 +        store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
 +        store.register_late_pass(|| box utils::internal_lints::IfChainStyle);
 +        store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
 +        store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
 +        store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
 +        store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
 +        store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
 +    }
++    #[cfg(feature = "metadata-collector-lint")]
++    {
++        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
++            store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default());
++        }
++    }
++
 +    store.register_late_pass(|| box utils::author::Author);
 +    store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
 +    store.register_late_pass(|| box 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(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold));
 +    store.register_late_pass(|| box booleans::NonminimalBool);
 +    store.register_late_pass(|| box eq_op::EqOp);
 +    store.register_late_pass(|| box enum_clike::UnportableVariant);
 +    store.register_late_pass(|| box float_literal::FloatLiteral);
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
 +    store.register_late_pass(|| box ptr::Ptr);
 +    store.register_late_pass(|| box ptr_eq::PtrEq);
 +    store.register_late_pass(|| box needless_bool::NeedlessBool);
 +    store.register_late_pass(|| box needless_bool::BoolComparison);
 +    store.register_late_pass(|| box needless_for_each::NeedlessForEach);
 +    store.register_late_pass(|| box approx_const::ApproxConstant);
 +    store.register_late_pass(|| box misc::MiscLints);
 +    store.register_late_pass(|| box eta_reduction::EtaReduction);
 +    store.register_late_pass(|| box identity_op::IdentityOp);
 +    store.register_late_pass(|| box erasing_op::ErasingOp);
 +    store.register_late_pass(|| box mut_mut::MutMut);
 +    store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed);
 +    store.register_late_pass(|| box len_zero::LenZero);
 +    store.register_late_pass(|| box attrs::Attributes);
 +    store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions);
 +    store.register_late_pass(|| box collapsible_match::CollapsibleMatch);
 +    store.register_late_pass(|| box unicode::Unicode);
 +    store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd);
 +    store.register_late_pass(|| box strings::StringAdd);
 +    store.register_late_pass(|| box implicit_return::ImplicitReturn);
 +    store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
 +    store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
 +    store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
 +    store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
 +    store.register_early_pass(|| box 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
 +        })
 +    });
 +
 +    store.register_late_pass(move || box methods::Methods::new(msrv));
 +    store.register_late_pass(move || box matches::Matches::new(msrv));
 +    store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
 +    store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
 +    store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv));
 +    store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv));
 +    store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
 +    store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
 +    store.register_late_pass(move || box ranges::Ranges::new(msrv));
 +    store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
 +    store.register_late_pass(move || box use_self::UseSelf::new(msrv));
 +    store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
 +    store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark);
 +    store.register_late_pass(move || box casts::Casts::new(msrv));
 +    store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv));
 +
 +    store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
 +    store.register_late_pass(|| box map_clone::MapClone);
 +    store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
 +    store.register_late_pass(|| box shadow::Shadow);
 +    store.register_late_pass(|| box unit_types::UnitTypes);
 +    store.register_late_pass(|| box loops::Loops);
 +    store.register_late_pass(|| box main_recursion::MainRecursion::default());
 +    store.register_late_pass(|| box lifetimes::Lifetimes);
 +    store.register_late_pass(|| box entry::HashMapPass);
 +    store.register_late_pass(|| box minmax::MinMaxPass);
 +    store.register_late_pass(|| box open_options::OpenOptions);
 +    store.register_late_pass(|| box zero_div_zero::ZeroDiv);
 +    store.register_late_pass(|| box mutex_atomic::Mutex);
 +    store.register_late_pass(|| box needless_update::NeedlessUpdate);
 +    store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default());
 +    store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef);
 +    store.register_late_pass(|| box no_effect::NoEffect);
 +    store.register_late_pass(|| box temporary_assignment::TemporaryAssignment);
 +    store.register_late_pass(|| box transmute::Transmute);
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold));
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack});
 +    store.register_late_pass(move || box vec::UselessVec{too_large_for_stack});
 +    store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented);
 +    store.register_late_pass(|| box strings::StringLitAsBytes);
 +    store.register_late_pass(|| box derive::Derive);
 +    store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
 +    store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
 +    store.register_late_pass(|| box empty_enum::EmptyEnum);
 +    store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons);
 +    store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons);
 +    store.register_late_pass(|| box regex::Regex::default());
 +    store.register_late_pass(|| box copies::CopyAndPaste);
 +    store.register_late_pass(|| box copy_iterator::CopyIterator);
 +    store.register_late_pass(|| box format::UselessFormat);
 +    store.register_late_pass(|| box swap::Swap);
 +    store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional);
 +    store.register_late_pass(|| box new_without_default::NewWithoutDefault::default());
 +    let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || box 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 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 doc::DocMarkdown::new(doc_valid_idents.clone()));
 +    store.register_late_pass(|| box neg_multiply::NegMultiply);
 +    store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
 +    store.register_late_pass(|| box mem_forget::MemForget);
 +    store.register_late_pass(|| box arithmetic::Arithmetic::default());
 +    store.register_late_pass(|| box assign_ops::AssignOps);
 +    store.register_late_pass(|| box let_if_seq::LetIfSeq);
 +    store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
 +    store.register_late_pass(|| box missing_doc::MissingDoc::new());
 +    store.register_late_pass(|| box missing_inline::MissingInline);
 +    store.register_late_pass(move || box exhaustive_items::ExhaustiveItems);
 +    store.register_late_pass(|| box if_let_some_result::OkIfLet);
 +    store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
 +    store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold));
 +    store.register_late_pass(|| box explicit_write::ExplicitWrite);
 +    store.register_late_pass(|| box 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,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move || box pass_by_ref_or_value);
 +    store.register_late_pass(|| box ref_option_ref::RefOptionRef);
 +    store.register_late_pass(|| box try_err::TryErr);
 +    store.register_late_pass(|| box bytecount::ByteCount);
 +    store.register_late_pass(|| box infinite_iter::InfiniteIter);
 +    store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
 +    store.register_late_pass(|| box useless_conversion::UselessConversion::default());
 +    store.register_late_pass(|| box implicit_hasher::ImplicitHasher);
 +    store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
 +    store.register_late_pass(|| box double_comparison::DoubleComparisons);
 +    store.register_late_pass(|| box question_mark::QuestionMark);
 +    store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings);
 +    store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
 +    store.register_late_pass(|| box map_unit_fn::MapUnit);
 +    store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
 +    store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
 +    store.register_late_pass(|| box unwrap::Unwrap);
 +    store.register_late_pass(|| box duration_subsec::DurationSubsec);
 +    store.register_late_pass(|| box indexing_slicing::IndexingSlicing);
 +    store.register_late_pass(|| box non_copy_const::NonCopyConst);
 +    store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
 +    store.register_late_pass(|| box redundant_clone::RedundantClone);
 +    store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
 +    store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy);
 +    store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
 +    store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
 +    store.register_late_pass(|| box transmuting_null::TransmutingNull);
 +    store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
 +    store.register_late_pass(|| box integer_division::IntegerDivision);
 +    store.register_late_pass(|| box inherent_to_string::InherentToString);
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds));
 +    store.register_late_pass(|| box comparison_chain::ComparisonChain);
 +    store.register_late_pass(|| box mut_key::MutableKeyType);
 +    store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
 +    store.register_early_pass(|| box reference::DerefAddrOf);
 +    store.register_early_pass(|| box reference::RefInDeref);
 +    store.register_early_pass(|| box double_parens::DoubleParens);
 +    store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new());
 +    store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval);
 +    store.register_early_pass(|| box if_not_else::IfNotElse);
 +    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
 +    store.register_early_pass(|| box int_plus_one::IntPlusOne);
 +    store.register_early_pass(|| box formatting::Formatting);
 +    store.register_early_pass(|| box misc_early::MiscEarlyLints);
 +    store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall);
 +    store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall);
 +    store.register_early_pass(|| box unused_unit::UnusedUnit);
 +    store.register_late_pass(|| box returns::Return);
 +    store.register_early_pass(|| box collapsible_if::CollapsibleIf);
 +    store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
 +    store.register_early_pass(|| box precedence::Precedence);
 +    store.register_early_pass(|| box needless_continue::NeedlessContinue);
 +    store.register_early_pass(|| box redundant_else::RedundantElse);
 +    store.register_late_pass(|| box create_dir::CreateDir);
 +    store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || box cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish));
 +    store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
 +    store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability));
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
 +    store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive));
 +    store.register_late_pass(|| box default::Default::default());
 +    store.register_late_pass(|| box unused_self::UnusedSelf);
 +    store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
 +    store.register_late_pass(|| box exit::Exit);
 +    store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
 +    store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
 +    store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
 +    store.register_early_pass(|| box as_conversions::AsConversions);
 +    store.register_late_pass(|| box let_underscore::LetUnderscore);
 +    store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
 +    store.register_early_pass(|| box 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 excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
 +    store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
 +    store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
 +    store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
 +    store.register_late_pass(|| box unnamed_address::UnnamedAddress);
 +    store.register_late_pass(|| box dereference::Dereferencing::default());
 +    store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
 +    store.register_late_pass(|| box future_not_send::FutureNotSend);
 +    store.register_late_pass(|| box if_let_mutex::IfLetMutex);
 +    store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
 +    store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
 +    store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
 +    store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
 +    store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
 +    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
 +    store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
 +        single_char_binding_names_threshold,
 +    });
 +    store.register_late_pass(|| box macro_use::MacroUseImports::default());
 +    store.register_late_pass(|| box map_identity::MapIdentity);
 +    store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
 +    store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
 +    store.register_late_pass(|| box repeat_once::RepeatOnce);
 +    store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
 +    store.register_late_pass(|| box self_assignment::SelfAssignment);
 +    store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
 +    store.register_late_pass(|| box manual_ok_or::ManualOkOr);
 +    store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
 +    store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned);
 +    store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
 +    let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
 +    store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
 +    store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
 +    store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
 +    store.register_late_pass(|| box strings::StrToString);
 +    store.register_late_pass(|| box strings::StringToString);
 +    store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
 +    store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default());
 +    store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
 +    store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
 +    store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
 +    store.register_late_pass(|| box manual_map::ManualMap);
 +    store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
 +    store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
 +
 +    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(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(methods::WRONG_PUB_SELF_CONVENTION),
 +        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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +        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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +        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(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(enum_variants::PUB_ENUM_VARIANT_NAMES),
 +        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(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::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_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(option_if_let_else::OPTION_IF_LET_ELSE),
 +        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(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_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(atomic_ordering::INVALID_ATOMIC_ORDERING),
 +        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(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(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +        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_identity::MAP_IDENTITY),
 +        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::FILTER_MAP_IDENTITY),
 +        LintId::of(methods::FILTER_NEXT),
 +        LintId::of(methods::FLAT_MAP_IDENTITY),
 +        LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +        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::MAP_COLLECT_RESULT_UNIT),
 +        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::UNINIT_ASSUMED_INIT),
 +        LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +        LintId::of(methods::UNNECESSARY_FOLD),
 +        LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +        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_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +        LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +        LintId::of(needless_update::NEEDLESS_UPDATE),
 +        LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +        LintId::of(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(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(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +        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(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +        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(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::RESULT_UNIT_ERR),
 +        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
 +        LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +        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::EMPTY_LOOP),
 +        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::FROM_ITER_INSTEAD_OF_COLLECT),
 +        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::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(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(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +        LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +        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(assign_ops::MISREFACTORED_ASSIGN_OP),
 +        LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +        LintId::of(booleans::NONMINIMAL_BOOL),
 +        LintId::of(casts::CHAR_LIT_AS_U8),
 +        LintId::of(casts::UNNECESSARY_CAST),
 +        LintId::of(copies::BRANCHES_SHARING_CODE),
 +        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(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +        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::MUT_RANGE_BOUND),
 +        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_identity::MAP_IDENTITY),
 +        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::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::SUSPICIOUS_MAP),
 +        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_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(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(atomic_ordering::INVALID_ATOMIC_ORDERING),
 +        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(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +        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::FOR_LOOPS_OVER_FALLIBLES),
 +        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::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(mut_key::MUTABLE_KEY_TYPE),
 +        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(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +        LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +        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::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::ITER_NTH),
 +        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(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(disallowed_method::DISALLOWED_METHOD),
 +        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(needless_borrow::NEEDLESS_BORROW),
 +        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(transmute::USELESS_TRANSMUTE),
 +        LintId::of(use_self::USE_SELF),
 +    ]);
 +}
 +
 +#[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_panic");
 +    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 +}
 +
 +// 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 4d73aef76e87e8604294791aac254627716f9f62,0000000000000000000000000000000000000000..9662a0b22a3ab563629a4b9e5e79f56395efb399
mode 100644,000000..100644
--- /dev/null
@@@ -1,245 -1,0 +1,259 @@@
- use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind};
 +use super::NEEDLESS_COLLECT;
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{is_type_diagnostic_item, match_type};
 +use clippy_utils::{is_trait_method, path_to_local_id, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
-         let ty = cx.typeck_results().node_type(ty.hir_id);
++use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
++
 +use rustc_span::symbol::{sym, Ident};
 +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);
 +        if let Some(generic_args) = chain_method.args;
 +        if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
-                     init: Some(init_expr), .. }
++        if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
 +        if is_type_diagnostic_item(cx, ty, sym::vec_type)
 +            || is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
 +            || match_type(cx, ty, &paths::BTREEMAP)
 +            || is_type_diagnostic_item(cx, ty, sym::hashmap_type);
 +        if let Some(sugg) = match &*method.ident.name.as_str() {
 +            "len" => Some("count()".to_string()),
 +            "is_empty" => Some("next().is_none()".to_string()),
 +            "contains" => {
 +                let contains_arg = snippet(cx, args[1].span, "??");
 +                let (arg, pred) = contains_arg
 +                    .strip_prefix('&')
 +                    .map_or(("&x", &*contains_arg), |s| ("x", s));
 +                Some(format!("any(|{}| x == {})", arg, pred))
 +            }
 +            _ => None,
 +        };
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                NEEDLESS_COLLECT,
 +                method0_span.with_hi(expr.span.hi()),
 +                NEEDLESS_COLLECT_MSG,
 +                "replace with",
 +                sugg,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
++    fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
++        if let Some(ty) = ty {
++            return Some(ty.hir_id);
++        }
++
++        if let Some(generic_args) = method_args {
++            if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
++                return Some(ty.hir_id);
++            }
++        }
++
++        None
++    }
 +    if let ExprKind::Block(block, _) = expr.kind {
 +        for stmt in block.stmts {
 +            if_chain! {
 +                if let StmtKind::Local(
 +                    Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
-                 if let Some(generic_args) = method_name.args;
-                 if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
-                 if let ty = cx.typeck_results().node_type(ty.hir_id);
++                    init: Some(init_expr), ty, .. }
 +                ) = stmt.kind;
 +                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);
++                if let Some(hir_id) = get_hir_id(*ty, method_name.args);
++                if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
 +                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) ||
 +                    match_type(cx, ty, &paths::LINKED_LIST);
 +                if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
 +                if let [iter_call] = &*iter_calls;
 +                then {
 +                    let mut used_count_visitor = UsedCountVisitor {
 +                        cx,
 +                        id: *pat_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_and_then(
 +                        cx,
 +                        super::NEEDLESS_COLLECT,
 +                        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::MachineApplicable,// 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: Ident,
 +}
 +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        // Check function calls on our collection
 +        if_chain! {
 +            if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
 +            if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0);
 +            if let &[name] = &path.segments;
 +            if name.ident == self.target;
 +            then {
 +                let len = sym!(len);
 +                let is_empty = sym!(is_empty);
 +                let contains = sym!(contains);
 +                match method_name.ident.name {
 +                    sym::into_iter => self.uses.push(
 +                        IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
 +                    ),
 +                    name if name == len => self.uses.push(
 +                        IterFunction { func: IterFunctionKind::Len, span: expr.span }
 +                    ),
 +                    name if name == is_empty => self.uses.push(
 +                        IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
 +                    ),
 +                    name if name == contains => self.uses.push(
 +                        IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
 +                    ),
 +                    _ => self.seen_other = true,
 +                }
 +                return
 +            }
 +        }
 +        // Check if the collection is used for anything else
 +        if_chain! {
 +            if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr;
 +            if let &[name] = &path.segments;
 +            if name.ident == self.target;
 +            then {
 +                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>, identifier: Ident) -> Option<Vec<IterFunction>> {
 +    let mut visitor = IterFunctionVisitor {
 +        uses: Vec::new(),
 +        target: identifier,
 +        seen_other: false,
 +    };
 +    visitor.visit_block(block);
 +    if visitor.seen_other { None } else { Some(visitor.uses) }
 +}
index de267cc77d2b406f0c369cd8f2e2101a3bb3502a,0000000000000000000000000000000000000000..55404b87ec9ce60400bf8137ac93a8dbe8a09e23
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,141 @@@
-     let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) {
-         used_in_condition.is_disjoint(&used_mutably)
-     } else {
-         return;
-     };
 +use super::WHILE_IMMUTABLE_CONDITION;
 +use crate::consts::constant;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::usage::mutated_variables;
 +use if_chain::if_chain;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefIdMap;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::HirIdSet;
 +use rustc_hir::{Expr, ExprKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
 +    if constant(cx, cx.typeck_results(), cond).is_some() {
 +        // A pure constant condition (e.g., `while false`) is not linted.
 +        return;
 +    }
 +
 +    let mut var_visitor = VarCollectorVisitor {
 +        cx,
 +        ids: HirIdSet::default(),
 +        def_ids: DefIdMap::default(),
 +        skip: false,
 +    };
 +    var_visitor.visit_expr(cond);
 +    if var_visitor.skip {
 +        return;
 +    }
 +    let used_in_condition = &var_visitor.ids;
++    let mutated_in_body = mutated_variables(expr, cx);
++    let mutated_in_condition = mutated_variables(cond, cx);
++    let no_cond_variable_mutated =
++        if let (Some(used_mutably_body), Some(used_mutably_cond)) = (mutated_in_body, mutated_in_condition) {
++            used_in_condition.is_disjoint(&used_mutably_body) && used_in_condition.is_disjoint(&used_mutably_cond)
++        } else {
++            return;
++        };
 +    let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
 +
 +    let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
 +        has_break_or_return: false,
 +    };
 +    has_break_or_return_visitor.visit_expr(expr);
 +    let has_break_or_return = has_break_or_return_visitor.has_break_or_return;
 +
 +    if no_cond_variable_mutated && !mutable_static_in_cond {
 +        span_lint_and_then(
 +            cx,
 +            WHILE_IMMUTABLE_CONDITION,
 +            cond.span,
 +            "variables in the condition are not mutated in the loop body",
 +            |diag| {
 +                diag.note("this may lead to an infinite or to a never running loop");
 +
 +                if has_break_or_return {
 +                    diag.note("this loop contains `return`s or `break`s");
 +                    diag.help("rewrite it as `if cond { loop { } }`");
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +struct HasBreakOrReturnVisitor {
 +    has_break_or_return: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_break_or_return {
 +            return;
 +        }
 +
 +        match expr.kind {
 +            ExprKind::Ret(_) | ExprKind::Break(_, _) => {
 +                self.has_break_or_return = true;
 +                return;
 +            },
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Collects the set of variables in an expression
 +/// Stops analysis if a function call is found
 +/// Note: In some cases such as `self`, there are no mutable annotation,
 +/// All variables definition IDs are collected
 +struct VarCollectorVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    ids: HirIdSet,
 +    def_ids: DefIdMap<bool>,
 +    skip: bool,
 +}
 +
 +impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
 +    fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Path(ref qpath) = ex.kind;
 +            if let QPath::Resolved(None, _) = *qpath;
 +            then {
 +                match self.cx.qpath_res(qpath, ex.hir_id) {
 +                    Res::Local(hir_id) => {
 +                        self.ids.insert(hir_id);
 +                    },
 +                    Res::Def(DefKind::Static, def_id) => {
 +                        let mutable = self.cx.tcx.is_mutable_static(def_id);
 +                        self.def_ids.insert(def_id, mutable);
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
 +        match ex.kind {
 +            ExprKind::Path(_) => self.insert_def_id(ex),
 +            // If there is any function/method call… we just stop analysis
 +            ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true,
 +
 +            _ => walk_expr(self, ex),
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 13b2a834b0a962ac4db369573dab48ddfc9bda16,0000000000000000000000000000000000000000..a70e8b26087efc1aa5bb2e0d346fe6eeb3789701
mode 100644,000000..100644
--- /dev/null
@@@ -1,2264 -1,0 +1,2272 @@@
-         if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
 +use crate::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::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};
 +use clippy_utils::visitors::LocalUsedVisitor;
 +use clippy_utils::{
 +    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, 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 if_chain::if_chain;
 +use rustc_ast::ast::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`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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)`
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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 let ExprKind::Match(ex, arms, _) = expr.kind {
 +            check_match_ref_pats(cx, ex, arms, 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_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 ident.as_str().starts_with('_')
 +                                    && !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
 +                                {
 +                                    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_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
 +    let attrs = cx.tcx.get_attrs(variant_def.def_id);
 +    clippy_utils::attrs::is_doc_hidden(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 mut missing_variants: Vec<_> = adt_def.variants.iter().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() && !is_doc_hidden(cx, x) => 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() {
 +                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(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if has_only_ref_pats(arms) {
 +        let mut suggs = Vec::with_capacity(arms.len() + 1);
 +        let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
 +            let span = ex.span.source_callsite();
 +            suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
 +            (
 +                "you don't need to add `&` to both the expression and the patterns",
 +                "try",
 +            )
 +        } else {
 +            let span = ex.span.source_callsite();
 +            suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
 +            (
 +                "you don't need to add `&` to all patterns",
 +                "instead of prefixing all patterns with `&`, you can dereference the expression",
 +            )
 +        };
 +
 +        suggs.extend(arms.iter().filter_map(|a| {
 +            if let PatKind::Ref(refp, _) = a.pat.kind {
 +                Some((a.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, 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 ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
 +        match match_source {
 +            MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
 +            MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Lint a `match` or desugared `if let` for replacement by `matches!`
 +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
 +    if_chain! {
 +        if arms.len() >= 2;
 +        if cx.typeck_results().expr_ty(expr).is_bool();
 +        if let Some((b1_arm, b0_arms)) = arms.split_last();
 +        if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
 +        if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
 +        if is_wild(&b1_arm.pat);
 +        if b0 != b1;
 +        let if_guard = &b0_arms[0].guard;
 +        if if_guard.is_none() || b0_arms.len() == 1;
 +        if cx.tcx.hir().attrs(b0_arms[0].hir_id).is_empty();
 +        if b0_arms[1..].iter()
 +            .all(|arm| {
 +                find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
 +                arm.guard.is_none() && cx.tcx.hir().attrs(arm.hir_id).is_empty()
 +            });
 +        then {
 +            // 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 _;
 +                b0_arms.iter()
 +                    .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
 +                    .join(" | ")
 +            };
 +            let pat_and_guard = if let Some(Guard::If(g)) = if_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 desugared { "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<'_>, desugared: bool) -> Option<bool> {
 +    match ex {
 +        ExprKind::Lit(Spanned {
 +            node: LitKind::Bool(b), ..
 +        }) => Some(*b),
 +        ExprKind::Block(
 +            rustc_hir::Block {
 +                stmts: &[],
 +                expr: Some(exp),
 +                ..
 +            },
 +            _,
 +        ) if desugared => {
 +            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 => {
 +            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()
 +}
 +
 +fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    match expr.kind {
 +        ExprKind::Tup(v) if v.is_empty() => true,
 +        ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
 +        _ => false,
 +    }
 +}
 +
 +// 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::Binding(rb, .., ident, _) = pats[0].kind;
++        if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
 +        if is_lang_ctor(cx, qpath, OptionSome);
-                 ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
-                 ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
++        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(arms: &[Arm<'_>]) -> bool {
 +    let mapped = arms
 +        .iter()
 +        .map(|a| {
 +            match a.pat.kind {
 +                PatKind::Ref(..) => Some(true), // &-patterns
 +                PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
 +                _ => None,                      // any other pattern is not fine
 +            }
 +        })
 +        .collect::<Option<Vec<bool>>>();
 +    // look for Some(v) where there's at least one true element
 +    mapped.map_or(false, |v| v.iter().any(|el| *el))
 +}
 +
 +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::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, 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 ExprKind::Match(op, arms, ref match_source) = &expr.kind {
 +            match match_source {
 +                MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
 +                MatchSource::IfLetDesugar { contains_else_clause } => {
 +                    find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
 +                },
 +                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "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() {
-                     .any(|ty| type_needs_ordered_drop(cx, ty)),
++                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))
-                 subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
++                    .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)
 +            || match_type(cx, ty, &paths::BTREEMAP)
 +            || match_type(cx, ty, &paths::LINKED_LIST)
 +            || 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<'_>,
 +        op: &'tcx Expr<'tcx>,
 +        arm: &Arm<'_>,
 +        keyword: &'static str,
 +        has_else: bool,
 +    ) {
 +        // also look inside refs
 +        let mut kind = &arm.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(op);
 +        // 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, op);
 +
 +        // check that `while_let_on_iterator` lint does not trigger
 +        if_chain! {
 +            if keyword == "while";
 +            if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
 +            if method_path.ident.name == sym::next;
 +            if is_trait_method(cx, op, sym::Iterator);
 +            then {
 +                return;
 +            }
 +        }
 +
 +        let result_expr = match &op.kind {
 +            ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +            _ => op,
 +        };
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_PATTERN_MATCHING,
 +            arm.pat.span,
 +            &format!("redundant pattern matching, consider using `{}`", good_method),
 +            |diag| {
 +                // while let ... = ... { ... }
 +                // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +                let expr_span = expr.span;
 +
 +                // while let ... = ... { ... }
 +                //                 ^^^
 +                let op_span = result_expr.span.source_callsite();
 +
 +                // 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));
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +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 e15dbb899b36aac49bd00dae5ebf6cc7058a4a5d,0000000000000000000000000000000000000000..0b1b6304defcb9246352a05df39c46239f23a312
mode 100644,000000..100644
--- /dev/null
@@@ -1,2309 -1,0 +1,2288 @@@
- #[rustfmt::skip]
- const PATTERN_METHODS: [(&str, usize); 17] = [
-     ("contains", 1),
-     ("starts_with", 1),
-     ("ends_with", 1),
-     ("find", 1),
-     ("rfind", 1),
-     ("split", 1),
-     ("rsplit", 1),
-     ("split_terminator", 1),
-     ("rsplit_terminator", 1),
-     ("splitn", 2),
-     ("rsplitn", 2),
-     ("matches", 1),
-     ("rmatches", 1),
-     ("match_indices", 1),
-     ("rmatch_indices", 1),
-     ("trim_start_matches", 1),
-     ("trim_end_matches", 1),
- ];
 +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 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 map_collect_result_unit;
 +mod map_flatten;
 +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 uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_lazy_eval;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +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, 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`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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:** This is the same as
 +    /// [`wrong_self_convention`](#wrong_self_convention), but for public items.
 +    ///
 +    /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).
 +    ///
 +    /// **Known problems:** Actually *renaming* the function may break clients if
 +    /// the function is part of the public interface. In that case, be mindful of
 +    /// the stability guarantees you've given your users.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # struct X;
 +    /// impl<'a> X {
 +    ///     pub fn as_str(self) -> &'a str {
 +    ///         "foo"
 +    ///     }
 +    /// }
 +    /// ```
 +    pub WRONG_PUB_SELF_CONVENTION,
 +    restriction,
 +    "defining a public 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 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()`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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)`.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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(_)`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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)`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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(_)`
 +    ///
 +    /// **Known problems:**
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +     /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +     /// **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(_)`.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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`.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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()`.
 +    ///
 +    /// **Known problems:** 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(_)`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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 the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// **Why is this bad?** `.push_str(s)` is clearer
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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(_)`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    pub SUSPICIOUS_MAP,
 +    complexity,
 +    "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
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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()`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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()`
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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)
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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,
 +    style,
 +    "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`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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 the use of `.bytes().nth()`.
 +    ///
 +    /// **Why is this bad?** `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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()`"
 +}
 +
 +pub struct Methods {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    WRONG_PUB_SELF_CONVENTION,
 +    OK_EXPECT,
 +    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,
 +    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
 +]);
 +
 +/// 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.hir_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
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                wrong_self_convention::check(
 +                    cx,
 +                    &name,
 +                    item.vis.node.is_pub(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg.pat.span,
 +                    implements_trait,
 +                    false
 +                );
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(projection_predicate.ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if contains_ty(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 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(),
 +                    false,
 +                    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(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", [recv, _]) => {
 +                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);
 +                },
 +                _ => {},
 +            },
 +            ("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),
 +            ("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_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");
 +                }
 +            },
 +            ("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) => {},
 +                _ => 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 f4090c7c617d4499c07cc79b6c3bcd5030f43ef4,0000000000000000000000000000000000000000..d313a3db479de6d51c5797985804403272cf32f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,57 @@@
-     for &(method, pos) in &crate::methods::PATTERN_METHODS {
 +use super::utils::get_hint_if_single_char_arg;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::symbol::Symbol;
 +
 +use super::SINGLE_CHAR_PATTERN;
 +
++const PATTERN_METHODS: [(&str, usize); 19] = [
++    ("contains", 1),
++    ("starts_with", 1),
++    ("ends_with", 1),
++    ("find", 1),
++    ("rfind", 1),
++    ("split", 1),
++    ("rsplit", 1),
++    ("split_terminator", 1),
++    ("rsplit_terminator", 1),
++    ("splitn", 2),
++    ("rsplitn", 2),
++    ("matches", 1),
++    ("rmatches", 1),
++    ("match_indices", 1),
++    ("rmatch_indices", 1),
++    ("strip_prefix", 1),
++    ("strip_suffix", 1),
++    ("trim_start_matches", 1),
++    ("trim_end_matches", 1),
++];
++
 +/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
 +pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
++    for &(method, pos) in &PATTERN_METHODS {
 +        if_chain! {
 +            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
 +            if *ty.kind() == ty::Str;
 +            if method_name.as_str() == method && args.len() > pos;
 +            let arg = &args[pos];
 +            let mut applicability = Applicability::MachineApplicable;
 +            if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    SINGLE_CHAR_PATTERN,
 +                    arg.span,
 +                    "single-character string constant used as pattern",
 +                    "try using a `char` instead",
 +                    hint,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index b61c4ffe9b3aebb5abd0a4d39ec94abf1d75e830,0000000000000000000000000000000000000000..8b66587bfd1620910db1411507609967275976c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,132 @@@
-         if !found_filtering {
-             span_lint(
-                 cx,
-                 UNNECESSARY_FILTER_MAP,
-                 expr.span,
-                 "this `.filter_map` can be written more simply using `.map`",
-             );
-             return;
-         }
-         if !found_mapping && !mutates_arg {
-             span_lint(
-                 cx,
-                 UNNECESSARY_FILTER_MAP,
-                 expr.span,
-                 "this `.filter_map` can be written more simply using `.filter`",
-             );
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::usage::mutated_variables;
 +use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
++use rustc_middle::ty::{self, TyS};
 +use rustc_span::sym;
 +
 +use super::UNNECESSARY_FILTER_MAP;
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
 +    if !is_trait_method(cx, expr, sym::Iterator) {
 +        return;
 +    }
 +
 +    if let hir::ExprKind::Closure(_, _, body_id, ..) = arg.kind {
 +        let body = cx.tcx.hir().body(body_id);
 +        let arg_id = body.params[0].pat.hir_id;
 +        let mutates_arg =
 +            mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
 +
 +        let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
 +
 +        let mut return_visitor = ReturnVisitor::new(cx, arg_id);
 +        return_visitor.visit_expr(&body.value);
 +        found_mapping |= return_visitor.found_mapping;
 +        found_filtering |= return_visitor.found_filtering;
 +
-         }
++        let sugg = if !found_filtering {
++            "map"
++        } else if !found_mapping && !mutates_arg {
++            let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
++            match cx.typeck_results().expr_ty(&body.value).kind() {
++                ty::Adt(adt, subst)
++                    if cx.tcx.is_diagnostic_item(sym::option_type, adt.did)
++                        && TyS::same_type(in_ty, subst.type_at(0)) =>
++                {
++                    "filter"
++                },
++                _ => return,
++            }
++        } else {
 +            return;
++        };
++        span_lint(
++            cx,
++            UNNECESSARY_FILTER_MAP,
++            expr.span,
++            &format!("this `.filter_map` can be written more simply using `.{}`", sugg),
++        );
 +    }
 +}
 +
 +// returns (found_mapping, found_filtering)
 +fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
 +    match &expr.kind {
 +        hir::ExprKind::Call(func, args) => {
 +            if let hir::ExprKind::Path(ref path) = func.kind {
 +                if is_lang_ctor(cx, path, OptionSome) {
 +                    if path_to_local_id(&args[0], arg_id) {
 +                        return (false, false);
 +                    }
 +                    return (true, false);
 +                }
 +            }
 +            (true, true)
 +        },
 +        hir::ExprKind::Block(block, _) => block
 +            .expr
 +            .as_ref()
 +            .map_or((false, false), |expr| check_expression(cx, arg_id, expr)),
 +        hir::ExprKind::Match(_, arms, _) => {
 +            let mut found_mapping = false;
 +            let mut found_filtering = false;
 +            for arm in *arms {
 +                let (m, f) = check_expression(cx, arg_id, arm.body);
 +                found_mapping |= m;
 +                found_filtering |= f;
 +            }
 +            (found_mapping, found_filtering)
 +        },
 +        // There must be an else_arm or there will be a type error
 +        hir::ExprKind::If(_, if_arm, Some(else_arm)) => {
 +            let if_check = check_expression(cx, arg_id, if_arm);
 +            let else_check = check_expression(cx, arg_id, else_arm);
 +            (if_check.0 | else_check.0, if_check.1 | else_check.1)
 +        },
 +        hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
 +        _ => (true, true),
 +    }
 +}
 +
 +struct ReturnVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    arg_id: hir::HirId,
 +    // Found a non-None return that isn't Some(input)
 +    found_mapping: bool,
 +    // Found a return that isn't Some
 +    found_filtering: bool,
 +}
 +
 +impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
 +        ReturnVisitor {
 +            cx,
 +            arg_id,
 +            found_mapping: false,
 +            found_filtering: false,
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
 +            let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
 +            self.found_mapping |= found_mapping;
 +            self.found_filtering |= found_filtering;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f6b0bdc7a45a83335b51dbce36113e537ec7f80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast::{GenericParam, GenericParamKind};
++use rustc_hir::PrimTy;
++use rustc_lint::EarlyContext;
++
++use super::BUILTIN_TYPE_SHADOW;
++
++pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) {
++    if let GenericParamKind::Type { .. } = param.kind {
++        if let Some(prim_ty) = PrimTy::from_name(param.ident.name) {
++            span_lint(
++                cx,
++                BUILTIN_TYPE_SHADOW,
++                param.ident.span,
++                &format!("this generic shadows the built-in type `{}`", prim_ty.name()),
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f65778e1193cfc08d011e0eec606f08e683bf6a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++use super::MiscEarlyLints;
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast::{Expr, ExprKind, UnOp};
++use rustc_lint::EarlyContext;
++
++use super::DOUBLE_NEG;
++
++pub(super) fn check(cx: &EarlyContext<'_>, expr: &Expr) {
++    match expr.kind {
++        ExprKind::Unary(UnOp::Neg, ref inner) => {
++            if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
++                span_lint(
++                    cx,
++                    DOUBLE_NEG,
++                    expr.span,
++                    "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
++                );
++            }
++        },
++        ExprKind::Lit(ref lit) => MiscEarlyLints::check_lit(cx, lit),
++        _ => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80e24213100787f572d6e237b1d86f223ed6034a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast::Lit;
++use rustc_lint::EarlyContext;
++
++use super::MIXED_CASE_HEX_LITERALS;
++
++pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) {
++    let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++        val
++    } else {
++        return; // It's useless so shouldn't lint.
++    };
++    if maybe_last_sep_idx <= 2 {
++        // It's meaningless or causes range error.
++        return;
++    }
++    let mut seen = (false, false);
++    for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
++        match ch {
++            b'a'..=b'f' => seen.0 = true,
++            b'A'..=b'F' => seen.1 = true,
++            _ => {},
++        }
++        if seen.0 && seen.1 {
++            span_lint(
++                cx,
++                MIXED_CASE_HEX_LITERALS,
++                lit.span,
++                "inconsistent casing in hexadecimal literal",
++            );
++            break;
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd38316fa25b0cea0f8334e76a2a8ae20d1351a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,348 @@@
++mod builtin_type_shadow;
++mod double_neg;
++mod mixed_case_hex_literals;
++mod redundant_pattern;
++mod unneeded_field_pattern;
++mod unneeded_wildcard_pattern;
++mod unseparated_literal_suffix;
++mod zero_prefixed_literal;
++
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::source::snippet_opt;
++use rustc_ast::ast::{Expr, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
++use rustc_ast::visit::FnKind;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for structure field patterns bound to wildcards.
++    ///
++    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
++    /// the fields that are actually bound.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct Foo {
++    /// #     a: i32,
++    /// #     b: i32,
++    /// #     c: i32,
++    /// # }
++    /// let f = Foo { a: 0, b: 0, c: 0 };
++    ///
++    /// // Bad
++    /// match f {
++    ///     Foo { a: _, b: 0, .. } => {},
++    ///     Foo { a: _, b: _, c: _ } => {},
++    /// }
++    ///
++    /// // Good
++    /// match f {
++    ///     Foo { b: 0, .. } => {},
++    ///     Foo { .. } => {},
++    /// }
++    /// ```
++    pub UNNEEDED_FIELD_PATTERN,
++    restriction,
++    "struct fields bound to a wildcard instead of using `..`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for function arguments having the similar names
++    /// differing by an underscore.
++    ///
++    /// **Why is this bad?** It affects code readability.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad
++    /// fn foo(a: i32, _a: i32) {}
++    ///
++    /// // Good
++    /// fn bar(a: i32, _b: i32) {}
++    /// ```
++    pub DUPLICATE_UNDERSCORE_ARGUMENT,
++    style,
++    "function arguments having names which only differ by an underscore"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects expressions of the form `--x`.
++    ///
++    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
++    /// decremented.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut x = 3;
++    /// --x;
++    /// ```
++    pub DOUBLE_NEG,
++    style,
++    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
++    /// digits.
++    ///
++    /// **Why is this bad?** It looks confusing.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad
++    /// let y = 0x1a9BAcD;
++    ///
++    /// // Good
++    /// let y = 0x1A9BACD;
++    /// ```
++    pub MIXED_CASE_HEX_LITERALS,
++    style,
++    "hex literals whose letter digits are not consistently upper- or lowercased"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if literal suffixes are not separated by an
++    /// underscore.
++    ///
++    /// **Why is this bad?** It is much less readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad
++    /// let y = 123832i32;
++    ///
++    /// // Good
++    /// let y = 123832_i32;
++    /// ```
++    pub UNSEPARATED_LITERAL_SUFFIX,
++    pedantic,
++    "literals whose suffix is not separated by an underscore"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if an integral constant literal starts with `0`.
++    ///
++    /// **Why is this bad?** In some languages (including the infamous C language
++    /// and most of its
++    /// family), this marks an octal constant. In Rust however, this is a decimal
++    /// constant. This could
++    /// be confusing for both the writer and a reader of the constant.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// In Rust:
++    /// ```rust
++    /// fn main() {
++    ///     let a = 0123;
++    ///     println!("{}", a);
++    /// }
++    /// ```
++    ///
++    /// prints `123`, while in C:
++    ///
++    /// ```c
++    /// #include <stdio.h>
++    ///
++    /// int main() {
++    ///     int a = 0123;
++    ///     printf("%d\n", a);
++    /// }
++    /// ```
++    ///
++    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
++    pub ZERO_PREFIXED_LITERAL,
++    complexity,
++    "integer literals starting with `0`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if a generic shadows a built-in type.
++    ///
++    /// **Why is this bad?** This gives surprising type errors.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```ignore
++    /// impl<u32> Foo<u32> {
++    ///     fn impl_func(&self) -> u32 {
++    ///         42
++    ///     }
++    /// }
++    /// ```
++    pub BUILTIN_TYPE_SHADOW,
++    style,
++    "shadowing a builtin type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for patterns in the form `name @ _`.
++    ///
++    /// **Why is this bad?** It's almost always more readable to just use direct
++    /// bindings.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let v = Some("abc");
++    ///
++    /// // Bad
++    /// match v {
++    ///     Some(x) => (),
++    ///     y @ _ => (),
++    /// }
++    ///
++    /// // Good
++    /// match v {
++    ///     Some(x) => (),
++    ///     y => (),
++    /// }
++    /// ```
++    pub REDUNDANT_PATTERN,
++    style,
++    "using `name @ _` in a pattern"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for tuple patterns with a wildcard
++    /// pattern (`_`) is next to a rest pattern (`..`).
++    ///
++    /// _NOTE_: While `_, ..` means there is at least one element left, `..`
++    /// means there are 0 or more elements left. This can make a difference
++    /// when refactoring, but shouldn't result in errors in the refactored code,
++    /// since the wildcard pattern isn't used anyway.
++    /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
++    /// can match that element as well.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct TupleStruct(u32, u32, u32);
++    /// # let t = TupleStruct(1, 2, 3);
++    /// // Bad
++    /// match t {
++    ///     TupleStruct(0, .., _) => (),
++    ///     _ => (),
++    /// }
++    ///
++    /// // Good
++    /// match t {
++    ///     TupleStruct(0, ..) => (),
++    ///     _ => (),
++    /// }
++    /// ```
++    pub UNNEEDED_WILDCARD_PATTERN,
++    complexity,
++    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
++}
++
++declare_lint_pass!(MiscEarlyLints => [
++    UNNEEDED_FIELD_PATTERN,
++    DUPLICATE_UNDERSCORE_ARGUMENT,
++    DOUBLE_NEG,
++    MIXED_CASE_HEX_LITERALS,
++    UNSEPARATED_LITERAL_SUFFIX,
++    ZERO_PREFIXED_LITERAL,
++    BUILTIN_TYPE_SHADOW,
++    REDUNDANT_PATTERN,
++    UNNEEDED_WILDCARD_PATTERN,
++]);
++
++impl EarlyLintPass for MiscEarlyLints {
++    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
++        for param in &gen.params {
++            builtin_type_shadow::check(cx, param);
++        }
++    }
++
++    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
++        unneeded_field_pattern::check(cx, pat);
++        redundant_pattern::check(cx, pat);
++        unneeded_wildcard_pattern::check(cx, pat);
++    }
++
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
++        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
++
++        for arg in &fn_kind.decl().inputs {
++            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
++                let arg_name = ident.to_string();
++
++                if let Some(arg_name) = arg_name.strip_prefix('_') {
++                    if let Some(correspondence) = registered_names.get(arg_name) {
++                        span_lint(
++                            cx,
++                            DUPLICATE_UNDERSCORE_ARGUMENT,
++                            *correspondence,
++                            &format!(
++                                "`{}` already exists, having another argument having almost the same \
++                                 name makes code comprehension and documentation more difficult",
++                                arg_name
++                            ),
++                        );
++                    }
++                } else {
++                    registered_names.insert(arg_name, arg.pat.span);
++                }
++            }
++        }
++    }
++
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++        double_neg::check(cx, expr)
++    }
++}
++
++impl MiscEarlyLints {
++    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
++        // We test if first character in snippet is a number, because the snippet could be an expansion
++        // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
++        // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
++        // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
++        // FIXME: Find a better way to detect those cases.
++        let lit_snip = match snippet_opt(cx, lit.span) {
++            Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
++            _ => return,
++        };
++
++        if let LitKind::Int(value, lit_int_type) = lit.kind {
++            let suffix = match lit_int_type {
++                LitIntType::Signed(ty) => ty.name_str(),
++                LitIntType::Unsigned(ty) => ty.name_str(),
++                LitIntType::Unsuffixed => "",
++            };
++            unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
++            if lit_snip.starts_with("0x") {
++                mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip)
++            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
++                /* nothing to do */
++            } else if value != 0 && lit_snip.starts_with('0') {
++                zero_prefixed_literal::check(cx, lit, &lit_snip)
++            }
++        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
++            let suffix = float_ty.name_str();
++            unseparated_literal_suffix::check(cx, lit, &lit_snip, suffix, "float")
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..525dbf7757c14c3c7e97cbffad764fca4d51a442
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::ast::{BindingMode, Mutability, Pat, PatKind};
++use rustc_errors::Applicability;
++use rustc_lint::EarlyContext;
++
++use super::REDUNDANT_PATTERN;
++
++pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
++    if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
++        let left_binding = match left {
++            BindingMode::ByRef(Mutability::Mut) => "ref mut ",
++            BindingMode::ByRef(Mutability::Not) => "ref ",
++            BindingMode::ByValue(..) => "",
++        };
++
++        if let PatKind::Wild = right.kind {
++            span_lint_and_sugg(
++                cx,
++                REDUNDANT_PATTERN,
++                pat.span,
++                &format!(
++                    "the `{} @ _` pattern can be written as just `{}`",
++                    ident.name, ident.name,
++                ),
++                "try",
++                format!("{}{}", left_binding, ident.name),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..329a0009a3e2cfc014aa00a1a92f8b37996926ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
++use rustc_ast::ast::{Pat, PatKind};
++use rustc_lint::{EarlyContext, LintContext};
++
++use super::UNNEEDED_FIELD_PATTERN;
++
++pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
++    if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
++        let mut wilds = 0;
++        let type_name = npat
++            .segments
++            .last()
++            .expect("A path must have at least one segment")
++            .ident
++            .name;
++
++        for field in pfields {
++            if let PatKind::Wild = field.pat.kind {
++                wilds += 1;
++            }
++        }
++        if !pfields.is_empty() && wilds == pfields.len() {
++            span_lint_and_help(
++                cx,
++                UNNEEDED_FIELD_PATTERN,
++                pat.span,
++                "all the struct fields are matched to a wildcard pattern, consider using `..`",
++                None,
++                &format!("try with `{} {{ .. }}` instead", type_name),
++            );
++            return;
++        }
++        if wilds > 0 {
++            for field in pfields {
++                if let PatKind::Wild = field.pat.kind {
++                    wilds -= 1;
++                    if wilds > 0 {
++                        span_lint(
++                            cx,
++                            UNNEEDED_FIELD_PATTERN,
++                            field.span,
++                            "you matched a field with a wildcard pattern, consider using `..` instead",
++                        );
++                    } else {
++                        let mut normal = vec![];
++
++                        for field in pfields {
++                            match field.pat.kind {
++                                PatKind::Wild => {},
++                                _ => {
++                                    if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
++                                        normal.push(n);
++                                    }
++                                },
++                            }
++                        }
++
++                        span_lint_and_help(
++                            cx,
++                            UNNEEDED_FIELD_PATTERN,
++                            field.span,
++                            "you matched a field with a wildcard pattern, consider using `..` \
++                             instead",
++                            None,
++                            &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
++                        );
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dd032d78f1d55f2651b43114036634c99e5bcf7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::ast::{Pat, PatKind};
++use rustc_errors::Applicability;
++use rustc_lint::EarlyContext;
++use rustc_span::source_map::Span;
++
++use super::UNNEEDED_WILDCARD_PATTERN;
++
++pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
++    if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
++        if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
++            if let Some((left_index, left_pat)) = patterns[..rest_index]
++                .iter()
++                .rev()
++                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
++                .enumerate()
++                .last()
++            {
++                span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
++            }
++
++            if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
++                .iter()
++                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
++                .enumerate()
++                .last()
++            {
++                span_lint(
++                    cx,
++                    patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
++                    right_index == 0,
++                );
++            }
++        }
++    }
++}
++
++fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
++    span_lint_and_sugg(
++        cx,
++        UNNEEDED_WILDCARD_PATTERN,
++        span,
++        if only_one {
++            "this pattern is unneeded as the `..` pattern can match that element"
++        } else {
++            "these patterns are unneeded as the `..` pattern can match those elements"
++        },
++        if only_one { "remove it" } else { "remove them" },
++        "".to_string(),
++        Applicability::MachineApplicable,
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2018aa6184a8db4568f751017d6e5514cbcda95f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::ast::Lit;
++use rustc_errors::Applicability;
++use rustc_lint::EarlyContext;
++
++use super::UNSEPARATED_LITERAL_SUFFIX;
++
++pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) {
++    let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++        val
++    } else {
++        return; // It's useless so shouldn't lint.
++    };
++    // Do not lint when literal is unsuffixed.
++    if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
++        span_lint_and_sugg(
++            cx,
++            UNSEPARATED_LITERAL_SUFFIX,
++            lit.span,
++            &format!("{} type suffix should be separated by an underscore", sugg_type),
++            "add an underscore",
++            format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++            Applicability::MachineApplicable,
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4963bba82f2da169887da3f88a3a5c7641a64000
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use rustc_ast::ast::Lit;
++use rustc_errors::Applicability;
++use rustc_lint::EarlyContext;
++
++use super::ZERO_PREFIXED_LITERAL;
++
++pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) {
++    span_lint_and_then(
++        cx,
++        ZERO_PREFIXED_LITERAL,
++        lit.span,
++        "this is a decimal constant",
++        |diag| {
++            diag.span_suggestion(
++                lit.span,
++                "if you mean to use a decimal constant, remove the `0` to avoid confusion",
++                lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
++                Applicability::MaybeIncorrect,
++            );
++            diag.span_suggestion(
++                lit.span,
++                "if you mean to use an octal constant, use `0o`",
++                format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
++                Applicability::MaybeIncorrect,
++            );
++        },
++    );
++}
index 191781be000cf8177919236a38389dafac24c3df,0000000000000000000000000000000000000000..a9ae2b77119bcaa1bda2f9d9d6bfa4264c2b2899
mode 100644,000000..100644
--- /dev/null
@@@ -1,324 -1,0 +1,314 @@@
- use rustc_lint::{LateContext, LateLintPass, Lint};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor};
 +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
-             InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint(
-                 cx,
-                 e,
-                 vec_alloc,
-                 "slow zero-filling initialization",
-                 SLOW_VECTOR_INITIALIZATION,
-             ),
++use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks slow zero-filled vector initialization
 +    ///
 +    /// **Why is this bad?** These structures are non-idiomatic and less efficient than simply using
 +    /// `vec![0; len]`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # use core::iter::repeat;
 +    /// # let len = 4;
 +    ///
 +    /// // Bad
 +    /// let mut vec1 = Vec::with_capacity(len);
 +    /// vec1.resize(len, 0);
 +    ///
 +    /// let mut vec2 = Vec::with_capacity(len);
 +    /// vec2.extend(repeat(0).take(len));
 +    ///
 +    /// // Good
 +    /// let mut vec1 = vec![0; len];
 +    /// let mut vec2 = vec![0; len];
 +    /// ```
 +    pub SLOW_VECTOR_INITIALIZATION,
 +    perf,
 +    "slow vector initialization"
 +}
 +
 +declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
 +
 +/// `VecAllocation` contains data regarding a vector allocated with `with_capacity` and then
 +/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
 +/// `vec = Vec::with_capacity(0)`
 +struct VecAllocation<'tcx> {
 +    /// HirId of the variable
 +    local_id: HirId,
 +
 +    /// Reference to the expression which allocates the vector
 +    allocation_expr: &'tcx Expr<'tcx>,
 +
 +    /// Reference to the expression used as argument on `with_capacity` call. This is used
 +    /// to only match slow zero-filling idioms of the same length than vector initialization.
 +    len_expr: &'tcx Expr<'tcx>,
 +}
 +
 +/// Type of slow initialization
 +enum InitializationType<'tcx> {
 +    /// Extend is a slow initialization with the form `vec.extend(repeat(0).take(..))`
 +    Extend(&'tcx Expr<'tcx>),
 +
 +    /// Resize is a slow initialization with the form `vec.resize(.., 0)`
 +    Resize(&'tcx Expr<'tcx>),
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
 +        if_chain! {
 +            if let ExprKind::Assign(left, right, _) = expr.kind;
 +
 +            // Extract variable
 +            if let Some(local_id) = path_to_local(left);
 +
 +            // Extract len argument
 +            if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
 +
 +            then {
 +                let vi = VecAllocation {
 +                    local_id,
 +                    allocation_expr: right,
 +                    len_expr: len_arg,
 +                };
 +
 +                Self::search_initialization(cx, vi, expr.hir_id);
 +            }
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
 +        if_chain! {
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
 +            if let Some(init) = local.init;
 +            if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
 +
 +            then {
 +                let vi = VecAllocation {
 +                    local_id,
 +                    allocation_expr: init,
 +                    len_expr: len_arg,
 +                };
 +
 +                Self::search_initialization(cx, vi, stmt.hir_id);
 +            }
 +        }
 +    }
 +}
 +
 +impl SlowVectorInit {
 +    /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
 +    /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
 +    fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = expr.kind;
 +            if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
 +            if name.ident.as_str() == "with_capacity";
 +            if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type);
 +            then {
 +                Some(arg)
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +
 +    /// Search initialization for the given vector
 +    fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
 +        let enclosing_body = get_enclosing_block(cx, parent_node);
 +
 +        if enclosing_body.is_none() {
 +            return;
 +        }
 +
 +        let mut v = VectorInitializationVisitor {
 +            cx,
 +            vec_alloc,
 +            slow_expression: None,
 +            initialization_found: false,
 +        };
 +
 +        v.visit_block(enclosing_body.unwrap());
 +
 +        if let Some(ref allocation_expr) = v.slow_expression {
 +            Self::lint_initialization(cx, allocation_expr, &v.vec_alloc);
 +        }
 +    }
 +
 +    fn lint_initialization<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        initialization: &InitializationType<'tcx>,
 +        vec_alloc: &VecAllocation<'_>,
 +    ) {
 +        match initialization {
-     fn emit_lint<'tcx>(
-         cx: &LateContext<'tcx>,
-         slow_fill: &Expr<'_>,
-         vec_alloc: &VecAllocation<'_>,
-         msg: &str,
-         lint: &'static Lint,
-     ) {
++            InitializationType::Extend(e) | InitializationType::Resize(e) => {
++                Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization")
++            },
 +        };
 +    }
 +
-         span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| {
++    fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
 +        let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
 +
++        span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
 +            diag.span_suggestion(
 +                vec_alloc.allocation_expr.span,
 +                "consider replace allocation with",
 +                format!("vec![0; {}]", len_expr),
 +                Applicability::Unspecified,
 +            );
 +        });
 +    }
 +}
 +
 +/// `VectorInitializationVisitor` searches for unsafe or slow vector initializations for the given
 +/// vector.
 +struct VectorInitializationVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +
 +    /// Contains the information.
 +    vec_alloc: VecAllocation<'tcx>,
 +
 +    /// Contains the slow initialization expression, if one was found.
 +    slow_expression: Option<InitializationType<'tcx>>,
 +
 +    /// `true` if the initialization of the vector has been found on the visited block.
 +    initialization_found: bool,
 +}
 +
 +impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
 +    /// Checks if the given expression is extending a vector with `repeat(0).take(..)`
 +    fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if self.initialization_found;
 +            if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind;
 +            if path_to_local_id(self_arg, self.vec_alloc.local_id);
 +            if path.ident.name == sym!(extend);
 +            if self.is_repeat_take(extend_arg);
 +
 +            then {
 +                self.slow_expression = Some(InitializationType::Extend(expr));
 +            }
 +        }
 +    }
 +
 +    /// Checks if the given expression is resizing a vector with 0
 +    fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if self.initialization_found;
 +            if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind;
 +            if path_to_local_id(self_arg, self.vec_alloc.local_id);
 +            if path.ident.name == sym!(resize);
 +
 +            // Check that is filled with 0
 +            if let ExprKind::Lit(ref lit) = fill_arg.kind;
 +            if let LitKind::Int(0, _) = lit.node;
 +
 +            // Check that len expression is equals to `with_capacity` expression
 +            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
 +
 +            then {
 +                self.slow_expression = Some(InitializationType::Resize(expr));
 +            }
 +        }
 +    }
 +
 +    /// Returns `true` if give expression is `repeat(0).take(...)`
 +    fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
 +        if_chain! {
 +            if let ExprKind::MethodCall(take_path, _, take_args, _) = expr.kind;
 +            if take_path.ident.name == sym!(take);
 +
 +            // Check that take is applied to `repeat(0)`
 +            if let Some(repeat_expr) = take_args.get(0);
 +            if self.is_repeat_zero(repeat_expr);
 +
 +            // Check that len expression is equals to `with_capacity` expression
 +            if let Some(len_arg) = take_args.get(1);
 +            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
 +
 +            then {
 +                return true;
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    /// Returns `true` if given expression is `repeat(0)`
 +    fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
 +        if_chain! {
 +            if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
 +            if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
 +            if let ExprKind::Lit(ref lit) = repeat_arg.kind;
 +            if let LitKind::Int(0, _) = lit.node;
 +
 +            then {
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        if self.initialization_found {
 +            match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                    self.search_slow_extend_filling(expr);
 +                    self.search_slow_resize_filling(expr);
 +                },
 +                _ => (),
 +            }
 +
 +            self.initialization_found = false;
 +        } else {
 +            walk_stmt(self, stmt);
 +        }
 +    }
 +
 +    fn visit_block(&mut self, block: &'tcx Block<'_>) {
 +        if self.initialization_found {
 +            if let Some(s) = block.stmts.get(0) {
 +                self.visit_stmt(s)
 +            }
 +
 +            self.initialization_found = false;
 +        } else {
 +            walk_block(self, block);
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        // Skip all the expressions previous to the vector initialization
 +        if self.vec_alloc.allocation_expr.hir_id == expr.hir_id {
 +            self.initialization_found = true;
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index ce2d0b3ab2f24962599d2ca957d5564641a2e193,0000000000000000000000000000000000000000..e14945651f5010551820308a272693951cc992ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,144 @@@
-             if is_unit_expr(expr) && !stmt.span.from_expansion();
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::position_before_rarrow;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_ast::visit::FnKind;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::BytePos;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
 +    ///
 +    /// **Why is this bad?** Such expressions add no value, but can make the code
 +    /// less readable. Depending on formatting they can make a `break` or `return`
 +    /// statement look like a function call.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// fn return_unit() -> () {
 +    ///     ()
 +    /// }
 +    /// ```
 +    pub UNUSED_UNIT,
 +    style,
 +    "needless unit expression"
 +}
 +
 +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]);
 +
 +impl EarlyLintPass for UnusedUnit {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
 +        if_chain! {
 +            if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
 +            if let ast::TyKind::Tup(ref vals) = ty.kind;
 +            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
 +            then {
 +                lint_unneeded_unit_return(cx, ty, span);
 +            }
 +        }
 +    }
 +
 +    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
 +        if_chain! {
 +            if let Some(stmt) = block.stmts.last();
 +            if let ast::StmtKind::Expr(ref expr) = stmt.kind;
++            if is_unit_expr(expr);
++            let ctxt = block.span.ctxt();
++            if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt;
 +            then {
 +                let sp = expr.span;
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNUSED_UNIT,
 +                    sp,
 +                    "unneeded unit expression",
 +                    "remove the final `()`",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 +        match e.kind {
 +            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
 +                if is_unit_expr(expr) && !expr.span.from_expansion() {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNUSED_UNIT,
 +                        expr.span,
 +                        "unneeded `()`",
 +                        "remove the `()`",
 +                        String::new(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
 +        let segments = &poly.trait_ref.path.segments;
 +
 +        if_chain! {
 +            if segments.len() == 1;
 +            if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
 +            if let Some(args) = &segments[0].args;
 +            if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
 +            if let ast::FnRetTy::Ty(ty) = &generic_args.output;
 +            if ty.kind.is_unit();
 +            then {
 +                lint_unneeded_unit_return(cx, ty, generic_args.span);
 +            }
 +        }
 +    }
 +}
 +
 +// get the def site
 +#[must_use]
 +fn get_def(span: Span) -> Option<Span> {
 +    if span.from_expansion() {
 +        Some(span.ctxt().outer_expn_data().def_site)
 +    } else {
 +        None
 +    }
 +}
 +
 +// is this expr a `()` unit?
 +fn is_unit_expr(expr: &ast::Expr) -> bool {
 +    if let ast::ExprKind::Tup(ref vals) = expr.kind {
 +        vals.is_empty()
 +    } else {
 +        false
 +    }
 +}
 +
 +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
 +    let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
 +        position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
 +            (
 +                #[allow(clippy::cast_possible_truncation)]
 +                ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
 +                Applicability::MachineApplicable,
 +            )
 +        })
 +    } else {
 +        (ty.span, Applicability::MaybeIncorrect)
 +    };
 +    span_lint_and_sugg(
 +        cx,
 +        UNUSED_UNIT,
 +        ret_span,
 +        "unneeded unit return type",
 +        "remove the `-> ()`",
 +        String::new(),
 +        appl,
 +    );
 +}
index d56855a71c159b2e1cdc351d7708e9f8371b3e46,0000000000000000000000000000000000000000..52c1dc3bdd239335438e40463066087a52d6b35f
mode 100644,000000..100644
--- /dev/null
@@@ -1,259 -1,0 +1,211 @@@
- #![deny(clippy::missing_docs_in_private_items)]
 +//! Read configurations files.
 +
- use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
- use rustc_span::source_map;
- use source_map::Span;
- use std::lazy::SyncLazy;
++#![allow(clippy::module_name_repetitions)]
 +
- use std::sync::Mutex;
++use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
++use serde::Deserialize;
++use std::error::Error;
 +use std::path::{Path, PathBuf};
- /// Gets the configuration file from arguments.
- pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
-     for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
-         if arg.has_name(sym!(conf_file)) {
-             return match arg.kind {
-                 MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
-                 MetaItemKind::NameValue(ref value) => {
-                     if let LitKind::Str(ref file, _) = value.kind {
-                         Ok(Some(file.to_string().into()))
-                     } else {
-                         Err(("`conf_file` value must be a string", value.span))
-                     }
-                 },
-             };
-         }
-     }
-     Ok(None)
- }
- /// Error from reading a configuration file.
- #[derive(Debug)]
- pub enum Error {
-     /// An I/O error.
-     Io(io::Error),
-     /// Not valid toml or doesn't fit the expected config format
-     Toml(String),
 +use std::{env, fmt, fs, io};
 +
- impl fmt::Display for Error {
-     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-         match self {
-             Self::Io(err) => err.fmt(f),
-             Self::Toml(err) => err.fmt(f),
++/// Conf with parse errors
++#[derive(Default)]
++pub struct TryConf {
++    pub conf: Conf,
++    pub errors: Vec<String>,
 +}
 +
- impl From<io::Error> for Error {
-     fn from(e: io::Error) -> Self {
-         Self::Io(e)
-     }
- }
++impl TryConf {
++    fn from_error(error: impl Error) -> Self {
++        Self {
++            conf: Conf::default(),
++            errors: vec![error.to_string()],
 +        }
 +    }
 +}
 +
- /// Vec of errors that might be collected during config toml parsing
- static ERRORS: SyncLazy<Mutex<Vec<Error>>> = SyncLazy::new(|| Mutex::new(Vec::new()));
++macro_rules! define_Conf {
++    ($(
++        #[$doc:meta]
++        $(#[conf_deprecated($dep:literal)])?
++        ($name:ident: $ty:ty = $default:expr),
++    )*) => {
++        /// Clippy lint configuration
++        pub struct Conf {
++            $(#[$doc] pub $name: $ty,)*
++        }
 +
- macro_rules! define_Conf {
-     ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
-         mod helpers {
-             use serde::Deserialize;
-             /// Type used to store lint configuration.
-             #[derive(Deserialize)]
-             #[serde(rename_all = "kebab-case", deny_unknown_fields)]
-             pub struct Conf {
-                 $(
-                     #[$doc]
-                     #[serde(default = $config_str)]
-                     #[serde(with = $config_str)]
-                     pub $config: $Ty,
-                 )+
-                 #[allow(dead_code)]
-                 #[serde(default)]
-                 third_party: Option<::toml::Value>,
++        mod defaults {
++            $(pub fn $name() -> $ty { $default })*
++        }
 +
-             $(
-                 mod $config {
-                     use serde::Deserialize;
-                     pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
-                         use super::super::{ERRORS, Error};
++        impl Default for Conf {
++            fn default() -> Self {
++                Self { $($name: defaults::$name(),)* }
 +            }
++        }
 +
-                         Ok(
-                             <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
-                                 ERRORS
-                                     .lock()
-                                     .expect("no threading here")
-                                     .push(Error::Toml(e.to_string()));
-                                 super::$config()
-                             })
-                         )
-                     }
-                 }
++        impl<'de> Deserialize<'de> for TryConf {
++            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
++                deserializer.deserialize_map(ConfVisitor)
++            }
++        }
 +
-                 #[must_use]
-                 fn $config() -> $Ty {
-                     let x = $default;
-                     x
++        #[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>())
++                    }
 +                }
- pub use self::helpers::Conf;
++                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
++                Ok(TryConf { conf, errors })
++            }
 +        }
 +    };
 +}
 +
-     /// Lint: 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. The minimum rust version that the project supports
-     (msrv, "msrv": Option<String>, None),
++// N.B., this macro is parsed by util/lintlib.py
 +define_Conf! {
-     (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
++    /// Lint: 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. 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
-     (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
++    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
-     (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
++    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
-     (doc_valid_idents, "doc_valid_idents": Vec<String>, [
++    #[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
-     (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
++    (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",
 +        "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
-     (type_complexity_threshold, "type_complexity_threshold": u64, 250),
++    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
-     (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
++    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
-     (too_large_for_stack, "too_large_for_stack": u64, 200),
++    (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
-     (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
++    (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_size_threshold, "enum_variant_size_threshold": u64, 200),
++    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
-     (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
++    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
-     (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
++    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
-     (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
++    (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.
-     (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
++    (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.
-     (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
++    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
-     (array_size_threshold, "array_size_threshold": u64, 512_000),
++    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
-     (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
++    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
-     (max_trait_bounds, "max_trait_bounds": u64, 3),
++    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
-     (max_struct_bools, "max_struct_bools": u64, 3),
++    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
-     (max_fn_params_bools, "max_fn_params_bools": u64, 3),
++    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
-     (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
++    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
-     (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
++    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
-     (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
++    (disallowed_methods: Vec<String> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
-     (upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive": bool, false),
++    (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
-     (cargo_ignore_publish, "cargo_ignore_publish": bool, false),
- }
- impl Default for Conf {
-     #[must_use]
-     fn default() -> Self {
-         toml::from_str("").expect("we never error on empty config files")
-     }
++    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
- /// Produces a `Conf` filled with the default values and forwards the errors
- ///
- /// Used internally for convenience
- fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
-     (Conf::default(), errors)
- }
++    (cargo_ignore_publish: bool = false),
 +}
 +
 +/// 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 {
 +            let config_file = current.join(config_file_name);
 +            match fs::metadata(&config_file) {
 +                // Only return if it's a file to handle the unlikely situation of a directory named
 +                // `clippy.toml`.
 +                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
 +                // Return the error if it's something other than `NotFound`; otherwise we didn't
 +                // find the project file yet, and continue searching.
 +                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
 +                _ => {},
 +            }
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
- pub fn read(path: &Path) -> (Conf, Vec<Error>) {
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
-         Err(err) => return default(vec![err.into()]),
++pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
++        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
-     assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
-     match toml::from_str(&content) {
-         Ok(toml) => {
-             let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
-             let toml_ref: &Conf = &toml;
-             let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;
-             if cyc_field.is_some() {
-                 let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
-                 errors.push(Error::Toml(cyc_err));
-             }
-             (toml, errors)
-         },
-         Err(e) => {
-             let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
-             errors.push(Error::Toml(e.to_string()));
-             default(errors)
-         },
-     }
 +    };
++    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
index 3d3d0e19d26224190bdd8e0546ae7e31599fb9bf,0000000000000000000000000000000000000000..ee7be24eae8013f05680ef3b7056aae0a2c973a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1232 -1,0 +1,1235 @@@
 +use crate::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::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
 +    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::{
 +    BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, 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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// *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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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 !run_lints(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 !run_lints(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 !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
 +            let fn_name = path.ident;
 +            if let Some(sugg) = self.map.get(&*fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(&args[0]).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 !run_lints(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 !run_lints(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 !run_lints(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<'_>) {
 +        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 then_block = match then.kind {
 +            ExprKind::Block(block, _) => block,
 +            _ => return,
 +        };
 +        let if_chain_span = is_expn_of(expr.span, "if_chain");
 +        if !els {
 +            check_nested_if_chains(cx, expr, then_block, if_chain_span);
 +        }
 +        let if_chain_span = match if_chain_span {
 +            None => return,
 +            Some(span) => span,
 +        };
 +        // check for `if a && b;`
 +        if_chain! {
 +            if let Some(cond) = cond;
 +            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 matches!(tail.kind,
 +            ExprKind::If(_, _, None)
 +            | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false }));
 +        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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e85637ca758884abee452581dfea267e164c905a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,632 @@@
++//! 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)
++
++// # NITs
++// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames
++
++use if_chain::if_chain;
++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::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_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/metadata_collection.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
++const EXCLUDED_LINT_GROUPS: [&str; 1] = ["clippy::internal"];
++
++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"],
++];
++
++/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
++const APPLICABILITY_NAME_INDEX: usize = 2;
++
++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:** Collects 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, Default)]
++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>,
++}
++
++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 = applicability_info.remove(&x.id));
++
++        // 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,
++    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, docs: String) -> Self {
++        Self {
++            id,
++            id_span,
++            group,
++            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),
++            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 index = self.applicability.unwrap_or_default();
++
++        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
++        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
++        s.serialize_field(
++            "applicability",
++            &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
++        )?;
++        s.end()
++    }
++}
++
++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_chain! {
++            // item validation
++            if let ItemKind::Static(ref ty, Mutability::Not, _) = item.kind;
++            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) = get_lint_group_or_lint(cx, &lint_name, item);
++            if let Some(docs) = extract_attr_docs_or_lint(cx, item);
++            then {
++                self.lints.push(LintMetadata::new(
++                    lint_name,
++                    SerializableSpan::from_item(cx, item),
++                    group,
++                    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 it self.
++                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`
++fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
++    cx.tcx
++        .hir()
++        .attrs(item.hir_id())
++        .iter()
++        .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string()))
++        .reduce(|mut acc, sym| {
++            acc.push_str(&sym);
++            acc.push('\n');
++            acc
++        })
++}
++
++fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'hir Item<'_>) -> Option<String> {
++    let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
++    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
++        get_lint_group(cx, lint_lst[0])
++            .or_else(|| {
++                lint_collection_error_item(cx, item, "Unable to determine lint group");
++                None
++            })
++            .filter(|group| !EXCLUDED_LINT_GROUPS.contains(&group.as_str()))
++    } 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(|x| *x == lint_id) {
++            return Some((*group_name).to_string());
++        }
++    }
++
++    None
++}
++
++// ==================================================================
++// 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);
++            }
++        };
++
++        // TODO xFrednet 2021-03-01: support function arguments?
++
++        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 d8b31344e6d8bd68b91284c3a47dabfad3837b95,0000000000000000000000000000000000000000..b67448e3a57406be07c7589baec3e72f17a964b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,5 -1,0 +1,5 @@@
- #[cfg(feature = "internal-lints")]
 +pub mod author;
 +pub mod conf;
 +pub mod inspector;
++#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 +pub mod internal_lints;
index d04c5f889dda0f24a6f9ff408cc02002b1a098a9,0000000000000000000000000000000000000000..0a1d4e1114285b18640658a9eaa0c48bd359aa64
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,22 @@@
- version = "0.1.53"
 +[package]
 +name = "clippy_utils"
++version = "0.1.54"
 +authors = ["The Rust Clippy Developers"]
 +edition = "2018"
 +publish = false
 +
 +[dependencies]
 +if_chain = "1.0.0"
 +itertools = "0.9"
 +regex-syntax = "0.6"
 +serde = { version = "1.0", features = ["derive"] }
 +unicode-normalization = "0.1"
 +rustc-semver="1.1.0"
 +
 +[features]
 +internal-lints = []
++metadata-collector-lint = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 7f827f1759d2f9542a6fa53668b6aa7204f0507e,0000000000000000000000000000000000000000..a4efae54894fb10463ba54f745c594830604b047
mode 100644,000000..100644
--- /dev/null
@@@ -1,235 -1,0 +1,243 @@@
 +//! 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.rsplitn(2, '.').nth(1).unwrap())
 +                }),
 +                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
 +///
 +/// ```ignore
 +/// 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
 +///
 +/// ```ignore
 +/// 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: Span,
 +    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
 +///
 +/// ```ignore
 +/// 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 07ae6e924e28b7daee63fe73710532859c40b5e5,0000000000000000000000000000000000000000..3b01158acd91f972a634b652afeebd6e28a3f7cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,937 -1,0 +1,1000 @@@
-                     // TODO: arm.pat?
 +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::stable_hasher::{HashStable, StableHasher};
 +use rustc_hir::def::Res;
 +use rustc_hir::HirIdMap;
 +use rustc_hir::{
 +    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, 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::ich::StableHashingContextProvider;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::Symbol;
 +use std::hash::Hash;
 +
 +/// 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(ref l), &StmtKind::Local(ref 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(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref 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))
 +            },
 +        }
 +    }
 +
 +    #[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, ref le), &ExprKind::AddrOf(rb, r_mut, ref 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(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
 +                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
 +                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
 +            (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref 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(ref l), &ExprKind::Box(ref 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(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
 +            | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
 +                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
 +            },
 +            (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref 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(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
 +                self.eq_expr(la, ra) && self.eq_expr(li, ri)
 +            },
 +            (&ExprKind::If(ref lc, ref lt, ref le), &ExprKind::If(ref rc, ref rt, ref re)) => {
 +                self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
 +            (&ExprKind::Loop(ref lb, ref ll, ref lls, _), &ExprKind::Loop(ref 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(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref 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(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
 +                let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body));
 +                let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value);
 +                let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body));
 +                let rl = celcx.expr(&self.inner.cx.tcx.hir().body(rl_id.body).value);
 +
 +                self.eq_expr(le, re) && ll == rl
 +            },
 +            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
 +            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref 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, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref 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::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),
 +            _ => 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(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref 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(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref 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(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref 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, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref 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(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
 +            (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
 +                let cx = self.inner.cx;
 +                let eval_const =
 +                    |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
 +                self.eq_ty(lt, rt) && eval_const(ll_id.body) == eval_const(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(ref l), &TyKind::Tup(ref 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: StableHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: StableHasher::new(),
 +        }
 +    }
 +
 +    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(ref e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        match b.rules {
 +            BlockCheckMode::DefaultBlock => 0,
 +            BlockCheckMode::UnsafeBlock(_) => 1,
 +            BlockCheckMode::PushUnsafeBlock(_) => 2,
 +            BlockCheckMode::PopUnsafeBlock(_) => 3,
 +        }
 +        .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.is_some().hash(&mut self.s);
 +
 +        if let Some(e) = simple_const {
 +            return e.hash(&mut self.s);
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, ref e) => {
 +                match kind {
 +                    BorrowKind::Ref => 0,
 +                    BorrowKind::Raw => 1,
 +                }
 +                .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(ref l, ref r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, ref l, ref r) => {
 +                o.node
 +                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(ref b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, ref l, ref r) => {
 +                op.node
 +                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &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(ref j) = *j {
 +                    self.hash_expr(&*j);
 +                }
 +            },
 +            ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(ref fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(cap, _, eid, _, _) => {
 +                match cap {
 +                    CaptureBy::Value => 0,
 +                    CaptureBy::Ref => 1,
 +                }
 +                .hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
 +            },
 +            ExprKind::Field(ref e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(ref a, ref i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(ref 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::LlvmInlineAsm(..) | ExprKind::Err => {},
 +            ExprKind::Lit(ref l) => {
 +                l.node.hash(&mut self.s);
 +            },
 +            ExprKind::Loop(ref b, ref i, ..) => {
 +                self.hash_block(b);
 +                if let Some(i) = *i {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::If(ref cond, ref then, ref else_opt) => {
 +                let c: fn(_, _, _) -> _ = ExprKind::If;
 +                c.hash(&mut self.s);
 +                self.hash_expr(cond);
 +                self.hash_expr(&**then);
 +                if let Some(ref e) = *else_opt {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Match(ref e, arms, ref s) => {
 +                self.hash_expr(e);
 +
 +                for arm in arms {
-     pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
++                    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(ref 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(ref e, ref l_id) => {
 +                self.hash_expr(e);
 +                self.hash_body(l_id.body);
 +            },
 +            ExprKind::Ret(ref e) => {
 +                if let Some(ref e) = *e {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                self.hash_qpath(qpath);
 +            },
 +            ExprKind::Struct(ref 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(ref e) = *expr {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Tup(tup) => {
 +                self.hash_exprs(tup);
 +            },
 +            ExprKind::Array(v) => {
 +                self.hash_exprs(v);
 +            },
 +            ExprKind::Unary(lop, ref le) => {
 +                lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &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.as_str().hash(&mut self.s);
 +    }
 +
 +    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
 +        match *p {
 +            QPath::Resolved(_, ref path) => {
 +                self.hash_path(path);
 +            },
 +            QPath::TypeRelative(_, ref path) => {
 +                self.hash_name(path.ident.name);
 +            },
 +            QPath::LangItem(lang_item, ..) => {
 +                lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &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) => {
++                ann.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &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);
++                }
++                i.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++            },
++            PatKind::Ref(pat, m) => {
++                self.hash_pat(pat);
++                m.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);
 +                }
 +            },
 +        }
 +    }
 +
 +    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(ref 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(ref expr) | Guard::IfLet(_, ref expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
-         self.hash_tykind(&ty.kind);
-     }
-     pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
-         std::mem::discriminant(ty).hash(&mut self.s);
-         match ty {
++    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<'_>) {
-             TyKind::Ptr(mut_ty) => {
++        std::mem::discriminant(&ty.kind).hash(&mut self.s);
++        match ty.kind {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            TyKind::Array(ty, anon_const) => {
 +                self.hash_ty(ty);
 +                self.hash_body(anon_const.body);
 +            },
-             TyKind::Rptr(lifetime, mut_ty) => {
++            TyKind::Ptr(ref mut_ty) => {
 +                self.hash_ty(&mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
-                 for ty in *ty_list {
++            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);
 +                }
 +                match bfn.decl.output {
 +                    FnRetTy::DefaultReturn(_) => {
 +                        ().hash(&mut self.s);
 +                    },
 +                    FnRetTy::Return(ref ty) => {
 +                        self.hash_ty(ty);
 +                    },
 +                }
 +                bfn.decl.c_variadic.hash(&mut self.s);
 +            },
 +            TyKind::Tup(ty_list) => {
-             TyKind::Path(qpath) => match qpath {
++                for ty in ty_list {
 +                    self.hash_ty(ty);
 +                }
 +            },
-             match arg {
-                 GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
-                 GenericArg::Type(ref ty) => self.hash_ty(&ty),
++            TyKind::Path(ref qpath) => match qpath {
 +                QPath::Resolved(ref maybe_ty, ref path) => {
 +                    if let Some(ref ty) = maybe_ty {
 +                        self.hash_ty(ty);
 +                    }
 +                    for segment in path.segments {
 +                        segment.ident.name.hash(&mut self.s);
 +                        self.hash_generic_args(segment.args().args);
 +                    }
 +                },
 +                QPath::TypeRelative(ref ty, ref segment) => {
 +                    self.hash_ty(ty);
 +                    segment.ident.name.hash(&mut self.s);
 +                },
 +                QPath::LangItem(lang_item, ..) => {
 +                    lang_item.hash(&mut self.s);
 +                },
 +            },
 +            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),
 +            }
 +        }
 +    }
 +}
index e81a92eb74ca7c74afd2bb56aed669ec8a9cadb1,0000000000000000000000000000000000000000..f5ee49c7d5f99f31f3cc4d912451fd99c8eabc0c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1609 -1,0 +1,1649 @@@
- use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
 +#![feature(box_patterns)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![cfg_attr(bootstrap, feature(or_patterns))]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +
 +// 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_ast::ast::{self, Attribute, BorrowKind, LitKind};
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-     ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
-     QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
++use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 +use rustc_hir::LangItem::{ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
-     match get_parent_node(cx.tcx, e.hir_id) {
++    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
++    PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 +};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::exports::Export;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::ty as rustc_ty;
 +use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
 +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};
 +use crate::ty::{can_partially_move_ty, 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
 +    }
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> 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 an expression references a variable of the given name.
 +pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
 +        if let [p] = path.segments {
 +            return p.ident.name == var;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, ref 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(_, ref path) => path.segments.get(0),
 +        QPath::TypeRelative(_, ref 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(_, ref path) => match_path(path, segments),
 +        QPath::TypeRelative(ref ty, ref 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.
 +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))
 +}
 +
 +/// 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, ref 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),
 +        _ => 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
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if jump_targets.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(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)) =>
 +        {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        }
 +        _ => true,
 +    }
 +}
 +
 +/// 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 {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        loops: Vec<HirId>,
 +        allow_closure: 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.allow_closure {
 +                return;
 +            }
 +            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);
 +            }
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +    };
 +    v.visit_expr(expr);
 +    v.allow_closure
 +}
 +
 +/// 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(ref path, _, ref 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(LOCAL_CRATE)
 +        .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,
 +    }
 +}
 +
 +/// Gets the name of a `Pat`, if any.
 +pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
 +    match pat.kind {
 +        PatKind::Binding(.., ref spname, _) => Some(spname.name),
 +        PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
 +        PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
 +        _ => 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::new(line_start, span.hi(), span.ctxt())
 +}
 +
 +/// 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(ref block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => 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((arm_id, Node::Arm(..))) => matches!(
 +            iter.next(),
 +            Some((
 +                _,
 +                Node::Expr(Expr {
 +                    kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }),
 +                    ..
 +                })
 +            ))
 +            if else_arm.hir_id == arm_id
 +        ),
 +        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 map = cx.tcx.hir();
 +    let parent_item = map.get_parent_item(e.hir_id);
 +    if let Some((Constant::Int(v), _)) = map
 +        .maybe_body_owned_by(parent_item)
 +        .and_then(|body_id| constant(cx, cx.tcx.typeck_body(body_id), 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(ref 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: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut i: I) -> bool {
 +        i.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(ref pat) | PatKind::Ref(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(ref pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats.iter().map(|pat| &**pat))
 +        },
 +        PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
 +        PatKind::Struct(ref qpath, ref fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, ref pats, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
 +        },
 +        PatKind::Slice(ref head, ref middle, ref 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()).map(|pat| &**pat))
 +                },
 +                _ => {
 +                    // 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().copied().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(ref 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, ref 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, ref 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(_, ref 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_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(ref fun, ref 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.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +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 ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
 +        conds.push(&**cond);
 +        if let ExprKind::Block(ref block, _) = then_expr.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(ref else_expr) = *else_expr {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(ref 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(ref 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())
 +}
 +
 +/// 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() { 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,
 +    }
 +}
 +
 +/// This function checks if any of the lints in the slice is enabled for the provided `HirId`.
 +/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn`
 +///
 +/// ```ignore
 +/// #[deny(clippy::YOUR_AWESOME_LINT)]
 +/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true
 +///
 +/// #[allow(clippy::YOUR_AWESOME_LINT)]
 +/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false
 +/// ```
 +pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
 +    lints.iter().any(|lint| {
 +        matches!(
 +            cx.tcx.lint_level_at_node(lint, id),
 +            (Level::Forbid | Level::Deny | Level::Warn, _)
 +        )
 +    })
 +}
 +
 +/// Returns Option<String> where String is a textual representation of the type encapsulated in the
 +/// slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and None otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk().last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
 +        return vec![(&exprs[0], &exprs[1])];
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: FxHashMap<_, Vec<&_>> =
 +        FxHashMap::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) 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(BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +#[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
 +        }
 +    }
 +}
index 1fa439639b24aebab5a0a4467496538faf4500dc,0000000000000000000000000000000000000000..8037d670500be736973135845d9592dc8424742f
mode 100644,000000..100644
--- /dev/null
@@@ -1,183 -1,0 +1,194 @@@
- #[cfg(feature = "internal-lints")]
 +//! 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"];
 +pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
 +pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 +pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
 +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: [&str; 3] = ["core", "default", "Default"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
 +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 FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
 +pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
 +pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
 +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: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
 +#[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 INTO: [&str; 3] = ["core", "convert", "Into"];
 +pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
 +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"];
 +#[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 LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
++#[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"];
 +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; 4] = ["core", "intrinsics", "", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
 +pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
 +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"];
 +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 TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
 +pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
index 53180d1f9f54f699014b50c35f0d1e1eff33b9cc,0000000000000000000000000000000000000000..4d49b43bde9ecd9751bc825b55afad614e5f1fd7
mode 100644,000000..100644
--- /dev/null
@@@ -1,429 -1,0 +1,460 @@@
-     let outer_span = hygiene::walk_chain(span, outer);
-     let (span, is_macro_call) = if outer_span.ctxt() == outer {
-         (outer_span, span.ctxt() != outer)
-     } else {
-         // The span is from a macro argument, and the outer context is the macro using the argument
-         if *applicability != Applicability::Unspecified {
-             *applicability = Applicability::MaybeIncorrect;
-         }
-         // TODO: get the argument span.
-         (span, false)
-     };
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use crate::line_span;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_span::hygiene;
 +use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 +use std::borrow::Cow;
 +
 +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
 +/// Also takes an `Option<String>` which can be put inside the braces.
 +pub fn expr_block<'a, T: LintContext>(
 +    cx: &T,
 +    expr: &Expr<'_>,
 +    option: Option<String>,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let code = snippet_block(cx, expr.span, default, indent_relative_to);
 +    let string = option.unwrap_or_default();
 +    if expr.span.from_expansion() {
 +        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
 +    } else if let ExprKind::Block(_, _) = expr.kind {
 +        Cow::Owned(format!("{}{}", code, string))
 +    } else if string.is_empty() {
 +        Cow::Owned(format!("{{ {} }}", code))
 +    } else {
 +        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
 +    }
 +}
 +
 +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
 +/// line.
 +///
 +/// ```rust,ignore
 +///     let x = ();
 +/// //          ^^
 +/// // will be converted to
 +///     let x = ();
 +/// //  ^^^^^^^^^^
 +/// ```
 +pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 +}
 +
 +fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
 +    let line_span = line_span(cx, span);
 +    snippet_opt(cx, line_span).and_then(|snip| {
 +        snip.find(|c: char| !c.is_whitespace())
 +            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
 +    })
 +}
 +
 +/// Returns the indentation of the line of a span
 +///
 +/// ```rust,ignore
 +/// let x = ();
 +/// //      ^^ -- will return 0
 +///     let x = ();
 +/// //          ^^ -- will return 4
 +/// ```
 +pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
 +    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 +}
 +
 +/// Gets a snippet of the indentation of the line of a span
 +pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
 +        let len = s.len() - s.trim_start().len();
 +        s.truncate(len);
 +        s
 +    })
 +}
 +
 +// If the snippet is empty, it's an attribute that was inserted during macro
 +// expansion and we want to ignore those, because they could come from external
 +// sources that the user has no control over.
 +// For some reason these attributes don't have any expansion info on them, so
 +// we have to check it this way until there is a better way.
 +pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
 +    if let Some(snippet) = snippet_opt(cx, span) {
 +        if snippet.is_empty() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +/// Returns the positon just before rarrow
 +///
 +/// ```rust,ignore
 +/// fn into(self) -> () {}
 +///              ^
 +/// // in case of unformatted code
 +/// fn into2(self)-> () {}
 +///               ^
 +/// fn into3(self)   -> () {}
 +///               ^
 +/// ```
 +pub fn position_before_rarrow(s: &str) -> Option<usize> {
 +    s.rfind("->").map(|rpos| {
 +        let mut rpos = rpos;
 +        let chars: Vec<char> = s.chars().collect();
 +        while rpos > 1 {
 +            if let Some(c) = chars.get(rpos - 1) {
 +                if c.is_whitespace() {
 +                    rpos -= 1;
 +                    continue;
 +                }
 +            }
 +            break;
 +        }
 +        rpos
 +    })
 +}
 +
 +/// Reindent a multiline string with possibility of ignoring the first line.
 +#[allow(clippy::needless_pass_by_value)]
 +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
 +    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
 +    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
 +    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
 +}
 +
 +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
 +    let x = s
 +        .lines()
 +        .skip(ignore_first as usize)
 +        .filter_map(|l| {
 +            if l.is_empty() {
 +                None
 +            } else {
 +                // ignore empty lines
 +                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
 +            }
 +        })
 +        .min()
 +        .unwrap_or(0);
 +    let indent = indent.unwrap_or(0);
 +    s.lines()
 +        .enumerate()
 +        .map(|(i, l)| {
 +            if (ignore_first && i == 0) || l.is_empty() {
 +                l.to_owned()
 +            } else if x > indent {
 +                l.split_at(x - indent).1.to_owned()
 +            } else {
 +                " ".repeat(indent - x) + l
 +            }
 +        })
 +        .collect::<Vec<String>>()
 +        .join("\n")
 +}
 +
 +/// Converts a span to a code snippet if available, otherwise use default.
 +///
 +/// This is useful if you want to provide suggestions for your lint or more generally, if you want
 +/// to convert a given `Span` to a `str`.
 +///
 +/// # Example
 +/// ```rust,ignore
 +/// snippet(cx, expr.span, "..")
 +/// ```
 +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
 +}
 +
 +/// Same as `snippet`, but it adapts the applicability level by following rules:
 +///
 +/// - Applicability level `Unspecified` will never be changed.
 +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
 +/// `HasPlaceholders`
 +pub fn snippet_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    if *applicability != Applicability::Unspecified && span.from_expansion() {
 +        *applicability = Applicability::MaybeIncorrect;
 +    }
 +    snippet_opt(cx, span).map_or_else(
 +        || {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Cow::Borrowed(default)
 +        },
 +        From::from,
 +    )
 +}
 +
 +/// Same as `snippet`, but should only be used when it's clear that the input span is
 +/// not a macro argument.
 +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet(cx, span.source_callsite(), default)
 +}
 +
 +/// Converts a span to a code snippet. Returns `None` if not available.
 +pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    cx.sess().source_map().span_to_snippet(span).ok()
 +}
 +
 +/// Converts a span (from a block) to a code snippet if available, otherwise use default.
 +///
 +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
 +/// things which need to be printed as such.
 +///
 +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
 +/// resulting snippet of the given span.
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", None)
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///     y;
 +/// }
 +/// ```
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", Some(if_expr.span))
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///         y;
 +///     } // aligned with `if`
 +/// ```
 +/// Note that the first line of the snippet always has 0 indentation.
 +pub fn snippet_block<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let snip = snippet(cx, span, default);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_block`, but adapts the applicability level by the rules of
 +/// `snippet_with_applicability`.
 +pub fn snippet_block_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    let snip = snippet_with_applicability(cx, span, default, applicability);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
 +/// will result in the macro call, rather then the expansion, if the span is from a child context.
 +/// If the span is not from a child context, it will be used directly instead.
 +///
 +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 +/// would result in `box []`. If given the context of the address of expression, this function will
 +/// correctly get a snippet of `vec![]`.
 +///
 +/// This will also return whether or not the snippet is a macro call.
 +pub fn snippet_with_context(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    outer: SyntaxContext,
 +    default: &'a str,
 +    applicability: &mut Applicability,
 +) -> (Cow<'a, str>, bool) {
++    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
++        || {
++            // The span is from a macro argument, and the outer context is the macro using the argument
++            if *applicability != Applicability::Unspecified {
++                *applicability = Applicability::MaybeIncorrect;
++            }
++            // TODO: get the argument span.
++            (span, false)
++        },
++        |outer_span| (outer_span, span.ctxt() != outer),
++    );
 +
 +    (
 +        snippet_with_applicability(cx, span, default, applicability),
 +        is_macro_call,
 +    )
 +}
 +
++/// Walks the span up to the target context, thereby returning the macro call site if the span is
++/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
++/// case of the span being in a macro expansion, but the target context is from expanding a macro
++/// argument.
++///
++/// Given the following
++///
++/// ```rust,ignore
++/// macro_rules! m { ($e:expr) => { f($e) }; }
++/// g(m!(0))
++/// ```
++///
++/// If called with a span of the call to `f` and a context of the call to `g` this will return a
++/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
++/// containing `0` as the context is the same as the outer context.
++///
++/// This will traverse through multiple macro calls. Given the following:
++///
++/// ```rust,ignore
++/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
++/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
++/// g(m!(0))
++/// ```
++///
++/// If called with a span of the call to `f` and a context of the call to `g` this will return a
++/// span containing `m!(0)`.
++pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
++    let outer_span = hygiene::walk_chain(span, outer);
++    (outer_span.ctxt() == outer).then(|| outer_span)
++}
++
 +/// Removes block comments from the given `Vec` of lines.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// without_block_comments(vec!["/*", "foo", "*/"]);
 +/// // => vec![]
 +///
 +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
 +/// // => vec!["bar"]
 +/// ```
 +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
 +    let mut without = vec![];
 +
 +    let mut nest_level = 0;
 +
 +    for line in lines {
 +        if line.contains("/*") {
 +            nest_level += 1;
 +            continue;
 +        } else if line.contains("*/") {
 +            nest_level -= 1;
 +            continue;
 +        }
 +
 +        if nest_level == 0 {
 +            without.push(line);
 +        }
 +    }
 +
 +    without
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::{reindent_multiline, without_block_comments};
 +
 +    #[test]
 +    fn test_reindent_multiline_single_line() {
 +        assert_eq!("", reindent_multiline("".into(), false, None));
 +        assert_eq!("...", reindent_multiline("...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_block() {
 +        assert_eq!("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +        } else {
 +            z
 +        }".into(), false, None));
 +        assert_eq!("\
 +    if x {
 +    \ty
 +    } else {
 +    \tz
 +    }", reindent_multiline("    if x {
 +        \ty
 +        } else {
 +        \tz
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_empty_line() {
 +        assert_eq!("\
 +    if x {
 +        y
 +
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +
 +        } else {
 +            z
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_lines_deeper() {
 +        assert_eq!("\
 +        if x {
 +            y
 +        } else {
 +            z
 +        }", reindent_multiline("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }".into(), true, Some(8)));
 +    }
 +
 +    #[test]
 +    fn test_without_block_comments_lines_without_block_comments() {
 +        let result = without_block_comments(vec!["/*", "", "*/"]);
 +        println!("result: {:?}", result);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
 +        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
 +
 +        let result = without_block_comments(vec!["/* rust", "", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* one-line comment */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["foo", "bar", "baz"]);
 +        assert_eq!(result, vec!["foo", "bar", "baz"]);
 +    }
 +}
index 5a8c629e3338c41683de26a237968a33bb1cda8b,0000000000000000000000000000000000000000..d431bdf34eeeea2c62f0662f8ca562df16e35ac2
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,241 @@@
- use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
- use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
 +use crate::path_to_local_id;
 +use rustc_hir as hir;
++use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
++use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +
 +/// 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
 +    }
 +}
 +
 +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)
 +    }
 +}
++
++pub trait Visitable<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V);
++}
++impl Visitable<'tcx> for &'tcx Expr<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
++        v.visit_expr(self)
++    }
++}
++impl Visitable<'tcx> for &'tcx Block<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
++        v.visit_block(self)
++    }
++}
++impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
++        v.visit_stmt(self)
++    }
++}
++impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
++        v.visit_body(self)
++    }
++}
++impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> {
++    fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
++        v.visit_arm(self)
++    }
++}
++
++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));
++}
index d6cc6d0c2c76394842dd80d085e811bf96b78725,0000000000000000000000000000000000000000..5a06afedbf4c2e7b86afff20847a30456b7a6d41
mode 100644,000000..100644
--- /dev/null
@@@ -1,670 -1,0 +1,670 @@@
-     (msrv, "msrv": Option<String>, None),
 +# 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)
 +  - [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?**
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **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
-     (configuration_ident, "configuration_value": Type, DefaultValue),
++    (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.
 +    ///
 +    /// **Known problems:** None. (Or describe where it could go wrong.)
 +    ///
 +    /// **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:
 +
 +1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
 +    like this:
 +    ```rust
 +    /// Lint: LINT_NAME. <The configuration field doc comment>
++    (configuration_ident: Type = DefaultValue),
 +    ```
 +    The configuration value and identifier should usually be the same. 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 cd398451783d6bc53b03596e5b7a55956c9a19d3,0000000000000000000000000000000000000000..593162f09a788ca221c0abda08d1fa589ff44a88
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2021-04-22"
 +[toolchain]
++channel = "nightly-2021-05-06"
 +components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 750a23e8c9841f4dcf29670cecb2b8f6e047b182,0000000000000000000000000000000000000000..0c82f37d6a22e32572c15956bdf6ecd7e0c32d80
mode 100644,000000..100644
--- /dev/null
@@@ -1,349 -1,0 +1,349 @@@
-             let conf = clippy_lints::read_conf(&[], sess);
 +#![feature(rustc_private)]
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_interface;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +
 +use rustc_interface::interface;
 +use rustc_session::parse::ParseSess;
 +use rustc_span::symbol::Symbol;
 +use rustc_tools_util::VersionInfo;
 +
 +use std::borrow::Cow;
 +use std::env;
 +use std::lazy::SyncLazy;
 +use std::ops::Deref;
 +use std::panic;
 +use std::path::{Path, PathBuf};
 +use std::process::{exit, Command};
 +
 +/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
 +/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
 +fn arg_value<'a, T: Deref<Target = str>>(
 +    args: &'a [T],
 +    find_arg: &str,
 +    pred: impl Fn(&str) -> bool,
 +) -> Option<&'a str> {
 +    let mut args = args.iter().map(Deref::deref);
 +    while let Some(arg) = args.next() {
 +        let mut arg = arg.splitn(2, '=');
 +        if arg.next() != Some(find_arg) {
 +            continue;
 +        }
 +
 +        match arg.next().or_else(|| args.next()) {
 +            Some(v) if pred(v) => return Some(v),
 +            _ => {},
 +        }
 +    }
 +    None
 +}
 +
 +#[test]
 +fn test_arg_value() {
 +    let args = &["--bar=bar", "--foobar", "123", "--foo"];
 +
 +    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
 +    assert_eq!(arg_value(args, "--bar", |_| false), None);
 +    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
 +    assert_eq!(arg_value(args, "--foo", |_| true), None);
 +}
 +
 +fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>) {
 +    parse_sess.env_depinfo.get_mut().insert((
 +        Symbol::intern("CLIPPY_ARGS"),
 +        args_env_var.as_deref().map(Symbol::intern),
 +    ));
 +}
 +
 +struct DefaultCallbacks;
 +impl rustc_driver::Callbacks for DefaultCallbacks {}
 +
 +/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of
 +/// `CLIPPY_ARGS` environment variable.
 +struct RustcCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for RustcCallbacks {
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +        }));
 +    }
 +}
 +
 +struct ClippyCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for ClippyCallbacks {
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let previous = config.register_lints.take();
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +        }));
 +        config.register_lints = Some(Box::new(move |sess, lint_store| {
 +            // technically we're ~guaranteed that this is none but might as well call anything that
 +            // is there already. Certainly it can't hurt.
 +            if let Some(previous) = &previous {
 +                (previous)(sess, lint_store);
 +            }
 +
++            let conf = clippy_lints::read_conf(sess);
 +            clippy_lints::register_plugins(lint_store, sess, &conf);
 +            clippy_lints::register_pre_expansion_lints(lint_store);
 +            clippy_lints::register_renamed(lint_store);
 +        }));
 +
 +        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
 +        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
 +        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
 +        // use for Clippy.
 +        config.opts.debugging_opts.mir_opt_level = Some(0);
 +    }
 +}
 +
 +fn display_help() {
 +    println!(
 +        "\
 +Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    -h, --help               Print this message
 +        --rustc              Pass all args to rustc
 +    -V, --version            Print version info and exit
 +
 +Other options are the same as `cargo check`.
 +
 +To allow or deny a lint from the command line you can use `cargo clippy --`
 +with:
 +
 +    -W --warn OPT       Set lint warnings
 +    -A --allow OPT      Set lint allowed
 +    -D --deny OPT       Set lint denied
 +    -F --forbid OPT     Set lint forbidden
 +
 +You can use tool lints to allow or deny lints from your code, eg.:
 +
 +    #[allow(clippy::needless_lifetimes)]
 +"
 +    );
 +}
 +
 +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
 +
 +static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
 +    let hook = panic::take_hook();
 +    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
 +    hook
 +});
 +
 +fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
 +    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
 +    (*ICE_HOOK)(info);
 +
 +    // Separate the output with an empty line
 +    eprintln!();
 +
 +    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
 +        rustc_errors::ColorConfig::Auto,
 +        None,
 +        false,
 +        false,
 +        None,
 +        false,
 +    ));
 +    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
 +
 +    // a .span_bug or .bug call has already printed what
 +    // it wants to print.
 +    if !info.payload().is::<rustc_errors::ExplicitBug>() {
 +        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
 +        handler.emit_diagnostic(&d);
 +    }
 +
 +    let version_info = rustc_tools_util::get_version_info!();
 +
 +    let xs: Vec<Cow<'static, str>> = vec![
 +        "the compiler unexpectedly panicked. this is a bug.".into(),
 +        format!("we would appreciate a bug report: {}", bug_report_url).into(),
 +        format!("Clippy version: {}", version_info).into(),
 +    ];
 +
 +    for note in &xs {
 +        handler.note_without_error(note);
 +    }
 +
 +    // If backtraces are enabled, also print the query stack
 +    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
 +
 +    let num_frames = if backtrace { None } else { Some(2) };
 +
 +    interface::try_print_query_stack(&handler, num_frames);
 +}
 +
 +fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
 +    home.and_then(|home| {
 +        toolchain.map(|toolchain| {
 +            let mut path = PathBuf::from(home);
 +            path.push("toolchains");
 +            path.push(toolchain);
 +            path
 +        })
 +    })
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +pub fn main() {
 +    rustc_driver::init_rustc_env_logger();
 +    SyncLazy::force(&ICE_HOOK);
 +    exit(rustc_driver::catch_with_exit_code(move || {
 +        let mut orig_args: Vec<String> = env::args().collect();
 +
 +        // Get the sysroot, looking from most specific to this invocation to the least:
 +        // - command line
 +        // - runtime environment
 +        //    - SYSROOT
 +        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
 +        // - sysroot from rustc in the path
 +        // - compile-time environment
 +        //    - SYSROOT
 +        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
 +        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
 +        let have_sys_root_arg = sys_root_arg.is_some();
 +        let sys_root = sys_root_arg
 +            .map(PathBuf::from)
 +            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
 +            .or_else(|| {
 +                let home = std::env::var("RUSTUP_HOME")
 +                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
 +                    .ok();
 +                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
 +                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
 +                    .ok();
 +                toolchain_path(home, toolchain)
 +            })
 +            .or_else(|| {
 +                Command::new("rustc")
 +                    .arg("--print")
 +                    .arg("sysroot")
 +                    .output()
 +                    .ok()
 +                    .and_then(|out| String::from_utf8(out.stdout).ok())
 +                    .map(|s| PathBuf::from(s.trim()))
 +            })
 +            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
 +            .or_else(|| {
 +                let home = option_env!("RUSTUP_HOME")
 +                    .or(option_env!("MULTIRUST_HOME"))
 +                    .map(ToString::to_string);
 +                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
 +                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
 +                    .map(ToString::to_string);
 +                toolchain_path(home, toolchain)
 +            })
 +            .map(|pb| pb.to_string_lossy().to_string())
 +            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
 +
 +        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
 +        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
 +        // uses
 +        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
 +            orig_args.remove(pos);
 +            orig_args[0] = "rustc".to_string();
 +
 +            // if we call "rustc", we need to pass --sysroot here as well
 +            let mut args: Vec<String> = orig_args.clone();
 +            if !have_sys_root_arg {
 +                args.extend(vec!["--sysroot".into(), sys_root]);
 +            };
 +
 +            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
 +        }
 +
 +        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
 +            let version_info = rustc_tools_util::get_version_info!();
 +            println!("{}", version_info);
 +            exit(0);
 +        }
 +
 +        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
 +        // We're invoking the compiler programmatically, so we ignore this/
 +        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
 +
 +        if wrapper_mode {
 +            // we still want to be able to invoke it normally though
 +            orig_args.remove(1);
 +        }
 +
 +        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
 +            display_help();
 +            exit(0);
 +        }
 +
 +        // this conditional check for the --sysroot flag is there so users can call
 +        // `clippy_driver` directly
 +        // without having to pass --sysroot or anything
 +        let mut args: Vec<String> = orig_args.clone();
 +        if !have_sys_root_arg {
 +            args.extend(vec!["--sysroot".into(), sys_root]);
 +        };
 +
 +        let mut no_deps = false;
 +        let clippy_args_var = env::var("CLIPPY_ARGS").ok();
 +        let clippy_args = clippy_args_var
 +            .as_deref()
 +            .unwrap_or_default()
 +            .split("__CLIPPY_HACKERY__")
 +            .filter_map(|s| match s {
 +                "" => None,
 +                "--no-deps" => {
 +                    no_deps = true;
 +                    None
 +                },
 +                _ => Some(s.to_string()),
 +            })
 +            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
 +            .collect::<Vec<String>>();
 +
 +        // We enable Clippy if one of the following conditions is met
 +        // - IF Clippy is run on its test suite OR
 +        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
 +        //    - IF `--no-deps` is not set (`!no_deps`) OR
 +        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
 +        let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
 +        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
 +        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
 +
 +        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
 +        if clippy_enabled {
 +            args.extend(clippy_args);
 +        }
 +
 +        if clippy_enabled {
 +            rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
 +        } else {
 +            rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
 +        }
 +    }))
 +}
index d92530f073f56285738fcc93d9c4eaef779691d0,0000000000000000000000000000000000000000..6524fd4706ce13f37694cff7407c8515cfeec45c
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,203 @@@
++//! 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)]
 +
 +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 enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string());
 +
 +    let mut command = Command::new(&*CLIPPY_PATH);
 +    command
 +        .current_dir(root_dir)
 +        .env("CLIPPY_DOGFOOD", "1")
 +        .env("CARGO_INCREMENTAL", "0")
++        .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection)
 +        .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("--")
 +        .arg("--no-deps")
 +        .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;
 +    }
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    // NOTE: `path_dep` crate is omitted on purpose here
 +    for d 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",
 +    ] {
 +        let mut command = Command::new(&*CLIPPY_PATH);
 +        command
 +            .current_dir(root_dir.join(d))
 +            .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());
 +    }
 +
 +    // 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();
 +}
index efd02bcbb6e28968bdb1e227e74a4190289c1110,0000000000000000000000000000000000000000..c7bc261de6c5a3f8c10af3ffc10ae0a47df5d579
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,4 @@@
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names`
 +
 +error: aborting due to previous error
 +
index 34267c0daf7c2cfd50fc647b6cd3f2b891e32856,0000000000000000000000000000000000000000..90021a034a3d3b358ae1eb472f733904ee8649d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,4 @@@
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
 +
 +error: aborting due to previous error
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69b8b6a0e68c3af97530ff3316c953e96c2d4efc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::builtin_type_shadow)]
++#![allow(non_camel_case_types)]
++
++fn foo<u32>(a: u32) -> u32 {
++    42
++    // ^ rustc's type error
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47a8a1e623e8aa0592b2abb24c8f4e88373543c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: this generic shadows the built-in type `u32`
++  --> $DIR/builtin_type_shadow.rs:4:8
++   |
++LL | fn foo<u32>(a: u32) -> u32 {
++   |        ^^^
++   |
++   = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
++
++error[E0308]: mismatched types
++  --> $DIR/builtin_type_shadow.rs:5:5
++   |
++LL | fn foo<u32>(a: u32) -> u32 {
++   |        ---             --- expected `u32` because of return type
++   |        |
++   |        this type parameter
++LL |     42
++   |     ^^ expected type parameter `u32`, found integer
++   |
++   = note: expected type parameter `u32`
++                        found type `{integer}`
++
++error: aborting due to 2 previous errors
++
++For more information about this error, try `rustc --explain E0308`.
index 3b03f8c7dfe7c71f6642646d2543a719ce3cbb4f,0000000000000000000000000000000000000000..c12c6a310275db9c77d39849235c02058c5ee4bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,234 @@@
 +#![allow(dead_code)]
 +#![warn(clippy::comparison_chain)]
 +
 +fn a() {}
 +fn b() {}
 +fn c() {}
 +
 +fn f(x: u8, y: u8, z: u8) {
 +    // Ignored: Only one branch
 +    if x > y {
 +        a()
 +    }
 +
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    }
 +
 +    // Ignored: Only one explicit conditional
 +    if x > y {
 +        a()
 +    } else {
 +        b()
 +    }
 +
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    if x > y {
 +        a()
 +    } else if y > x {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    if x > 1 {
 +        a()
 +    } else if x < 1 {
 +        b()
 +    } else if x == 1 {
 +        c()
 +    }
 +
 +    // Ignored: Binop args are not equivalent
 +    if x > 1 {
 +        a()
 +    } else if y > 1 {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    // Ignored: Binop args are not equivalent
 +    if x > y {
 +        a()
 +    } else if x > z {
 +        b()
 +    } else if y > z {
 +        c()
 +    }
 +
 +    // Ignored: Not binary comparisons
 +    if true {
 +        a()
 +    } else if false {
 +        b()
 +    } else {
 +        c()
 +    }
 +}
 +
 +#[allow(clippy::float_cmp)]
 +fn g(x: f64, y: f64, z: f64) {
 +    // Ignored: f64 doesn't implement Ord
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    }
 +
 +    // Ignored: f64 doesn't implement Ord
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    // Ignored: f64 doesn't implement Ord
 +    if x > y {
 +        a()
 +    } else if y > x {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    // Ignored: f64 doesn't implement Ord
 +    if x > 1.0 {
 +        a()
 +    } else if x < 1.0 {
 +        b()
 +    } else if x == 1.0 {
 +        c()
 +    }
 +}
 +
 +fn h<T: Ord>(x: T, y: T, z: T) {
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    }
 +
 +    if x > y {
 +        a()
 +    } else if x < y {
 +        b()
 +    } else {
 +        c()
 +    }
 +
 +    if x > y {
 +        a()
 +    } else if y > x {
 +        b()
 +    } else {
 +        c()
 +    }
 +}
 +
 +// The following uses should be ignored
 +mod issue_5212 {
 +    use super::{a, b, c};
 +    fn foo() -> u8 {
 +        21
 +    }
 +
 +    fn same_operation_equals() {
 +        // operands are fixed
 +
 +        if foo() == 42 {
 +            a()
 +        } else if foo() == 42 {
 +            b()
 +        }
 +
 +        if foo() == 42 {
 +            a()
 +        } else if foo() == 42 {
 +            b()
 +        } else {
 +            c()
 +        }
 +
 +        // operands are transposed
 +
 +        if foo() == 42 {
 +            a()
 +        } else if 42 == foo() {
 +            b()
 +        }
 +    }
 +
 +    fn same_operation_not_equals() {
 +        // operands are fixed
 +
 +        if foo() > 42 {
 +            a()
 +        } else if foo() > 42 {
 +            b()
 +        }
 +
 +        if foo() > 42 {
 +            a()
 +        } else if foo() > 42 {
 +            b()
 +        } else {
 +            c()
 +        }
 +
 +        if foo() < 42 {
 +            a()
 +        } else if foo() < 42 {
 +            b()
 +        }
 +
 +        if foo() < 42 {
 +            a()
 +        } else if foo() < 42 {
 +            b()
 +        } else {
 +            c()
 +        }
 +    }
 +}
 +
++enum Sign {
++    Negative,
++    Positive,
++    Zero,
++}
++
++impl Sign {
++    const fn sign_i8(n: i8) -> Self {
++        if n == 0 {
++            Sign::Zero
++        } else if n > 0 {
++            Sign::Positive
++        } else {
++            Sign::Negative
++        }
++    }
++}
++
++const fn sign_i8(n: i8) -> Sign {
++    if n == 0 {
++        Sign::Zero
++    } else if n > 0 {
++        Sign::Positive
++    } else {
++        Sign::Negative
++    }
++}
++
 +fn main() {}
index e3d5928be231b3d18ef3e0509c779b8ee948db74,0000000000000000000000000000000000000000..ae67ebded437496e0d3e40c23c21a490e2b2901a
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,21 @@@
-     let a: Vec<_> = my_iterator.take(1).collect();
-     assert_eq!(a.len(), 1);
-     let b: Vec<_> = my_iterator.collect();
-     assert_eq!(b.len(), 5);
 +#![warn(clippy::copy_iterator)]
 +
 +#[derive(Copy, Clone)]
 +struct Countdown(u8);
 +
 +impl Iterator for Countdown {
 +    type Item = u8;
 +
 +    fn next(&mut self) -> Option<u8> {
 +        self.0.checked_sub(1).map(|c| {
 +            self.0 = c;
 +            c
 +        })
 +    }
 +}
 +
 +fn main() {
 +    let my_iterator = Countdown(5);
++    assert_eq!(my_iterator.take(1).count(), 1);
++    assert_eq!(my_iterator.count(), 5);
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82095febc194aefc5ca5237007bc26f762bee6ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#[derive(Default)]
++struct A<T> {
++    a: Vec<A<T>>,
++    b: T,
++}
++
++fn main() {
++    if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a9cd32380a19700ab44dc1a67c89934c8f1c19a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/ice-7169.rs:8:12
++   |
++LL |     if let Ok(_) = Ok::<_, ()>(A::<String>::default()) {}
++   |     -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::<String>::default()).is_ok()`
++   |
++   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
++
++error: aborting due to previous error
++
index d806bc6d40102e6f1efef7166d9f2ad22be0fc15,0000000000000000000000000000000000000000..d742856bc4163af0999ad145af09e9a6d6dc942d
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,115 @@@
++// edition:2018
++
 +#[warn(clippy::eval_order_dependence)]
 +#[allow(
 +    unused_assignments,
 +    unused_variables,
 +    clippy::many_single_char_names,
 +    clippy::no_effect,
 +    dead_code,
 +    clippy::blacklisted_name
 +)]
 +fn main() {
 +    let mut x = 0;
 +    let a = {
 +        x = 1;
 +        1
 +    } + x;
 +
 +    // Example from iss#277
 +    x += {
 +        x = 20;
 +        2
 +    };
 +
 +    // Does it work in weird places?
 +    // ...in the base for a struct expression?
 +    struct Foo {
 +        a: i32,
 +        b: i32,
 +    };
 +    let base = Foo { a: 4, b: 5 };
 +    let foo = Foo {
 +        a: x,
 +        ..{
 +            x = 6;
 +            base
 +        }
 +    };
 +    // ...inside a closure?
 +    let closure = || {
 +        let mut x = 0;
 +        x += {
 +            x = 20;
 +            2
 +        };
 +    };
 +    // ...not across a closure?
 +    let mut y = 0;
 +    let b = (y, || y = 1);
 +
 +    // && and || evaluate left-to-right.
 +    let a = {
 +        x = 1;
 +        true
 +    } && (x == 3);
 +    let a = {
 +        x = 1;
 +        true
 +    } || (x == 3);
 +
 +    // Make sure we don't get confused by alpha conversion.
 +    let a = {
 +        let mut x = 1;
 +        x = 2;
 +        1
 +    } + x;
 +
 +    // No warning if we don't read the variable...
 +    x = {
 +        x = 20;
 +        2
 +    };
 +    // ...if the assignment is in a closure...
 +    let b = {
 +        || {
 +            x = 1;
 +        };
 +        1
 +    } + x;
 +    // ... or the access is under an address.
 +    let b = (
 +        {
 +            let p = &x;
 +            1
 +        },
 +        {
 +            x = 1;
 +            x
 +        },
 +    );
 +
 +    // Limitation: l-values other than simple variables don't trigger
 +    // the warning.
 +    let mut tup = (0, 0);
 +    let c = {
 +        tup.0 = 1;
 +        1
 +    } + tup.0;
 +    // Limitation: you can get away with a read under address-of.
 +    let mut z = 0;
 +    let b = (
 +        &{
 +            z = x;
 +            x
 +        },
 +        {
 +            x = 3;
 +            x
 +        },
 +    );
 +}
++
++async fn issue_6925() {
++    let _ = vec![async { true }.await, async { false }.await];
++}
index 8f4fa2228f7f4d7f7eff5594a51a23ef28116900,0000000000000000000000000000000000000000..35eb85e95a320bf72497178054bf1a1a312bc20c
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
- error: unsequenced read of a variable
-   --> $DIR/eval_order_dependence.rs:15:9
++error: unsequenced read of `x`
++  --> $DIR/eval_order_dependence.rs:17:9
 +   |
 +LL |     } + x;
 +   |         ^
 +   |
 +   = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
 +note: whether read occurs before this write depends on evaluation order
-   --> $DIR/eval_order_dependence.rs:13:9
++  --> $DIR/eval_order_dependence.rs:15:9
 +   |
 +LL |         x = 1;
 +   |         ^^^^^
 +
- error: unsequenced read of a variable
-   --> $DIR/eval_order_dependence.rs:18:5
++error: unsequenced read of `x`
++  --> $DIR/eval_order_dependence.rs:20:5
 +   |
 +LL |     x += {
 +   |     ^
 +   |
 +note: whether read occurs before this write depends on evaluation order
-   --> $DIR/eval_order_dependence.rs:19:9
++  --> $DIR/eval_order_dependence.rs:21:9
 +   |
 +LL |         x = 20;
 +   |         ^^^^^^
 +
- error: unsequenced read of a variable
-   --> $DIR/eval_order_dependence.rs:31:12
++error: unsequenced read of `x`
++  --> $DIR/eval_order_dependence.rs:33:12
 +   |
 +LL |         a: x,
 +   |            ^
 +   |
 +note: whether read occurs before this write depends on evaluation order
-   --> $DIR/eval_order_dependence.rs:33:13
++  --> $DIR/eval_order_dependence.rs:35:13
 +   |
 +LL |             x = 6;
 +   |             ^^^^^
 +
- error: unsequenced read of a variable
-   --> $DIR/eval_order_dependence.rs:40:9
++error: unsequenced read of `x`
++  --> $DIR/eval_order_dependence.rs:42:9
 +   |
 +LL |         x += {
 +   |         ^
 +   |
 +note: whether read occurs before this write depends on evaluation order
-   --> $DIR/eval_order_dependence.rs:41:13
++  --> $DIR/eval_order_dependence.rs:43:13
 +   |
 +LL |             x = 20;
 +   |             ^^^^^^
 +
 +error: aborting due to 4 previous errors
 +
index 59f7ad9c1062490f66039c203abba078c53fbbbe,0000000000000000000000000000000000000000..7698b88a88c8aa80a143ac27ab11e145a880ef51
mode 100644,000000..100644
--- /dev/null
@@@ -1,97 -1,0 +1,131 @@@
- #![allow(clippy::needless_return, unused)]
++// edition:2018
 +// run-rustfix
 +
 +#![warn(clippy::implicit_return)]
- #[allow(clippy::needless_bool)]
++#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +
 +    return true
 +}
 +
- #[allow(clippy::needless_return)]
 +fn test_if_block() -> bool {
 +    if true { return true } else { return false }
 +}
 +
 +#[rustfmt::skip]
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => { return true },
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_match_with_unreachable(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => unreachable!(),
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_loop() -> bool {
 +    loop {
 +        return true;
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_loop_with_block() -> bool {
 +    loop {
 +        {
 +            return true;
 +        }
 +    }
 +}
 +
- fn main() {
-     let _ = test_end_of_fn();
-     let _ = test_if_block();
-     let _ = test_match(true);
-     let _ = test_match_with_unreachable(true);
-     let _ = test_loop();
-     let _ = test_loop_with_block();
-     let _ = test_loop_with_nests();
-     let _ = test_loop_with_if_let();
-     test_closure();
-     let _ = test_return_macro();
 +fn test_loop_with_nests() -> bool {
 +    loop {
 +        if true {
 +            return true;
 +        } else {
 +            let _ = true;
 +        }
 +    }
 +}
 +
 +#[allow(clippy::redundant_pattern_matching)]
 +fn test_loop_with_if_let() -> bool {
 +    loop {
 +        if let Some(x) = Some(true) {
 +            return x;
 +        }
 +    }
 +}
 +
 +fn test_closure() {
 +    #[rustfmt::skip]
 +    let _ = || { return true };
 +    let _ = || return true;
 +}
 +
 +fn test_panic() -> bool {
 +    panic!()
 +}
 +
 +fn test_return_macro() -> String {
 +    return format!("test {}", "test")
 +}
 +
++fn macro_branch_test() -> bool {
++    macro_rules! m {
++        ($t:expr, $f:expr) => {
++            if true { $t } else { $f }
++        };
++    }
++    return m!(true, false)
++}
++
++fn loop_test() -> bool {
++    'outer: loop {
++        if true {
++            return true;
++        }
++
++        let _ = loop {
++            if false {
++                return false;
++            }
++            if true {
++                break true;
++            }
++        };
++    }
 +}
++
++fn loop_macro_test() -> bool {
++    macro_rules! m {
++        ($e:expr) => {
++            break $e
++        };
++    }
++    return loop {
++        m!(true);
++    }
++}
++
++fn divergent_test() -> bool {
++    fn diverge() -> ! {
++        panic!()
++    }
++    diverge()
++}
++
++// issue #6940
++async fn foo() -> bool {
++    return true
++}
++
++fn main() {}
index 2c1bc046515089a7ea73f93969df248168a7164b,0000000000000000000000000000000000000000..45bbc2ec670e05463d1a0f8d6f8d5fc557561cfe
mode 100644,000000..100644
--- /dev/null
@@@ -1,97 -1,0 +1,131 @@@
- #![allow(clippy::needless_return, unused)]
++// edition:2018
 +// run-rustfix
 +
 +#![warn(clippy::implicit_return)]
- #[allow(clippy::needless_bool)]
++#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +
 +    true
 +}
 +
- #[allow(clippy::needless_return)]
 +fn test_if_block() -> bool {
 +    if true { true } else { false }
 +}
 +
 +#[rustfmt::skip]
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => { true },
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_match_with_unreachable(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => unreachable!(),
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_loop() -> bool {
 +    loop {
 +        break true;
 +    }
 +}
 +
- #[allow(clippy::never_loop)]
 +fn test_loop_with_block() -> bool {
 +    loop {
 +        {
 +            break true;
 +        }
 +    }
 +}
 +
- fn main() {
-     let _ = test_end_of_fn();
-     let _ = test_if_block();
-     let _ = test_match(true);
-     let _ = test_match_with_unreachable(true);
-     let _ = test_loop();
-     let _ = test_loop_with_block();
-     let _ = test_loop_with_nests();
-     let _ = test_loop_with_if_let();
-     test_closure();
-     let _ = test_return_macro();
 +fn test_loop_with_nests() -> bool {
 +    loop {
 +        if true {
 +            break true;
 +        } else {
 +            let _ = true;
 +        }
 +    }
 +}
 +
 +#[allow(clippy::redundant_pattern_matching)]
 +fn test_loop_with_if_let() -> bool {
 +    loop {
 +        if let Some(x) = Some(true) {
 +            return x;
 +        }
 +    }
 +}
 +
 +fn test_closure() {
 +    #[rustfmt::skip]
 +    let _ = || { true };
 +    let _ = || true;
 +}
 +
 +fn test_panic() -> bool {
 +    panic!()
 +}
 +
 +fn test_return_macro() -> String {
 +    format!("test {}", "test")
 +}
 +
++fn macro_branch_test() -> bool {
++    macro_rules! m {
++        ($t:expr, $f:expr) => {
++            if true { $t } else { $f }
++        };
++    }
++    m!(true, false)
++}
++
++fn loop_test() -> bool {
++    'outer: loop {
++        if true {
++            break true;
++        }
++
++        let _ = loop {
++            if false {
++                break 'outer false;
++            }
++            if true {
++                break true;
++            }
++        };
++    }
++}
++
++fn loop_macro_test() -> bool {
++    macro_rules! m {
++        ($e:expr) => {
++            break $e
++        };
++    }
++    loop {
++        m!(true);
++    }
++}
++
++fn divergent_test() -> bool {
++    fn diverge() -> ! {
++        panic!()
++    }
++    diverge()
 +}
++
++// issue #6940
++async fn foo() -> bool {
++    true
++}
++
++fn main() {}
index 3608319e5bd2cc5e770cac96b0fcad9c839f4c6c,0000000000000000000000000000000000000000..16fe9ed444ff696d20c0c3411cb56913a631a6d2
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,109 @@@
-   --> $DIR/implicit_return.rs:12:5
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:39:9
++  --> $DIR/implicit_return.rs:13:5
 +   |
 +LL |     true
 +   |     ^^^^ help: add `return` as shown: `return true`
 +   |
 +   = note: `-D clippy::implicit-return` implied by `-D warnings`
 +
 +error: missing `return` statement
 +  --> $DIR/implicit_return.rs:17:15
 +   |
 +LL |     if true { true } else { false }
 +   |               ^^^^ help: add `return` as shown: `return true`
 +
 +error: missing `return` statement
 +  --> $DIR/implicit_return.rs:17:29
 +   |
 +LL |     if true { true } else { false }
 +   |                             ^^^^^ help: add `return` as shown: `return false`
 +
 +error: missing `return` statement
 +  --> $DIR/implicit_return.rs:23:17
 +   |
 +LL |         true => false,
 +   |                 ^^^^^ help: add `return` as shown: `return false`
 +
 +error: missing `return` statement
 +  --> $DIR/implicit_return.rs:24:20
 +   |
 +LL |         false => { true },
 +   |                    ^^^^ help: add `return` as shown: `return true`
 +
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:47:13
++  --> $DIR/implicit_return.rs:37:9
 +   |
 +LL |         break true;
 +   |         ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 +
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:56:13
++  --> $DIR/implicit_return.rs:44:13
 +   |
 +LL |             break true;
 +   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 +
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:74:18
++  --> $DIR/implicit_return.rs:52:13
 +   |
 +LL |             break true;
 +   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
 +
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:75:16
++  --> $DIR/implicit_return.rs:70:18
 +   |
 +LL |     let _ = || { true };
 +   |                  ^^^^ help: add `return` as shown: `return true`
 +
 +error: missing `return` statement
-   --> $DIR/implicit_return.rs:83:5
++  --> $DIR/implicit_return.rs:71:16
 +   |
 +LL |     let _ = || true;
 +   |                ^^^^ help: add `return` as shown: `return true`
 +
 +error: missing `return` statement
- error: aborting due to 11 previous errors
++  --> $DIR/implicit_return.rs:79:5
 +   |
 +LL |     format!("test {}", "test")
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
 +
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:88:5
++   |
++LL |     m!(true, false)
++   |     ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:94:13
++   |
++LL |             break true;
++   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:99:17
++   |
++LL |                 break 'outer false;
++   |                 ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:114:5
++   |
++LL | /     loop {
++LL | |         m!(true);
++LL | |     }
++   | |_____^
++   |
++help: add `return` as shown
++   |
++LL |     return loop {
++LL |         m!(true);
++LL |     }
++   |
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:128:5
++   |
++LL |     true
++   |     ^^^^ help: add `return` as shown: `return true`
++
++error: aborting due to 16 previous errors
 +
index 72591f12baf852811efbc511a1ee7cf965929fe7,0000000000000000000000000000000000000000..3d8fb8507e515c470a8ff1d2a59d5002b7ef3fd1
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,218 @@@
 +fn fn_val(i: i32) -> i32 {
 +    unimplemented!()
 +}
 +fn fn_constref(i: &i32) -> i32 {
 +    unimplemented!()
 +}
 +fn fn_mutref(i: &mut i32) {
 +    unimplemented!()
 +}
 +fn fooi() -> i32 {
 +    unimplemented!()
 +}
 +fn foob() -> bool {
 +    unimplemented!()
 +}
 +
 +#[allow(clippy::many_single_char_names)]
 +fn immutable_condition() {
 +    // Should warn when all vars mentioned are immutable
 +    let y = 0;
 +    while y < 10 {
 +        println!("KO - y is immutable");
 +    }
 +
 +    let x = 0;
 +    while y < 10 && x < 3 {
 +        let mut k = 1;
 +        k += 2;
 +        println!("KO - x and y immutable");
 +    }
 +
 +    let cond = false;
 +    while !cond {
 +        println!("KO - cond immutable");
 +    }
 +
 +    let mut i = 0;
 +    while y < 10 && i < 3 {
 +        i += 1;
 +        println!("OK - i is mutable");
 +    }
 +
 +    let mut mut_cond = false;
 +    while !mut_cond || cond {
 +        mut_cond = true;
 +        println!("OK - mut_cond is mutable");
 +    }
 +
 +    while fooi() < x {
 +        println!("OK - Fn call results may vary");
 +    }
 +
 +    while foob() {
 +        println!("OK - Fn call results may vary");
 +    }
 +
 +    let mut a = 0;
 +    let mut c = move || {
 +        while a < 5 {
 +            a += 1;
 +            println!("OK - a is mutable");
 +        }
 +    };
 +    c();
 +
 +    let mut tup = (0, 0);
 +    while tup.0 < 5 {
 +        tup.0 += 1;
 +        println!("OK - tup.0 gets mutated")
 +    }
 +}
 +
 +fn unused_var() {
 +    // Should warn when a (mutable) var is not used in while body
 +    let (mut i, mut j) = (0, 0);
 +
 +    while i < 3 {
 +        j = 3;
 +        println!("KO - i not mentioned");
 +    }
 +
 +    while i < 3 && j > 0 {
 +        println!("KO - i and j not mentioned");
 +    }
 +
 +    while i < 3 {
 +        let mut i = 5;
 +        fn_mutref(&mut i);
 +        println!("KO - shadowed");
 +    }
 +
 +    while i < 3 && j > 0 {
 +        i = 5;
 +        println!("OK - i in cond and mentioned");
 +    }
 +}
 +
 +fn used_immutable() {
 +    let mut i = 0;
 +
 +    while i < 3 {
 +        fn_constref(&i);
 +        println!("KO - const reference");
 +    }
 +
 +    while i < 3 {
 +        fn_val(i);
 +        println!("KO - passed by value");
 +    }
 +
 +    while i < 3 {
 +        println!("OK - passed by mutable reference");
 +        fn_mutref(&mut i)
 +    }
 +
 +    while i < 3 {
 +        fn_mutref(&mut i);
 +        println!("OK - passed by mutable reference");
 +    }
 +}
 +
 +const N: i32 = 5;
 +const B: bool = false;
 +
 +fn consts() {
 +    while false {
 +        println!("Constants are not linted");
 +    }
 +
 +    while B {
 +        println!("Constants are not linted");
 +    }
 +
 +    while N > 0 {
 +        println!("Constants are not linted");
 +    }
 +}
 +
 +use std::cell::Cell;
 +
 +fn maybe_i_mutate(i: &Cell<bool>) {
 +    unimplemented!()
 +}
 +
 +fn internally_mutable() {
 +    let b = Cell::new(true);
 +
 +    while b.get() {
 +        // b cannot be silently coerced to `bool`
 +        maybe_i_mutate(&b);
 +        println!("OK - Method call within condition");
 +    }
 +}
 +
 +struct Counter {
 +    count: usize,
 +}
 +
 +impl Counter {
 +    fn inc(&mut self) {
 +        self.count += 1;
 +    }
 +
 +    fn inc_n(&mut self, n: usize) {
 +        while self.count < n {
 +            self.inc();
 +        }
 +        println!("OK - self borrowed mutably");
 +    }
 +
 +    fn print_n(&self, n: usize) {
 +        while self.count < n {
 +            println!("KO - {} is not mutated", self.count);
 +        }
 +    }
 +}
 +
 +fn while_loop_with_break_and_return() {
 +    let y = 0;
 +    while y < 10 {
 +        if y == 0 {
 +            break;
 +        }
 +        println!("KO - loop contains break");
 +    }
 +
 +    while y < 10 {
 +        if y == 0 {
 +            return;
 +        }
 +        println!("KO - loop contains return");
 +    }
 +}
 +
++fn immutable_condition_false_positive(mut n: u64) -> u32 {
++    let mut count = 0;
++    while {
++        n >>= 1;
++        n != 0
++    } {
++        count += 1;
++    }
++    count
++}
++
 +fn main() {
 +    immutable_condition();
 +    unused_var();
 +    used_immutable();
 +    internally_mutable();
++    immutable_condition_false_positive(5);
 +
 +    let mut c = Counter { count: 0 };
 +    c.inc_n(5);
 +    c.print_n(2);
 +
 +    while_loop_with_break_and_return();
 +}
index 0918a6868ab4b08c4958f4975209012c24faa09f,0000000000000000000000000000000000000000..2458bf1e490bbf9c672de2987677c9157ed9b0a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,77 @@@
- use std::collections::{HashMap, VecDeque};
++use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque};
 +
 +fn main() {
 +    let sample = [1; 5];
 +    let indirect_iter = sample.iter().collect::<Vec<_>>();
 +    indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +    let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +    indirect_len.len();
 +    let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +    indirect_empty.is_empty();
 +    let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +    indirect_contains.contains(&&5);
 +    let indirect_negative = sample.iter().collect::<Vec<_>>();
 +    indirect_negative.len();
 +    indirect_negative
 +        .into_iter()
 +        .map(|x| (*x, *x + 1))
 +        .collect::<HashMap<_, _>>();
 +
 +    // #6202
 +    let a = "a".to_string();
 +    let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
 +    let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +    non_copy_contains.contains(&a);
 +
 +    // Fix #5991
 +    let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 +    let vec_b = vec_a.iter().collect::<Vec<_>>();
 +    if vec_b.len() > 3 {}
 +    let other_vec = vec![1, 3, 12, 4, 16, 2];
 +    let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::<Vec<_>>();
 +
 +    // Fix #6297
 +    let sample = [1; 5];
 +    let multiple_indirect = sample.iter().collect::<Vec<_>>();
 +    let sample2 = vec![2, 3];
 +    if multiple_indirect.is_empty() {
 +        // do something
 +    } else {
 +        let found = sample2
 +            .iter()
 +            .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0))
 +            .collect::<Vec<_>>();
 +    }
 +}
++
++mod issue7110 {
++    // #7110 - lint for type annotation cases
++    use super::*;
++
++    fn lint_vec(string: &str) -> usize {
++        let buffer: Vec<&str> = string.split('/').collect();
++        buffer.len()
++    }
++    fn lint_vec_deque() -> usize {
++        let sample = [1; 5];
++        let indirect_len: VecDeque<_> = sample.iter().collect();
++        indirect_len.len()
++    }
++    fn lint_linked_list() -> usize {
++        let sample = [1; 5];
++        let indirect_len: LinkedList<_> = sample.iter().collect();
++        indirect_len.len()
++    }
++    fn lint_binary_heap() -> usize {
++        let sample = [1; 5];
++        let indirect_len: BinaryHeap<_> = sample.iter().collect();
++        indirect_len.len()
++    }
++    fn dont_lint(string: &str) -> usize {
++        let buffer: Vec<&str> = string.split('/').collect();
++        for buff in &buffer {
++            println!("{}", buff);
++        }
++        buffer.len()
++    }
++}
index c773b841f3b4b711c5c1f1321edd13452ecfe733,0000000000000000000000000000000000000000..f094e182a48f31eb1cb9e945581f7cf32613829d
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,129 @@@
- error: aborting due to 5 previous errors
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:5:39
 +   |
 +LL |     let indirect_iter = sample.iter().collect::<Vec<_>>();
 +   |                                       ^^^^^^^
 +LL |     indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +   = note: `-D clippy::needless-collect` implied by `-D warnings`
 +help: use the original Iterator instead of collecting it and then producing a new one
 +   |
 +LL |     
 +LL |     sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:7:38
 +   |
 +LL |     let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +   |                                      ^^^^^^^
 +LL |     indirect_len.len();
 +   |     ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL |     
 +LL |     sample.iter().count();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:9:40
 +   |
 +LL |     let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +   |                                        ^^^^^^^
 +LL |     indirect_empty.is_empty();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator has anything instead of collecting it and seeing if it's empty
 +   |
 +LL |     
 +LL |     sample.iter().next().is_none();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:11:43
 +   |
 +LL |     let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +   |                                           ^^^^^^^
 +LL |     indirect_contains.contains(&&5);
 +   |     ------------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL |     
 +LL |     sample.iter().any(|x| x == &5);
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:23:48
 +   |
 +LL |     let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +   |                                                ^^^^^^^
 +LL |     non_copy_contains.contains(&a);
 +   |     ------------------------------ the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL |     
 +LL |     sample.into_iter().any(|x| x == a);
 +   |
 +
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:52:51
++   |
++LL |         let buffer: Vec<&str> = string.split('/').collect();
++   |                                                   ^^^^^^^
++LL |         buffer.len()
++   |         ------------ the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL |         
++LL |         string.split('/').count()
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:57:55
++   |
++LL |         let indirect_len: VecDeque<_> = sample.iter().collect();
++   |                                                       ^^^^^^^
++LL |         indirect_len.len()
++   |         ------------------ the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL |         
++LL |         sample.iter().count()
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:62:57
++   |
++LL |         let indirect_len: LinkedList<_> = sample.iter().collect();
++   |                                                         ^^^^^^^
++LL |         indirect_len.len()
++   |         ------------------ the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL |         
++LL |         sample.iter().count()
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:67:57
++   |
++LL |         let indirect_len: BinaryHeap<_> = sample.iter().collect();
++   |                                                         ^^^^^^^
++LL |         indirect_len.len()
++   |         ------------------ the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL |         
++LL |         sample.iter().count()
++   |
++
++error: aborting due to 9 previous errors
 +
index d8b5f19e144dcfd0567610bc8ef6302109125eb3,0000000000000000000000000000000000000000..fcbe9af9f5616c177b860960e5276fd2e735b86e
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,59 @@@
 +// run-rustfix
 +
 +#![allow(unused_must_use)]
 +
 +use std::collections::HashSet;
 +
 +fn main() {
 +    let x = "foo";
 +    x.split('x');
 +    x.split("xx");
 +    x.split('x');
 +
 +    let y = "x";
 +    x.split(y);
 +    x.split('ß');
 +    x.split('ℝ');
 +    x.split('💣');
 +    // Can't use this lint for unicode code points which don't fit in a char
 +    x.split("❤️");
 +    x.contains('x');
 +    x.starts_with('x');
 +    x.ends_with('x');
 +    x.find('x');
 +    x.rfind('x');
 +    x.rsplit('x');
 +    x.split_terminator('x');
 +    x.rsplit_terminator('x');
 +    x.splitn(0, 'x');
 +    x.rsplitn(0, 'x');
 +    x.matches('x');
 +    x.rmatches('x');
 +    x.match_indices('x');
 +    x.rmatch_indices('x');
 +    x.trim_start_matches('x');
 +    x.trim_end_matches('x');
++    x.strip_prefix('x');
++    x.strip_suffix('x');
 +    // Make sure we escape characters correctly.
 +    x.split('\n');
 +    x.split('\'');
 +    x.split('\'');
 +
 +    let h = HashSet::<String>::new();
 +    h.contains("X"); // should not warn
 +
 +    x.replace(";", ",").split(','); // issue #2978
 +    x.starts_with('\x03'); // issue #2996
 +
 +    // Issue #3204
 +    const S: &str = "#";
 +    x.find(S);
 +
 +    // Raw string
 +    x.split('a');
 +    x.split('a');
 +    x.split('a');
 +    x.split('\'');
 +    x.split('#');
 +}
index a7bc73e3756dfb13e30925159b237321d296a62c,0000000000000000000000000000000000000000..b8bc20f4070fc4a91f797a8d3d6dc51c85223f6b
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,59 @@@
 +// run-rustfix
 +
 +#![allow(unused_must_use)]
 +
 +use std::collections::HashSet;
 +
 +fn main() {
 +    let x = "foo";
 +    x.split("x");
 +    x.split("xx");
 +    x.split('x');
 +
 +    let y = "x";
 +    x.split(y);
 +    x.split("ß");
 +    x.split("ℝ");
 +    x.split("💣");
 +    // Can't use this lint for unicode code points which don't fit in a char
 +    x.split("❤️");
 +    x.contains("x");
 +    x.starts_with("x");
 +    x.ends_with("x");
 +    x.find("x");
 +    x.rfind("x");
 +    x.rsplit("x");
 +    x.split_terminator("x");
 +    x.rsplit_terminator("x");
 +    x.splitn(0, "x");
 +    x.rsplitn(0, "x");
 +    x.matches("x");
 +    x.rmatches("x");
 +    x.match_indices("x");
 +    x.rmatch_indices("x");
 +    x.trim_start_matches("x");
 +    x.trim_end_matches("x");
++    x.strip_prefix("x");
++    x.strip_suffix("x");
 +    // Make sure we escape characters correctly.
 +    x.split("\n");
 +    x.split("'");
 +    x.split("\'");
 +
 +    let h = HashSet::<String>::new();
 +    h.contains("X"); // should not warn
 +
 +    x.replace(";", ",").split(","); // issue #2978
 +    x.starts_with("\x03"); // issue #2996
 +
 +    // Issue #3204
 +    const S: &str = "#";
 +    x.find(S);
 +
 +    // Raw string
 +    x.split(r"a");
 +    x.split(r#"a"#);
 +    x.split(r###"a"###);
 +    x.split(r###"'"###);
 +    x.split(r###"#"###);
 +}
index ee4e7e50efd17371a24a1eb904a8db47e4b12a27,0000000000000000000000000000000000000000..6d94d8a34e39095dfe8c4086e5c8e05929d7e0ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,196 @@@
-   --> $DIR/single_char_pattern.rs:37:13
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:9:13
 +   |
 +LL |     x.split("x");
 +   |             ^^^ help: try using a `char` instead: `'x'`
 +   |
 +   = note: `-D clippy::single-char-pattern` implied by `-D warnings`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:15:13
 +   |
 +LL |     x.split("ß");
 +   |             ^^^ help: try using a `char` instead: `'ß'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:16:13
 +   |
 +LL |     x.split("ℝ");
 +   |             ^^^ help: try using a `char` instead: `'ℝ'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:17:13
 +   |
 +LL |     x.split("💣");
 +   |             ^^^^ help: try using a `char` instead: `'💣'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:20:16
 +   |
 +LL |     x.contains("x");
 +   |                ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:21:19
 +   |
 +LL |     x.starts_with("x");
 +   |                   ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:22:17
 +   |
 +LL |     x.ends_with("x");
 +   |                 ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:23:12
 +   |
 +LL |     x.find("x");
 +   |            ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:24:13
 +   |
 +LL |     x.rfind("x");
 +   |             ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:25:14
 +   |
 +LL |     x.rsplit("x");
 +   |              ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:26:24
 +   |
 +LL |     x.split_terminator("x");
 +   |                        ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:27:25
 +   |
 +LL |     x.rsplit_terminator("x");
 +   |                         ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:28:17
 +   |
 +LL |     x.splitn(0, "x");
 +   |                 ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:29:18
 +   |
 +LL |     x.rsplitn(0, "x");
 +   |                  ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:30:15
 +   |
 +LL |     x.matches("x");
 +   |               ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:31:16
 +   |
 +LL |     x.rmatches("x");
 +   |                ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:32:21
 +   |
 +LL |     x.match_indices("x");
 +   |                     ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:33:22
 +   |
 +LL |     x.rmatch_indices("x");
 +   |                      ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:34:26
 +   |
 +LL |     x.trim_start_matches("x");
 +   |                          ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
 +  --> $DIR/single_char_pattern.rs:35:24
 +   |
 +LL |     x.trim_end_matches("x");
 +   |                        ^^^ help: try using a `char` instead: `'x'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:38:13
++  --> $DIR/single_char_pattern.rs:36:20
++   |
++LL |     x.strip_prefix("x");
++   |                    ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:37:20
++   |
++LL |     x.strip_suffix("x");
++   |                    ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:39:13
 +   |
 +LL |     x.split("/n");
 +   |             ^^^^ help: try using a `char` instead: `'/n'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:39:13
++  --> $DIR/single_char_pattern.rs:40:13
 +   |
 +LL |     x.split("'");
 +   |             ^^^ help: try using a `char` instead: `'/''`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:44:31
++  --> $DIR/single_char_pattern.rs:41:13
 +   |
 +LL |     x.split("/'");
 +   |             ^^^^ help: try using a `char` instead: `'/''`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:45:19
++  --> $DIR/single_char_pattern.rs:46:31
 +   |
 +LL |     x.replace(";", ",").split(","); // issue #2978
 +   |                               ^^^ help: try using a `char` instead: `','`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:52:13
++  --> $DIR/single_char_pattern.rs:47:19
 +   |
 +LL |     x.starts_with("/x03"); // issue #2996
 +   |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:53:13
++  --> $DIR/single_char_pattern.rs:54:13
 +   |
 +LL |     x.split(r"a");
 +   |             ^^^^ help: try using a `char` instead: `'a'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:54:13
++  --> $DIR/single_char_pattern.rs:55:13
 +   |
 +LL |     x.split(r#"a"#);
 +   |             ^^^^^^ help: try using a `char` instead: `'a'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:55:13
++  --> $DIR/single_char_pattern.rs:56:13
 +   |
 +LL |     x.split(r###"a"###);
 +   |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
 +
 +error: single-character string constant used as pattern
-   --> $DIR/single_char_pattern.rs:56:13
++  --> $DIR/single_char_pattern.rs:57:13
 +   |
 +LL |     x.split(r###"'"###);
 +   |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
 +
 +error: single-character string constant used as pattern
- error: aborting due to 30 previous errors
++  --> $DIR/single_char_pattern.rs:58:13
 +   |
 +LL |     x.split(r###"#"###);
 +   |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
 +
++error: aborting due to 32 previous errors
 +
index af858e4abcf5595f350a87d183bfbee5c4b6ae7d,0000000000000000000000000000000000000000..c58181f518d58e10fdc42e9267a14012c549a858
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,21 @@@
 +fn main() {
 +    let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
 +    let _ = (0..4).filter_map(|x| {
 +        if x > 1 {
 +            return Some(x);
 +        };
 +        None
 +    });
 +    let _ = (0..4).filter_map(|x| match x {
 +        0 | 1 => None,
 +        _ => Some(x),
 +    });
 +
 +    let _ = (0..4).filter_map(|x| Some(x + 1));
 +
 +    let _ = (0..4).filter_map(i32::checked_abs);
 +}
++
++fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> {
++    "".chars().filter_map(|_| None)
++}
index a192ebde3ebf4e73047dde8152c6dee234cf6bfc,0000000000000000000000000000000000000000..7bb43cf7ae82ded49dc035f3e03f798993547cc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,89 @@@
 +// run-rustfix
 +
 +// The output for humans should just highlight the whole span without showing
 +// the suggested replacement, but we also want to test that suggested
 +// replacement only removes one set of parentheses, rather than naïvely
 +// stripping away any starting or ending parenthesis characters—hence this
 +// test of the JSON error format.
 +
 +#![feature(custom_inner_attributes)]
 +#![rustfmt::skip]
 +
 +#![deny(clippy::unused_unit)]
 +#![allow(dead_code)]
 +#![allow(clippy::from_over_into)]
 +
 +struct Unitter;
 +impl Unitter {
 +    #[allow(clippy::no_effect)]
 +    pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G)
 +    where G: Fn() {
 +        let _y: &dyn Fn() = &f;
 +        (); // this should not lint, as it's not in return type position
 +    }
 +}
 +
 +impl Into<()> for Unitter {
 +    #[rustfmt::skip]
 +    fn into(self) {
 +        
 +    }
 +}
 +
 +trait Trait {
 +    fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut(),
 +        H: Fn();
 +}
 +
 +impl Trait for Unitter {
 +    fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut(),
 +        H: Fn() {}
 +}
 +
 +fn return_unit() {  }
 +
 +#[allow(clippy::needless_return)]
 +#[allow(clippy::never_loop)]
 +#[allow(clippy::unit_cmp)]
 +fn main() {
 +    let u = Unitter;
 +    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
 +    return_unit();
 +    loop {
 +        break;
 +    }
 +    return;
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/4076
 +fn foo() {
 +    macro_rules! foo {
 +        (recv($r:expr) -> $res:pat => $body:expr) => {
 +            $body
 +        }
 +    }
 +
 +    foo! {
 +        recv(rx) -> _x => ()
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn test(){}
 +
 +#[rustfmt::skip]
 +fn test2(){}
 +
 +#[rustfmt::skip]
 +fn test3(){}
++
++fn macro_expr() {
++    macro_rules! e {
++        () => (());
++    }
++    e!()
++}
index 96041a7dd850e3807504697a84b87026447a5955,0000000000000000000000000000000000000000..21073fb802adab910d8832a307472ef04cafd341
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,89 @@@
 +// run-rustfix
 +
 +// The output for humans should just highlight the whole span without showing
 +// the suggested replacement, but we also want to test that suggested
 +// replacement only removes one set of parentheses, rather than naïvely
 +// stripping away any starting or ending parenthesis characters—hence this
 +// test of the JSON error format.
 +
 +#![feature(custom_inner_attributes)]
 +#![rustfmt::skip]
 +
 +#![deny(clippy::unused_unit)]
 +#![allow(dead_code)]
 +#![allow(clippy::from_over_into)]
 +
 +struct Unitter;
 +impl Unitter {
 +    #[allow(clippy::no_effect)]
 +    pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
 +    where G: Fn() -> () {
 +        let _y: &dyn Fn() -> () = &f;
 +        (); // this should not lint, as it's not in return type position
 +    }
 +}
 +
 +impl Into<()> for Unitter {
 +    #[rustfmt::skip]
 +    fn into(self) -> () {
 +        ()
 +    }
 +}
 +
 +trait Trait {
 +    fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut() -> (),
 +        H: Fn() -> ();
 +}
 +
 +impl Trait for Unitter {
 +    fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut() -> (),
 +        H: Fn() -> () {}
 +}
 +
 +fn return_unit() -> () { () }
 +
 +#[allow(clippy::needless_return)]
 +#[allow(clippy::never_loop)]
 +#[allow(clippy::unit_cmp)]
 +fn main() {
 +    let u = Unitter;
 +    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
 +    return_unit();
 +    loop {
 +        break();
 +    }
 +    return();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/4076
 +fn foo() {
 +    macro_rules! foo {
 +        (recv($r:expr) -> $res:pat => $body:expr) => {
 +            $body
 +        }
 +    }
 +
 +    foo! {
 +        recv(rx) -> _x => ()
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn test()->(){}
 +
 +#[rustfmt::skip]
 +fn test2() ->(){}
 +
 +#[rustfmt::skip]
 +fn test3()-> (){}
++
++fn macro_expr() {
++    macro_rules! e {
++        () => (());
++    }
++    e!()
++}
index d0d9beb9b2d9fa686d907f650dfb55343d62feb0,0000000000000000000000000000000000000000..5707cf0ce0f8c08177f68866b7065c6c9feb2182
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,114 @@@
-     r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
 +# Common utils for the several housekeeping scripts.
 +
 +import os
 +import re
 +import collections
 +
 +import logging as log
 +log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s')
 +
 +Lint = collections.namedtuple('Lint', 'name level doc sourcefile group')
 +Config = collections.namedtuple('Config', 'name ty doc default')
 +
 +lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''')
 +group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
 +conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE)
 +confvar_re = re.compile(
++    r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE)
 +comment_re = re.compile(r'''\s*/// ?(.*)''')
 +
 +lint_levels = {
 +    "correctness": 'Deny',
 +    "style": 'Warn',
 +    "complexity": 'Warn',
 +    "perf": 'Warn',
 +    "restriction": 'Allow',
 +    "pedantic": 'Allow',
 +    "nursery": 'Allow',
 +    "cargo": 'Allow',
 +}
 +
 +
 +def parse_lints(lints, filepath):
 +    comment = []
 +    clippy = False
 +    deprecated = False
 +    name = ""
 +
 +    with open(filepath) as fp:
 +        for line in fp:
 +            if clippy or deprecated:
 +                m = lintname_re.search(line)
 +                if m:
 +                    name = m.group(1).lower()
 +                    line = next(fp)
 +
 +                    if deprecated:
 +                        level = "Deprecated"
 +                        group = "deprecated"
 +                    else:
 +                        while True:
 +                            g = group_re.search(line)
 +                            if g:
 +                                group = g.group(1).lower()
 +                                level = lint_levels.get(group, None)
 +                                break
 +                            line = next(fp)
 +
 +                    if level is None:
 +                        continue
 +
 +                    log.info("found %s with level %s in %s",
 +                             name, level, filepath)
 +                    lints.append(Lint(name, level, comment, filepath, group))
 +                    comment = []
 +
 +                    clippy = False
 +                    deprecated = False
 +                    name = ""
 +                else:
 +                    m = comment_re.search(line)
 +                    if m:
 +                        comment.append(m.group(1))
 +            elif line.startswith("declare_clippy_lint!"):
 +                clippy = True
 +                deprecated = False
 +            elif line.startswith("declare_deprecated_lint!"):
 +                clippy = False
 +                deprecated = True
 +            elif line.startswith("declare_lint!"):
 +                import sys
 +                print(
 +                    "don't use `declare_lint!` in Clippy, "
 +                    "use `declare_clippy_lint!` instead"
 +                )
 +                sys.exit(42)
 +
 +
 +def parse_configs(path):
 +    configs = {}
 +    with open(os.path.join(path, 'utils/conf.rs')) as fp:
 +        contents = fp.read()
 +
 +    match = re.search(conf_re, contents)
 +    confvars = re.findall(confvar_re, match.group(1))
 +
 +    for (lints, doc, name, ty, default) in confvars:
 +        for lint in lints.split(','):
 +            configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default)
 +    return configs
 +
 +
 +def parse_all(path="clippy_lints/src"):
 +    lints = []
 +    for root, dirs, files in os.walk(path):
 +        for fn in files:
 +            if fn.endswith('.rs'):
 +                parse_lints(lints, os.path.join(root, fn))
 +
 +    log.info("got %s lints", len(lints))
 +
 +    configs = parse_configs(path)
 +    log.info("got %d configs", len(configs))
 +
 +    return lints, configs