]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '61eb38aeda6cb54b93b872bf503d70084c4d621c' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 1 Jul 2021 16:17:38 +0000 (18:17 +0200)
committerflip1995 <philipp.krones@embecosm.com>
Thu, 1 Jul 2021 16:17:38 +0000 (18:17 +0200)
140 files changed:
1  2 
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/.github/workflows/remark.yml
src/tools/clippy/.remarkrc
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/Cargo.toml
src/tools/clippy/README.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/fmt.rs
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/setup/git_hook.rs
src/tools/clippy/clippy_dev/src/setup/intellij.rs
src/tools/clippy/clippy_dev/src/setup/mod.rs
src/tools/clippy/clippy_dev/src/setup/vscode.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/assign_ops.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/blacklisted_name.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/collapsible_match.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
src/tools/clippy/clippy_lints/src/deprecated_lints.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/disallowed_method.rs
src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
src/tools/clippy/clippy_lints/src/disallowed_type.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/get_last_with_len.rs
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/manual_map.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/append_instead_of_extend.rs
src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
src/tools/clippy/clippy_lints/src/methods/map_identity.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/useless_conversion.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/wildcard_imports.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/attrs.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/ptr.rs
src/tools/clippy/doc/basics.md
src/tools/clippy/doc/common_tools_writing_lints.md
src/tools/clippy/doc/release.md
src/tools/clippy/lintcheck/README.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/main.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml
src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/clippy.toml
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs
src/tools/clippy/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr
src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml
src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/append_instead_of_extend.fixed
src/tools/clippy/tests/ui/append_instead_of_extend.rs
src/tools/clippy/tests/ui/append_instead_of_extend.stderr
src/tools/clippy/tests/ui/assertions_on_constants.rs
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs
src/tools/clippy/tests/ui/blacklisted_name.rs
src/tools/clippy/tests/ui/default_numeric_fallback.rs
src/tools/clippy/tests/ui/default_numeric_fallback.stderr
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/disallowed_script_idents.rs
src/tools/clippy/tests/ui/disallowed_script_idents.stderr
src/tools/clippy/tests/ui/doc/doc.rs
src/tools/clippy/tests/ui/doc/doc.stderr
src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
src/tools/clippy/tests/ui/field_reassign_with_default.rs
src/tools/clippy/tests/ui/field_reassign_with_default.stderr
src/tools/clippy/tests/ui/filter_map_identity.fixed
src/tools/clippy/tests/ui/filter_map_identity.rs
src/tools/clippy/tests/ui/filter_map_identity.stderr
src/tools/clippy/tests/ui/flat_map_identity.fixed
src/tools/clippy/tests/ui/flat_map_identity.rs
src/tools/clippy/tests/ui/flat_map_identity.stderr
src/tools/clippy/tests/ui/match_same_arms.stderr
src/tools/clippy/tests/ui/match_same_arms2.stderr
src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed
src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs
src/tools/clippy/tests/ui/mut_key.stderr
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed
src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/tests/ui/vec.fixed
src/tools/clippy/tests/ui/vec.rs
src/tools/clippy/tests/ui/while_let_on_iterator.fixed
src/tools/clippy/tests/ui/while_let_on_iterator.rs
src/tools/clippy/tests/ui/while_let_on_iterator.stderr
src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed
src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs
src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr
src/tools/clippy/tests/ui/zero_offset.rs
src/tools/clippy/tests/ui/zero_offset.stderr
src/tools/clippy/util/etc/pre-commit.sh
src/tools/clippy/util/etc/vscode-tasks.json
src/tools/clippy/util/lintlib.py

index 32103f59d8b0e14d389e4b714c8fc419e9999ba9,0000000000000000000000000000000000000000..d856c55a41a4b8768b246009e43785a0b21ef6ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,87 @@@
-       run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
 +name: Clippy Test
 +
 +on:
 +  push:
 +    # Ignore bors branches, since they are covered by `clippy_bors.yml`
 +    branches-ignore:
 +      - auto
 +      - try
 +    # Don't run Clippy tests, when only textfiles were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +  pull_request:
 +    # Don't run Clippy tests, when only textfiles were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +
 +jobs:
 +  base:
 +    # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Set LD_LIBRARY_PATH (Linux)
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 +
 +    - name: Build
 +      run: cargo build --features deny-warnings,internal-lints
 +
 +    - name: Test
 +      run: cargo test --features deny-warnings,internal-lints
 +
 +    - name: Test clippy_lints
 +      run: cargo test --features deny-warnings,internal-lints
 +      working-directory: clippy_lints
 +
 +    - name: Test rustc_tools_util
 +      run: cargo test --features deny-warnings
 +      working-directory: rustc_tools_util
 +
 +    - name: Test clippy_dev
 +      run: cargo test --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test cargo-clippy
 +      run: ../target/debug/cargo-clippy
 +      working-directory: clippy_workspace_tests
 +
 +    - name: Test cargo-clippy --fix
++      run: ../target/debug/cargo-clippy clippy --fix
 +      working-directory: clippy_workspace_tests
 +
 +    - name: Test clippy-driver
 +      run: bash .github/driver.sh
 +      env:
 +        OS: ${{ runner.os }}
 +
 +    - name: Test cargo dev new lint
 +      run: |
 +        cargo dev new_lint --name new_early_pass --pass early
 +        cargo dev new_lint --name new_late_pass --pass late
 +        cargo check
 +        git reset --hard HEAD
index f27fee87dc1653f508555f90661143aa338a5e2c,0000000000000000000000000000000000000000..146b6fccd0c76e94962ca9260732ce849a535ee0
mode 100644,000000..100644
--- /dev/null
@@@ -1,273 -1,0 +1,268 @@@
-     # FIXME: should not be necessary once 1.24.2 is the default version on the windows runner
-     - name: Update rustup
-       run: rustup self update
-       if: runner.os == 'Windows'
 +name: Clippy Test (bors)
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
 +
 +defaults:
 +  run:
 +    shell: bash
 +
 +jobs:
 +  changelog:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +      with:
 +        ref: ${{ github.ref }}
 +
 +    # Run
 +    - name: Check Changelog
 +      run: |
 +        MESSAGE=$(git log --format=%B -n 1)
 +        PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
 +        body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
 +          python -c "import sys, json; print(json.load(sys.stdin)['body'])")
 +        output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
 +          echo "ERROR: PR body must contain 'changelog: ...'"
 +          exit 1
 +        }
 +        if [[ "$output" = "none" ]]; then
 +          echo "WARNING: changelog is 'none'"
 +        else
 +          echo "changelog: $output"
 +        fi
 +      env:
 +        PYTHONIOENCODING: 'utf-8'
 +  base:
 +    needs: changelog
 +    strategy:
 +      matrix:
 +        os: [ubuntu-latest, windows-latest, macos-latest]
 +        host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
 +        exclude:
 +        - os: ubuntu-latest
 +          host: x86_64-apple-darwin
 +        - os: ubuntu-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: macos-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: macos-latest
 +          host: i686-unknown-linux-gnu
 +        - os: macos-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: windows-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: windows-latest
 +          host: i686-unknown-linux-gnu
 +        - os: windows-latest
 +          host: x86_64-apple-darwin
 +
 +    runs-on: ${{ matrix.os }}
 +
 +    # NOTE: If you modify this job, make sure you copy the changes to clippy.yml
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Install dependencies (Linux-i686)
 +      run: |
 +        sudo dpkg --add-architecture i386
 +        sudo apt-get update
 +        sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
 +      if: matrix.host == 'i686-unknown-linux-gnu'
 +
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
-       run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Set LD_LIBRARY_PATH (Linux)
 +      if: runner.os == 'Linux'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 +    - name: Link rustc dylib (MacOS)
 +      if: runner.os == 'macOS'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        sudo mkdir -p /usr/local/lib
 +        sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
 +    - name: Set PATH (Windows)
 +      if: runner.os == 'Windows'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "$SYSROOT/bin" >> $GITHUB_PATH
 +
 +    - name: Build
 +      run: cargo build --features deny-warnings,internal-lints
 +
 +    - name: Test
 +      run: cargo test --features deny-warnings,internal-lints
 +
 +    - name: Test clippy_lints
 +      run: cargo test --features deny-warnings,internal-lints
 +      working-directory: clippy_lints
 +
 +    - name: Test rustc_tools_util
 +      run: cargo test --features deny-warnings
 +      working-directory: rustc_tools_util
 +
 +    - name: Test clippy_dev
 +      run: cargo test --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test cargo-clippy
 +      run: ../target/debug/cargo-clippy
 +      working-directory: clippy_workspace_tests
 +
 +    - name: Test cargo-clippy --fix
++      run: ../target/debug/cargo-clippy clippy --fix
 +      working-directory: clippy_workspace_tests
 +
 +    - name: Test clippy-driver
 +      run: bash .github/driver.sh
 +      env:
 +        OS: ${{ runner.os }}
 +
 +    - name: Test cargo dev new lint
 +      run: |
 +        cargo dev new_lint --name new_early_pass --pass early
 +        cargo dev new_lint --name new_late_pass --pass late
 +        cargo check
 +        git reset --hard HEAD
 +
 +  integration_build:
 +    needs: changelog
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Build Integration Test
 +      run: cargo test --test integration --features integration --no-run
 +
 +    # Upload
 +    - name: Extract Binaries
 +      run: |
 +        DIR=$CARGO_TARGET_DIR/debug
 +        rm $DIR/deps/integration-*.d
 +        mv $DIR/deps/integration-* $DIR/integration
 +        find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
 +        rm -rf $CARGO_TARGET_DIR/release
 +
 +    - name: Upload Binaries
 +      uses: actions/upload-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +  integration:
 +    needs: integration_build
 +    strategy:
 +      fail-fast: false
 +      max-parallel: 6
 +      matrix:
 +        integration:
 +        - 'rust-lang/cargo'
 +        # FIXME: re-enable once fmt_macros is renamed in RLS
 +        # - 'rust-lang/rls'
 +        - 'rust-lang/chalk'
 +        - 'rust-lang/rustfmt'
 +        - 'Marwes/combine'
 +        - 'Geal/nom'
 +        - 'rust-lang/stdarch'
 +        - 'serde-rs/serde'
 +        # FIXME: chrono currently cannot be compiled with `--all-targets`
 +        # - 'chronotope/chrono'
 +        - 'hyperium/hyper'
 +        - 'rust-random/rand'
 +        - 'rust-lang/futures-rs'
 +        - 'rust-itertools/itertools'
 +        - 'rust-lang-nursery/failure'
 +        - 'rust-lang/log'
 +
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Download
 +    - name: Download target dir
 +      uses: actions/download-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +    - name: Make Binaries Executable
 +      run: chmod +x $CARGO_TARGET_DIR/debug/*
 +
 +    # Run
 +    - name: Test ${{ matrix.integration }}
 +      run: |
 +        RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
 +          $CARGO_TARGET_DIR/debug/integration
 +      env:
 +        INTEGRATION: ${{ matrix.integration }}
 +
 +  # These jobs doesn't actually test anything, but they're only used to tell
 +  # bors the build completed, as there is no practical way to detect when a
 +  # workflow is successful listening to webhooks only.
 +  #
 +  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
 +
 +  end-success:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 4f25a86b2e4df3cac0a28a61639b0d6df04d36cd,0000000000000000000000000000000000000000..77efdec1e50db71b34a533df1d0deb068e3827e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,55 @@@
-       run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended
 +name: Remark
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +  pull_request:
 +    paths:
 +    - '**.md'
 +
 +jobs:
 +  remark:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - name: Checkout
 +      uses: actions/checkout@v2.3.3
 +
 +    - name: Setup Node.js
 +      uses: actions/setup-node@v1.4.4
 +
 +    - name: Install remark
++      run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
 +
 +    # Run
 +    - name: Check *.md files
 +      run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null
 +
 +  # These jobs doesn't actually test anything, but they're only used to tell
 +  # bors the build completed, as there is no practical way to detect when a
 +  # workflow is successful listening to webhooks only.
 +  #
 +  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
 +
 +  end-success:
 +    name: bors remark test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [remark]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors remark test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [remark]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 0ede7ac75cb6f98d73d8a4efa0cc2cad09b55fa3,0000000000000000000000000000000000000000..04b82b8cc581ff573d7bc7cd14856d38c2d127ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,13 @@@
 +{
 +  "plugins": [
 +    "remark-preset-lint-recommended",
++    "remark-gfm",
 +    ["remark-lint-list-item-indent", false],
 +    ["remark-lint-no-literal-urls", false],
 +    ["remark-lint-no-shortcut-reference-link", false],
 +    ["remark-lint-maximum-line-length", 120]
 +  ],
 +  "settings": {
 +    "commonmark": true
 +  }
 +}
index 41af8e190ddf3581503dc5cd0b902f68cb1e0f20,0000000000000000000000000000000000000000..f3a8070323879ec8e9d1fb765ac47df1a74c3cb2
mode 100644,000000..100644
--- /dev/null
@@@ -1,2772 -1,0 +1,2905 @@@
- [7c7683c...master](https://github.com/rust-lang/rust-clippy/compare/7c7683c...master)
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](doc/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
- Current beta, release 2021-06-17
++[3ae8faf...master](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...master)
++
++## Rust 1.54
++
++Current beta, release 2021-07-29
++
++[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
++
++### New Lints
++
++- [`ref_binding_to_reference`]
++  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
++- [`needless_bitwise_bool`]
++  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
++- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
++- [`manual_str_repeat`]
++  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
++- [`suspicious_splitn`]
++  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
++
++### Moves and Deprecations
++
++- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
++  the new `avoid_breaking_exported_api` config option (see
++  [Enhancements](#1-54-enhancements))
++  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
++- Move [`inconsistent_struct_constructor`] to `pedantic`
++  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
++- Move [`needless_borrow`] to `style` (now warn-by-default)
++  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
++- Move [`suspicious_operation_groupings`] to `nursery`
++  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
++- Move [`semicolon_if_nothing_returned`] to `pedantic`
++  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
++
++### Enhancements <a name="1-54-enhancements"></a>
++
++- [`while_let_on_iterator`]: Now also lints in nested loops
++  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
++- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
++  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
++- [`needless_collect`]: Now also lints on assignments with type annotations
++  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
++- [`if_then_some_else_none`]: Now works with the MSRV config
++  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
++- Add `avoid_breaking_exported_api` config option for the lints
++  [`enum_variant_names`], [`large_types_passed_by_value`],
++  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
++  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
++  this configuration option to `false` before a major release (1.0/2.0/...) to
++  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
++- [`needless_collect`]: Now lints on even more data structures
++  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
++- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
++  attributes as sufficient documentation
++  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
++- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
++  Now work as expected when used with `allow`
++  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
++
++### False Positive Fixes
++
++- [`implicit_return`]: Now takes all diverging functions in account to avoid
++  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
++- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
++  and the struct is used in the loop
++  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
++- [`multiple_inherent_impl`]: No longer lints with generic arguments
++  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
++- [`comparison_chain`]: No longer lints in a `const` context
++  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
++- [`while_immutable_condition`]: Fix false positive where mutation in the loop
++  variable wasn't picked up
++  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
++- [`default_trait_access`]: No longer lints in macros
++  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
++- [`needless_question_mark`]: No longer lints when the inner value is implicitly
++  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
++- [`unused_unit`]: No longer lints when multiple macro contexts are involved
++  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
++- [`eval_order_dependence`]: Fix false positive in async context
++  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
++- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
++  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
++- [`wrong_self_convention`]: No longer lints in trait implementations of
++  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
++- [`suboptimal_flops`]: No longer lints on `powi(2)`
++  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
++- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
++  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
++- [`option_if_let_else`]: No longer lints on `else if let` pattern
++  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
++- [`use_self`], [`useless_conversion`]: Fix false positives when generic
++  arguments are involved
++  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
++- [`manual_unwrap_or`]: Fix false positive with deref coercion
++  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
++- [`similar_names`]: No longer lints on `wparam`/`lparam`
++  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
++- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
++  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
++
++### Suggestion Fixes/Improvements
++
++- [`implicit_return`]
++  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
++    - Fix suggestion for async functions
++    - Improve suggestion with macros
++    - Suggest to change `break` to `return` when appropriate
++- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
++  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
++- [`match_single_binding`]: Improve suggestion when match scrutinee has side
++  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
++- [`needless_borrow`]: Now suggests to also change usage sites as needed
++  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
++- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
++  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
++- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
++  when a `<_ as Trait>::_` is involved
++  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
++- [`not_unsafe_ptr_arg_deref`]: Improved error message
++  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
++
++### ICE Fixes
++
++- Fix ICE when running Clippy on `libstd`
++  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
++- [`implicit_return`]
++  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
- Current stable, released 2021-05-06
++Current stable, released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
++Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_method`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* [`ref_in_deref`] Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`blacklisted_name`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
++[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
++[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
++[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
++[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
++[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index 7d7b7c811738e897ac7aa0a61d95908ca1b49368,0000000000000000000000000000000000000000..4273fda4e640d5d9356a5d63f166db26224fc603
mode 100644,000000..100644
--- /dev/null
@@@ -1,381 -1,0 +1,381 @@@
- Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 +# 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 setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 +you just cloned.
 +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 +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 b003b15a11d750553db593179b658ae375bf6464,0000000000000000000000000000000000000000..9b5d9b2adf3b2413c85027477d1a2e36043b9801
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
- version = "0.1.54"
 +[package]
 +name = "clippy"
++version = "0.1.55"
 +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"
 +serde = { version = "1.0", features = ["derive"] }
 +derive-new = "0.5"
 +regex = "1.4"
 +quote = "1"
 +syn = { version = "1", features = ["full"] }
 +# This is used by the `collect-metadata` alias.
 +filetime = "0.2"
 +
 +# A noop dependency that changes in the Rust repository, it's a bit of a hack.
 +# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
 +# for more information.
 +rustc-workspace-hack = "1.0.0"
 +
 +[build-dependencies]
 +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 +
 +[features]
 +deny-warnings = ["clippy_lints/deny-warnings"]
 +integration = ["tempfile"]
 +internal-lints = ["clippy_lints/internal-lints"]
 +metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index bd322cc80702acc0ee8c2498fd71f903b81dfdf9,0000000000000000000000000000000000000000..e1c968273cdf63ef95eae0633929566df1304e7e
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,246 @@@
- | Category              | Description                                                             | Default level |
- | --------------------- | ----------------------------------------------------------------------- | ------------- |
- | `clippy::all`         | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
- | `clippy::correctness` | code that is outright wrong or very useless                             | **deny**      |
- | `clippy::style`       | code that should be written in a more idiomatic way                     | **warn**      |
- | `clippy::complexity`  | code that does something simple but in a complex way                    | **warn**      |
- | `clippy::perf`        | code that can be written to run faster                                  | **warn**      |
- | `clippy::pedantic`    | lints which are rather strict or might have false positives             | allow         |
- | `clippy::nursery`     | new lints that are still under development                              | allow         |
- | `clippy::cargo`       | lints for the cargo manifest                                            | allow         |
 +# Clippy
 +
 +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
 +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
 +
 +A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 +
 +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 +
 +Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 +You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
 +
- Clippy can automatically apply some lint suggestions.
- Note that this is still experimental and only supported on the nightly channel:
++| Category              | Description                                                                         | Default level |
++| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
++| `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
++| `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
++| `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
++| `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
++| `clippy::complexity`  | code that does something simple but in a complex way                                | **warn**      |
++| `clippy::perf`        | code that can be written to run faster                                              | **warn**      |
++| `clippy::pedantic`    | lints which are rather strict or might have false positives                         | allow         |
++| `clippy::nursery`     | new lints that are still under development                                          | allow         |
++| `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |
 +
 +More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 +
 +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
 +for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
 +very selectively, if at all.
 +
 +Table of contents:
 +
 +*   [Usage instructions](#usage)
 +*   [Configuration](#configuration)
 +*   [Contributing](#contributing)
 +*   [License](#license)
 +
 +## Usage
 +
 +Below are instructions on how to use Clippy as a subcommand, compiled from source
 +or in Travis CI.
 +
 +### As a cargo subcommand (`cargo clippy`)
 +
 +One way to use Clippy is by installing Clippy through rustup as a cargo
 +subcommand.
 +
 +#### Step 1: Install rustup
 +
 +You can install [rustup](https://rustup.rs/) on supported platforms. This will help
 +us install Clippy and its dependencies.
 +
 +If you already have rustup installed, update to ensure you have the latest
 +rustup and compiler:
 +
 +```terminal
 +rustup update
 +```
 +
 +#### Step 2: Install Clippy
 +
 +Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
 +
 +```terminal
 +rustup component add clippy
 +```
 +If it says that it can't find the `clippy` component, please run `rustup self update`.
 +
 +#### Step 3: Run Clippy
 +
 +Now you can run Clippy by invoking the following command:
 +
 +```terminal
 +cargo clippy
 +```
 +
 +#### Automatically applying Clippy suggestions
 +
- cargo clippy --fix -Z unstable-options
++Clippy can automatically apply some lint suggestions, just like the compiler.
 +
 +```terminal
++cargo clippy --fix
 +```
 +
 +#### Workspaces
 +
 +All the usual workspace options should work with Clippy. For example the following command
 +will run Clippy on the `example` crate:
 +
 +```terminal
 +cargo clippy -p example
 +```
 +
 +As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
 +If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
 +
 +```terminal
 +cargo clippy -p example -- --no-deps
 +```
 +
 +### As a rustc replacement (`clippy-driver`)
 +
 +Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
 +your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
 +
 +```terminal
 +rustc --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +Then, to enable Clippy, you will need to call:
 +
 +```terminal
 +clippy-driver --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
 +
 +### Travis CI
 +
 +You can add Clippy to Travis CI in the same way you use it locally:
 +
 +```yml
 +language: rust
 +rust:
 +  - stable
 +  - beta
 +before_script:
 +  - rustup component add clippy
 +script:
 +  - cargo clippy
 +  # if you want the build job to fail when encountering warnings, use
 +  - cargo clippy -- -D warnings
 +  # in order to also check tests and non-default crate features, use
 +  - cargo clippy --all-targets --all-features -- -D warnings
 +  - cargo test
 +  # etc.
 +```
 +
 +Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
 +That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
 +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
 +line. (You can swap `clippy::all` with the specific lint category you are targeting.)
 +
 +## Configuration
 +
 +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
 +value` mapping eg.
 +
 +```toml
 +avoid-breaking-exported-api = false
 +blacklisted-names = ["toto", "tata", "titi"]
 +cognitive-complexity-threshold = 30
 +```
 +
 +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
 +lints can be configured and the meaning of the variables.
 +
 +To deactivate the “for further information visit *lint-link*” message you can
 +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
 +
 +### Allowing/denying lints
 +
 +You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
 +
 +*   the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
 +
 +*   all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
 +    `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
 +    lints prone to false positives.
 +
 +*   only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
 +
 +*   `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
 +
 +Note: `allow` means to suppress the lint for your code. With `warn` the lint
 +will only emit a warning, while with `deny` the lint will emit an error, when
 +triggering for your code. An error causes clippy to exit with an error code, so
 +is useful in scripts like CI/CD.
 +
 +If you do not want to include your lint levels in your code, you can globally
 +enable/disable lints by passing extra flags to Clippy during the run:
 +
 +To allow `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -A clippy::lint_name
 +```
 +
 +And to warn on `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -W clippy::lint_name
 +```
 +
 +This also works with lint groups. For example you
 +can run Clippy with warnings for all lints enabled:
 +```terminal
 +cargo clippy -- -W clippy::pedantic
 +```
 +
 +If you care only about a single lint, you can allow all others and then explicitly warn on
 +the lint(s) you are interested in:
 +```terminal
 +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
 +```
 +
 +### Specifying the minimum supported Rust version
 +
 +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
 +specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
 +
 +```toml
 +msrv = "1.30.0"
 +```
 +
 +The MSRV can also be specified as an inner attribute, like below.
 +
 +```rust
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "1.30.0"]
 +
 +fn main() {
 +  ...
 +}
 +```
 +
 +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
 +is equivalent to `msrv = 1.30.0`.
 +
 +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
 +
 +Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
 +
 +## Contributing
 +
 +If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
 +
 +## License
 +
 +Copyright 2014-2021 The Rust Project Developers
 +
 +Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 +[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
 +<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
 +option. Files in the project may not be
 +copied, modified, or distributed except according to those terms.
index b1844e29b3273fa3e41a1a65fe070f4db1ffe73f,0000000000000000000000000000000000000000..5c6c106e0e638429705e62ebba344f6e4ca6bf78
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,17 @@@
- opener = "0.4"
 +[package]
 +name = "clippy_dev"
 +version = "0.0.1"
 +authors = ["The Rust Clippy Developers"]
 +edition = "2018"
 +
 +[dependencies]
 +bytecount = "0.6"
 +clap = "2.33"
 +itertools = "0.9"
++opener = "0.5"
 +regex = "1"
 +shell-escape = "0.1"
 +walkdir = "2"
 +
 +[features]
 +deny-warnings = []
index 1517cdc9419626600ad03b60cbc28e1ef1cc7d42,0000000000000000000000000000000000000000..c81eb40d52f3551ffc698db36acf7767b5c3874e
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,198 @@@
-             if path.extension() != Some("rs".as_ref())
-                 || entry.file_name() == "ice-3891.rs"
-                 // Avoid rustfmt bug rust-lang/rustfmt#1873
-                 || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
-             {
 +use crate::clippy_project_root;
 +use shell_escape::escape;
 +use std::ffi::OsStr;
 +use std::path::Path;
 +use std::process::{self, Command};
 +use std::{fs, io};
 +use walkdir::WalkDir;
 +
 +#[derive(Debug)]
 +pub enum CliError {
 +    CommandFailed(String, String),
 +    IoError(io::Error),
 +    RustfmtNotInstalled,
 +    WalkDirError(walkdir::Error),
 +    RaSetupActive,
 +}
 +
 +impl From<io::Error> for CliError {
 +    fn from(error: io::Error) -> Self {
 +        Self::IoError(error)
 +    }
 +}
 +
 +impl From<walkdir::Error> for CliError {
 +    fn from(error: walkdir::Error) -> Self {
 +        Self::WalkDirError(error)
 +    }
 +}
 +
 +struct FmtContext {
 +    check: bool,
 +    verbose: bool,
 +}
 +
 +// the "main" function of cargo dev fmt
 +pub fn run(check: bool, verbose: bool) {
 +    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
 +        let mut success = true;
 +
 +        let project_root = clippy_project_root();
 +
 +        // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
 +        // format because rustfmt would also format the entire rustc repo as it is a local
 +        // dependency
 +        if fs::read_to_string(project_root.join("Cargo.toml"))
 +            .expect("Failed to read clippy Cargo.toml")
 +            .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
 +        {
 +            return Err(CliError::RaSetupActive);
 +        }
 +
 +        rustfmt_test(context)?;
 +
 +        success &= cargo_fmt(context, project_root.as_path())?;
 +        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
 +        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
 +        success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
 +
 +        for entry in WalkDir::new(project_root.join("tests")) {
 +            let entry = entry?;
 +            let path = entry.path();
 +
-                     "error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
++            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
 +                continue;
 +            }
 +
 +            success &= rustfmt(context, path)?;
 +        }
 +
 +        Ok(success)
 +    }
 +
 +    fn output_err(err: CliError) {
 +        match err {
 +            CliError::CommandFailed(command, stderr) => {
 +                eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr);
 +            },
 +            CliError::IoError(err) => {
 +                eprintln!("error: {}", err);
 +            },
 +            CliError::RustfmtNotInstalled => {
 +                eprintln!("error: rustfmt nightly is not installed.");
 +            },
 +            CliError::WalkDirError(err) => {
 +                eprintln!("error: {}", err);
 +            },
 +            CliError::RaSetupActive => {
 +                eprintln!(
++                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
 +Not formatting because that would format the local repo as well!
 +Please revert the changes to Cargo.tomls first."
 +                );
 +            },
 +        }
 +    }
 +
 +    let context = FmtContext { check, verbose };
 +    let result = try_run(&context);
 +    let code = match result {
 +        Ok(true) => 0,
 +        Ok(false) => {
 +            eprintln!();
 +            eprintln!("Formatting check failed.");
 +            eprintln!("Run `cargo dev fmt` to update formatting.");
 +            1
 +        },
 +        Err(err) => {
 +            output_err(err);
 +            1
 +        },
 +    };
 +    process::exit(code);
 +}
 +
 +fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
 +    let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
 +
 +    format!(
 +        "cd {} && {} {}",
 +        escape(dir.as_ref().to_string_lossy()),
 +        escape(program.as_ref().to_string_lossy()),
 +        arg_display.join(" ")
 +    )
 +}
 +
 +fn exec(
 +    context: &FmtContext,
 +    program: impl AsRef<OsStr>,
 +    dir: impl AsRef<Path>,
 +    args: &[impl AsRef<OsStr>],
 +) -> Result<bool, CliError> {
 +    if context.verbose {
 +        println!("{}", format_command(&program, &dir, args));
 +    }
 +
 +    let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
 +    let output = child.wait_with_output()?;
 +    let success = output.status.success();
 +
 +    if !context.check && !success {
 +        let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
 +        return Err(CliError::CommandFailed(
 +            format_command(&program, &dir, args),
 +            String::from(stderr),
 +        ));
 +    }
 +
 +    Ok(success)
 +}
 +
 +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
 +    let mut args = vec!["+nightly", "fmt", "--all"];
 +    if context.check {
 +        args.push("--");
 +        args.push("--check");
 +    }
 +    let success = exec(context, "cargo", path, &args)?;
 +
 +    Ok(success)
 +}
 +
 +fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
 +    let program = "rustfmt";
 +    let dir = std::env::current_dir()?;
 +    let args = &["+nightly", "--version"];
 +
 +    if context.verbose {
 +        println!("{}", format_command(&program, &dir, args));
 +    }
 +
 +    let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
 +
 +    if output.status.success() {
 +        Ok(())
 +    } else if std::str::from_utf8(&output.stderr)
 +        .unwrap_or("")
 +        .starts_with("error: 'rustfmt' is not installed")
 +    {
 +        Err(CliError::RustfmtNotInstalled)
 +    } else {
 +        Err(CliError::CommandFailed(
 +            format_command(&program, &dir, args),
 +            std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
 +        ))
 +    }
 +}
 +
 +fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
 +    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
 +    if context.check {
 +        args.push("--check".as_ref());
 +    }
 +    let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
 +    if !success {
 +        eprintln!("rustfmt failed on {}", path.display());
 +    }
 +    Ok(success)
 +}
index 69f42aca8b6900a092fc09e73aff9b23eea1e817,0000000000000000000000000000000000000000..72bdaf8d59282f89f39f63142a9191325f4c224f
mode 100644,000000..100644
--- /dev/null
@@@ -1,560 -1,0 +1,560 @@@
- pub mod ide_setup;
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use itertools::Itertools;
 +use regex::Regex;
 +use std::collections::HashMap;
 +use std::ffi::OsStr;
 +use std::fs;
 +use std::lazy::SyncLazy;
 +use std::path::{Path, PathBuf};
 +use walkdir::WalkDir;
 +
 +pub mod bless;
 +pub mod fmt;
 +pub mod new_lint;
 +pub mod serve;
++pub mod setup;
 +pub mod stderr_length_check;
 +pub mod update_lints;
 +
 +static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_clippy_lint!\s*[\{(]
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    (?P<cat>[a-z_]+)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +
 +static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_deprecated_lint!\s*[{(]\s*
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
 +
 +pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Debug)]
 +pub struct Lint {
 +    pub name: String,
 +    pub group: String,
 +    pub desc: String,
 +    pub deprecation: Option<String>,
 +    pub module: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.to_string(),
 +            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
 +            deprecation: deprecation.map(ToString::to_string),
 +            module: module.to_string(),
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    pub fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    pub fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns all deprecated lints
 +    #[must_use]
 +    pub fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    pub fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_lint_group_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .map(|l| format!("        LintId::of({}::{}),", l.module, l.name.to_uppercase()))
 +        .sorted()
 +        .collect::<Vec<String>>()
 +}
 +
 +/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .map(|l| &l.module)
 +        .unique()
 +        .map(|module| format!("mod {};", module))
 +        .sorted()
 +        .collect::<Vec<String>>()
 +}
 +
 +/// Generates the list of lint links at the bottom of the README
 +#[must_use]
 +pub fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .sorted_by_key(|l| &l.name)
 +        .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
 +        .collect()
 +}
 +
 +/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
 +#[must_use]
 +pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .flat_map(|l| {
 +            l.deprecation
 +                .clone()
 +                .map(|depr_text| {
 +                    vec![
 +                        "    store.register_removed(".to_string(),
 +                        format!("        \"clippy::{}\",", l.name),
 +                        format!("        \"{}\",", depr_text),
 +                        "    );".to_string(),
 +                    ]
 +                })
 +                .expect("only deprecated lints should be passed")
 +        })
 +        .collect::<Vec<String>>()
 +}
 +
 +#[must_use]
 +pub fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> Vec<String> {
 +    let header = "    store.register_lints(&[".to_string();
 +    let footer = "    ]);".to_string();
 +    let internal_lints = internal_lints
 +        .sorted_by_key(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .map(|l| {
 +            format!(
 +                "        #[cfg(feature = \"internal-lints\")]\n        {}::{},",
 +                l.module,
 +                l.name.to_uppercase()
 +            )
 +        });
 +    let other_lints = usable_lints
 +        .sorted_by_key(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .map(|l| format!("        {}::{},", l.module, l.name.to_uppercase()))
 +        .sorted();
 +    let mut lint_list = vec![header];
 +    lint_list.extend(internal_lints);
 +    lint_list.extend(other_lints);
 +    lint_list.push(footer);
 +    lint_list
 +}
 +
 +/// Gathers all files in `src/clippy_lints` and gathers all lints inside
 +pub fn gather_all() -> impl Iterator<Item = Lint> {
 +    lint_files().flat_map(|f| gather_from_file(&f))
 +}
 +
 +fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
 +    let content = fs::read_to_string(dir_entry.path()).unwrap();
 +    let path = dir_entry.path();
 +    let filename = path.file_stem().unwrap();
 +    let path_buf = path.with_file_name(filename);
 +    let mut rel_path = path_buf
 +        .strip_prefix(clippy_project_root().join("clippy_lints/src"))
 +        .expect("only files in `clippy_lints/src` should be looked at");
 +    // If the lints are stored in mod.rs, we get the module name from
 +    // the containing directory:
 +    if filename == "mod" {
 +        rel_path = rel_path.parent().unwrap();
 +    }
 +
 +    let module = rel_path
 +        .components()
 +        .map(|c| c.as_os_str().to_str().unwrap())
 +        .collect::<Vec<_>>()
 +        .join("::");
 +
 +    parse_contents(&content, &module)
 +}
 +
 +fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
 +    let lints = DEC_CLIPPY_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
 +    let deprecated = DEC_DEPRECATED_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
 +    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
 +    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
 +}
 +
 +/// Collects all .rs files in the `clippy_lints/src` directory
 +fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
 +    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
 +    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
 +    let path = clippy_project_root().join("clippy_lints/src");
 +    WalkDir::new(path)
 +        .into_iter()
 +        .filter_map(Result::ok)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +}
 +
 +/// Whether a file has had its text changed or not
 +#[derive(PartialEq, Debug)]
 +pub struct FileChange {
 +    pub changed: bool,
 +    pub new_lines: String,
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +pub fn replace_region_in_file<F>(
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    replace_start: bool,
 +    write_back: bool,
 +    replacements: F,
 +) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
 +    let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
 +
 +    if write_back {
 +        if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
 +            panic!("Cannot write to {}: {}", path.display(), e);
 +        }
 +    }
 +    file_change
 +}
 +
 +/// Replaces a region in a text delimited by two lines matching regexes.
 +///
 +/// * `text` is the input text on which you want to perform the replacement
 +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
 +///   delimiter line is never replaced.
 +/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
 +///
 +/// If you want to perform the replacement on files instead of already parsed text,
 +/// use `replace_region_in_file`.
 +///
 +/// # Example
 +///
 +/// ```
 +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
 +/// let result =
 +///     clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
 +///         vec!["a different".to_string(), "text".to_string()]
 +///     })
 +///     .new_lines;
 +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
 +/// ```
 +///
 +/// # Panics
 +///
 +/// Panics if start or end is not valid regex
 +pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let replace_it = replacements();
 +    let mut in_old_region = false;
 +    let mut found = false;
 +    let mut new_lines = vec![];
 +    let start = Regex::new(start).unwrap();
 +    let end = Regex::new(end).unwrap();
 +
 +    for line in text.lines() {
 +        if in_old_region {
 +            if end.is_match(line) {
 +                in_old_region = false;
 +                new_lines.extend(replace_it.clone());
 +                new_lines.push(line.to_string());
 +            }
 +        } else if start.is_match(line) {
 +            if !replace_start {
 +                new_lines.push(line.to_string());
 +            }
 +            in_old_region = true;
 +            found = true;
 +        } else {
 +            new_lines.push(line.to_string());
 +        }
 +    }
 +
 +    if !found {
 +        // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
 +        // given text or file. Most likely this is an error on the programmer's side and the Regex
 +        // is incorrect.
 +        eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
 +        std::process::exit(1);
 +    }
 +
 +    let mut new_lines = new_lines.join("\n");
 +    if text.ends_with('\n') {
 +        new_lines.push('\n');
 +    }
 +    let changed = new_lines != text;
 +    FileChange { changed, new_lines }
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
 +
 +#[test]
 +fn test_parse_contents() {
 +    let result: Vec<Lint> = parse_contents(
 +        r#"
 +declare_clippy_lint! {
 +    pub PTR_ARG,
 +    style,
 +    "really long \
 +     text"
 +}
 +
 +declare_clippy_lint!{
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "single line"
 +}
 +
 +/// some doc comment
 +declare_deprecated_lint! {
 +    pub SHOULD_ASSERT_EQ,
 +    "`assert!()` will be more flexible with RFC 2011"
 +}
 +    "#,
 +        "module_name",
 +    )
 +    .collect();
 +
 +    let expected = vec![
 +        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
 +        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
 +        Lint::new(
 +            "should_assert_eq",
 +            "Deprecated",
 +            "`assert!()` will be more flexible with RFC 2011",
 +            Some("`assert!()` will be more flexible with RFC 2011"),
 +            "module_name",
 +        ),
 +    ];
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region() {
 +    let text = "\nabc\n123\n789\ndef\nghi";
 +    let expected = FileChange {
 +        changed: true,
 +        new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
 +        vec!["hello world".to_string()]
 +    });
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region_with_start() {
 +    let text = "\nabc\n123\n789\ndef\nghi";
 +    let expected = FileChange {
 +        changed: true,
 +        new_lines: "\nhello world\ndef\nghi".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
 +        vec!["hello world".to_string()]
 +    });
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_replace_region_no_changes() {
 +    let text = "123\n456\n789";
 +    let expected = FileChange {
 +        changed: false,
 +        new_lines: "123\n456\n789".to_string(),
 +    };
 +    let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
 +    assert_eq!(expected, result);
 +}
 +
 +#[test]
 +fn test_usable_lints() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
 +        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![Lint::new(
 +        "should_assert_eq2",
 +        "Not Deprecated",
 +        "abc",
 +        None,
 +        "module_name",
 +    )];
 +    assert_eq!(expected, Lint::usable_lints(&lints));
 +}
 +
 +#[test]
 +fn test_by_lint_group() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +    ];
 +    let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +    expected.insert(
 +        "group1".to_string(),
 +        vec![
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +        ],
 +    );
 +    expected.insert(
 +        "group2".to_string(),
 +        vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
 +    );
 +    assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +}
 +
 +#[test]
 +fn test_gen_changelog_lint_list() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![
 +        format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
 +        format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
 +    ];
 +    assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
 +}
 +
 +#[test]
 +fn test_gen_deprecated() {
 +    let lints = vec![
 +        Lint::new(
 +            "should_assert_eq",
 +            "group1",
 +            "abc",
 +            Some("has been superseded by should_assert_eq2"),
 +            "module_name",
 +        ),
 +        Lint::new(
 +            "another_deprecated",
 +            "group2",
 +            "abc",
 +            Some("will be removed"),
 +            "module_name",
 +        ),
 +    ];
 +    let expected: Vec<String> = vec![
 +        "    store.register_removed(",
 +        "        \"clippy::should_assert_eq\",",
 +        "        \"has been superseded by should_assert_eq2\",",
 +        "    );",
 +        "    store.register_removed(",
 +        "        \"clippy::another_deprecated\",",
 +        "        \"will be removed\",",
 +        "    );",
 +    ]
 +    .into_iter()
 +    .map(String::from)
 +    .collect();
 +    assert_eq!(expected, gen_deprecated(lints.iter()));
 +}
 +
 +#[test]
 +#[should_panic]
 +fn test_gen_deprecated_fail() {
 +    let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
 +    let _deprecated_lints = gen_deprecated(lints.iter());
 +}
 +
 +#[test]
 +fn test_gen_modules_list() {
 +    let lints = vec![
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
 +    ];
 +    let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
 +    assert_eq!(expected, gen_modules_list(lints.iter()));
 +}
 +
 +#[test]
 +fn test_gen_lint_group_list() {
 +    let lints = vec![
 +        Lint::new("abc", "group1", "abc", None, "module_name"),
 +        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +        Lint::new("internal", "internal_style", "abc", None, "module_name"),
 +    ];
 +    let expected = vec![
 +        "        LintId::of(module_name::ABC),".to_string(),
 +        "        LintId::of(module_name::INTERNAL),".to_string(),
 +        "        LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(),
 +    ];
 +    assert_eq!(expected, gen_lint_group_list(lints.iter()));
 +}
index 7040c257c831b0e49ec02e340ecdb4878876d8a1,0000000000000000000000000000000000000000..ff324ff6ee6fff5b6a38edc9aaefc6ee09df0f34
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,224 @@@
- use clap::{App, Arg, ArgMatches, SubCommand};
- use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints};
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
-         ("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")),
++use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
++use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
 +        ("bless", Some(matches)) => {
 +            bless::bless(matches.is_present("ignore-timestamp"));
 +        },
 +        ("fmt", Some(matches)) => {
 +            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
 +        },
 +        ("update_lints", Some(matches)) => {
 +            if matches.is_present("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.is_present("check") {
 +                update_lints::run(update_lints::UpdateMode::Check);
 +            } else {
 +                update_lints::run(update_lints::UpdateMode::Change);
 +            }
 +        },
 +        ("new_lint", Some(matches)) => {
 +            match new_lint::create(
 +                matches.value_of("pass"),
 +                matches.value_of("name"),
 +                matches.value_of("category"),
 +            ) {
 +                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
 +        ("limit_stderr_length", _) => {
 +            stderr_length_check::check();
 +        },
-             SubCommand::with_name("ide_setup")
-                 .about("Alter dependencies so Intellij Rust can find rustc internals")
-                 .arg(
-                     Arg::with_name("rustc-repo-path")
-                         .long("repo-path")
-                         .short("r")
-                         .help("The path to a rustc repo that will be used for setting the dependencies")
-                         .takes_value(true)
-                         .value_name("path")
-                         .required(true),
++        ("setup", Some(sub_command)) => match sub_command.subcommand() {
++            ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
++                matches
++                    .value_of("rustc-repo-path")
++                    .expect("this field is mandatory and therefore always valid"),
++            ),
++            ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
++            ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
++            _ => {},
++        },
++        ("remove", Some(sub_command)) => match sub_command.subcommand() {
++            ("git-hook", Some(_)) => setup::git_hook::remove_hook(),
++            ("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
++            ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
++            _ => {},
++        },
 +        ("serve", Some(matches)) => {
 +            let port = matches.value_of("port").unwrap().parse().unwrap();
 +            let lint = matches.value_of("lint");
 +            serve::run(port, lint);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("Clippy developer tooling")
++        .setting(AppSettings::ArgRequiredElseHelp)
 +        .subcommand(
 +            SubCommand::with_name("bless")
 +                .about("bless the test output changes")
 +                .arg(
 +                    Arg::with_name("ignore-timestamp")
 +                        .long("ignore-timestamp")
 +                        .help("Include files updated before clippy was built"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("fmt")
 +                .about("Run rustfmt on all projects and tests")
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Use the rustfmt --check option"),
 +                )
 +                .arg(
 +                    Arg::with_name("verbose")
 +                        .short("v")
 +                        .long("verbose")
 +                        .help("Echo commands run"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                 * the lint count in README.md is correct\n \
 +                 * the changelog contains markdown link references at the bottom\n \
 +                 * all lint groups include the correct lints\n \
 +                 * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
 +                 * all lints are registered in the lint store",
 +                )
 +                .arg(Arg::with_name("print-only").long("print-only").help(
 +                    "Print a table of lints to STDOUT. \
 +                 This does not include deprecated and internal lints. \
 +                 (Does not modify any files)",
 +                ))
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .arg(
 +                    Arg::with_name("pass")
 +                        .short("p")
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .possible_values(&["early", "late"])
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("name")
 +                        .short("n")
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("category")
 +                        .short("c")
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .possible_values(&[
 +                            "style",
 +                            "correctness",
++                            "suspicious",
 +                            "complexity",
 +                            "perf",
 +                            "pedantic",
 +                            "restriction",
 +                            "cargo",
 +                            "nursery",
 +                            "internal",
 +                            "internal_warn",
 +                        ])
 +                        .takes_value(true),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("limit_stderr_length")
 +                .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
 +        )
 +        .subcommand(
++            SubCommand::with_name("setup")
++                .about("Support for setting up your personal development environment")
++                .setting(AppSettings::ArgRequiredElseHelp)
++                .subcommand(
++                    SubCommand::with_name("intellij")
++                        .about("Alter dependencies so Intellij Rust can find rustc internals")
++                        .arg(
++                            Arg::with_name("rustc-repo-path")
++                                .long("repo-path")
++                                .short("r")
++                                .help("The path to a rustc repo that will be used for setting the dependencies")
++                                .takes_value(true)
++                                .value_name("path")
++                                .required(true),
++                        ),
++                )
++                .subcommand(
++                    SubCommand::with_name("git-hook")
++                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
++                        .arg(
++                            Arg::with_name("force-override")
++                                .long("force-override")
++                                .short("f")
++                                .help("Forces the override of an existing git pre-commit hook")
++                                .required(false),
++                        ),
++                )
++                .subcommand(
++                    SubCommand::with_name("vscode-tasks")
++                        .about("Add several tasks to vscode for formatting, validation and testing")
++                        .arg(
++                            Arg::with_name("force-override")
++                                .long("force-override")
++                                .short("f")
++                                .help("Forces the override of existing vscode tasks")
++                                .required(false),
++                        ),
++                ),
++        )
++        .subcommand(
++            SubCommand::with_name("remove")
++                .about("Support for undoing changes done by the setup command")
++                .setting(AppSettings::ArgRequiredElseHelp)
++                .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
++                .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
++                .subcommand(
++                    SubCommand::with_name("intellij")
++                        .about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .arg(
 +                    Arg::with_name("port")
 +                        .long("port")
 +                        .short("p")
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .validator_os(serve::validate_port),
 +                )
 +                .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
 +        )
 +        .get_matches()
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3fbb77d59235c32604ec8079fcb1c57081a73521
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++use std::fs;
++use std::path::Path;
++
++use super::verify_inside_clippy_dir;
++
++/// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
++/// I've decided against this for the sake of simplicity and to make sure that it doesn't install
++/// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
++/// for formatting and should therefor only be used in a normal clone of clippy
++const REPO_GIT_DIR: &str = ".git";
++const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh";
++const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit";
++
++pub fn install_hook(force_override: bool) {
++    if !check_precondition(force_override) {
++        return;
++    }
++
++    // So a little bit of a funny story. Git on unix requires the pre-commit file
++    // to have the `execute` permission to be set. The Rust functions for modifying
++    // these flags doesn't seem to work when executed with normal user permissions.
++    //
++    // However, there is a little hack that is also being used by Rust itself in their
++    // setup script. Git saves the `execute` flag when syncing files. This means
++    // that we can check in a file with execution permissions and the sync it to create
++    // a file with the flag set. We then copy this file here. The copy function will also
++    // include the `execute` permission.
++    match fs::copy(HOOK_SOURCE_FILE, HOOK_TARGET_FILE) {
++        Ok(_) => {
++            println!("info: the hook can be removed with `cargo dev remove git-hook`");
++            println!("git hook successfully installed");
++        },
++        Err(err) => eprintln!(
++            "error: unable to copy `{}` to `{}` ({})",
++            HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
++        ),
++    }
++}
++
++fn check_precondition(force_override: bool) -> bool {
++    if !verify_inside_clippy_dir() {
++        return false;
++    }
++
++    // Make sure that we can find the git repository
++    let git_path = Path::new(REPO_GIT_DIR);
++    if !git_path.exists() || !git_path.is_dir() {
++        eprintln!("error: clippy_dev was unable to find the `.git` directory");
++        return false;
++    }
++
++    // Make sure that we don't override an existing hook by accident
++    let path = Path::new(HOOK_TARGET_FILE);
++    if path.exists() {
++        if force_override {
++            return delete_git_hook_file(path);
++        }
++
++        eprintln!("error: there is already a pre-commit hook installed");
++        println!("info: use the `--force-override` flag to override the existing hook");
++        return false;
++    }
++
++    true
++}
++
++pub fn remove_hook() {
++    let path = Path::new(HOOK_TARGET_FILE);
++    if path.exists() {
++        if delete_git_hook_file(path) {
++            println!("git hook successfully removed");
++        }
++    } else {
++        println!("no pre-commit hook was found");
++    }
++}
++
++fn delete_git_hook_file(path: &Path) -> bool {
++    if let Err(err) = fs::remove_file(path) {
++        eprintln!("error: unable to delete existing pre-commit git hook ({})", err);
++        false
++    } else {
++        true
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf741e6d1217f98b25b1168ba8ede7df757440ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,223 @@@
++use std::fs;
++use std::fs::File;
++use std::io::prelude::*;
++use std::path::{Path, PathBuf};
++
++// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
++// the respective rustc subcrates instead of using extern crate xyz.
++// This allows IntelliJ to analyze rustc internals and show proper information inside Clippy
++// code. See https://github.com/rust-lang/rust-clippy/issues/5514 for details
++
++const RUSTC_PATH_SECTION: &str = "[target.'cfg(NOT_A_PLATFORM)'.dependencies]";
++const DEPENDENCIES_SECTION: &str = "[dependencies]";
++
++const CLIPPY_PROJECTS: &[ClippyProjectInfo] = &[
++    ClippyProjectInfo::new("root", "Cargo.toml", "src/driver.rs"),
++    ClippyProjectInfo::new("clippy_lints", "clippy_lints/Cargo.toml", "clippy_lints/src/lib.rs"),
++    ClippyProjectInfo::new("clippy_utils", "clippy_utils/Cargo.toml", "clippy_utils/src/lib.rs"),
++];
++
++/// Used to store clippy project information to later inject the dependency into.
++struct ClippyProjectInfo {
++    /// Only used to display information to the user
++    name: &'static str,
++    cargo_file: &'static str,
++    lib_rs_file: &'static str,
++}
++
++impl ClippyProjectInfo {
++    const fn new(name: &'static str, cargo_file: &'static str, lib_rs_file: &'static str) -> Self {
++        Self {
++            name,
++            cargo_file,
++            lib_rs_file,
++        }
++    }
++}
++
++pub fn setup_rustc_src(rustc_path: &str) {
++    let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) {
++        Ok(path) => path,
++        Err(_) => return,
++    };
++
++    for project in CLIPPY_PROJECTS {
++        if inject_deps_into_project(&rustc_source_dir, project).is_err() {
++            return;
++        }
++    }
++
++    println!("info: the source paths can be removed again with `cargo dev remove intellij`");
++}
++
++fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
++    let mut path = PathBuf::from(rustc_path);
++
++    if path.is_relative() {
++        match path.canonicalize() {
++            Ok(absolute_path) => {
++                println!("info: the rustc path was resolved to: `{}`", absolute_path.display());
++                path = absolute_path;
++            },
++            Err(err) => {
++                eprintln!("error: unable to get the absolute path of rustc ({})", err);
++                return Err(());
++            },
++        };
++    }
++
++    let path = path.join("compiler");
++    println!("info: looking for compiler sources at: {}", path.display());
++
++    if !path.exists() {
++        eprintln!("error: the given path does not exist");
++        return Err(());
++    }
++
++    if !path.is_dir() {
++        eprintln!("error: the given path is not a directory");
++        return Err(());
++    }
++
++    Ok(path)
++}
++
++fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo) -> Result<(), ()> {
++    let cargo_content = read_project_file(project.cargo_file)?;
++    let lib_content = read_project_file(project.lib_rs_file)?;
++
++    if inject_deps_into_manifest(rustc_source_dir, project.cargo_file, &cargo_content, &lib_content).is_err() {
++        eprintln!(
++            "error: unable to inject dependencies into {} with the Cargo file {}",
++            project.name, project.cargo_file
++        );
++        Err(())
++    } else {
++        Ok(())
++    }
++}
++
++/// `clippy_dev` expects to be executed in the root directory of Clippy. This function
++/// loads the given file or returns an error. Having it in this extra function ensures
++/// that the error message looks nice.
++fn read_project_file(file_path: &str) -> Result<String, ()> {
++    let path = Path::new(file_path);
++    if !path.exists() {
++        eprintln!("error: unable to find the file `{}`", file_path);
++        return Err(());
++    }
++
++    match fs::read_to_string(path) {
++        Ok(content) => Ok(content),
++        Err(err) => {
++            eprintln!("error: the file `{}` could not be read ({})", file_path, err);
++            Err(())
++        },
++    }
++}
++
++fn inject_deps_into_manifest(
++    rustc_source_dir: &Path,
++    manifest_path: &str,
++    cargo_toml: &str,
++    lib_rs: &str,
++) -> std::io::Result<()> {
++    // do not inject deps if we have already done so
++    if cargo_toml.contains(RUSTC_PATH_SECTION) {
++        eprintln!(
++            "warn: dependencies are already setup inside {}, skipping file",
++            manifest_path
++        );
++        return Ok(());
++    }
++
++    let extern_crates = lib_rs
++        .lines()
++        // only take dependencies starting with `rustc_`
++        .filter(|line| line.starts_with("extern crate rustc_"))
++        // we have something like "extern crate foo;", we only care about the "foo"
++        // extern crate rustc_middle;
++        //              ^^^^^^^^^^^^
++        .map(|s| &s[13..(s.len() - 1)]);
++
++    let new_deps = extern_crates.map(|dep| {
++        // format the dependencies that are going to be put inside the Cargo.toml
++        format!(
++            "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
++            dep = dep,
++            source_path = rustc_source_dir.display()
++        )
++    });
++
++    // format a new [dependencies]-block with the new deps we need to inject
++    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
++    new_deps.for_each(|dep_line| {
++        all_deps.push_str(&dep_line);
++    });
++    all_deps.push_str("\n[dependencies]\n");
++
++    // replace "[dependencies]" with
++    // [dependencies]
++    // dep1 = { path = ... }
++    // dep2 = { path = ... }
++    // etc
++    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
++
++    // println!("{}", new_manifest);
++    let mut file = File::create(manifest_path)?;
++    file.write_all(new_manifest.as_bytes())?;
++
++    println!("info: successfully setup dependencies inside {}", manifest_path);
++
++    Ok(())
++}
++
++pub fn remove_rustc_src() {
++    for project in CLIPPY_PROJECTS {
++        remove_rustc_src_from_project(project);
++    }
++}
++
++fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
++    let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) {
++        content
++    } else {
++        return false;
++    };
++    let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) {
++        section_start
++    } else {
++        println!(
++            "info: dependencies could not be found in `{}` for {}, skipping file",
++            project.cargo_file, project.name
++        );
++        return true;
++    };
++
++    let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) {
++        end_point
++    } else {
++        eprintln!(
++            "error: the end of the rustc dependencies section could not be found in `{}`",
++            project.cargo_file
++        );
++        return false;
++    };
++
++    cargo_content.replace_range(section_start..end_point, "");
++
++    match File::create(project.cargo_file) {
++        Ok(mut file) => {
++            file.write_all(cargo_content.as_bytes()).unwrap();
++            println!("info: successfully removed dependencies inside {}", project.cargo_file);
++            true
++        },
++        Err(err) => {
++            eprintln!(
++                "error: unable to open file `{}` to remove rustc dependencies for {} ({})",
++                project.cargo_file, project.name, err
++            );
++            false
++        },
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1e4dd103b88bfa708948c44ba7cd8a48ec1a346
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++pub mod git_hook;
++pub mod intellij;
++pub mod vscode;
++
++use std::path::Path;
++
++const CLIPPY_DEV_DIR: &str = "clippy_dev";
++
++/// This function verifies that the tool is being executed in the clippy directory.
++/// This is useful to ensure that setups only modify Clippys resources. The verification
++/// is done by checking that `clippy_dev` is a sub directory of the current directory.
++///
++/// It will print an error message and return `false` if the directory could not be
++/// verified.
++fn verify_inside_clippy_dir() -> bool {
++    let path = Path::new(CLIPPY_DEV_DIR);
++    if path.exists() && path.is_dir() {
++        true
++    } else {
++        eprintln!("error: unable to verify that the working directory is clippys directory");
++        false
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d59001b2c66afc49b6cfc7c8fa230f2bc7b50446
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,104 @@@
++use std::fs;
++use std::path::Path;
++
++use super::verify_inside_clippy_dir;
++
++const VSCODE_DIR: &str = ".vscode";
++const TASK_SOURCE_FILE: &str = "util/etc/vscode-tasks.json";
++const TASK_TARGET_FILE: &str = ".vscode/tasks.json";
++
++pub fn install_tasks(force_override: bool) {
++    if !check_install_precondition(force_override) {
++        return;
++    }
++
++    match fs::copy(TASK_SOURCE_FILE, TASK_TARGET_FILE) {
++        Ok(_) => {
++            println!("info: the task file can be removed with `cargo dev remove vscode-tasks`");
++            println!("vscode tasks successfully installed");
++        },
++        Err(err) => eprintln!(
++            "error: unable to copy `{}` to `{}` ({})",
++            TASK_SOURCE_FILE, TASK_TARGET_FILE, err
++        ),
++    }
++}
++
++fn check_install_precondition(force_override: bool) -> bool {
++    if !verify_inside_clippy_dir() {
++        return false;
++    }
++
++    let vs_dir_path = Path::new(VSCODE_DIR);
++    if vs_dir_path.exists() {
++        // verify the target will be valid
++        if !vs_dir_path.is_dir() {
++            eprintln!("error: the `.vscode` path exists but seems to be a file");
++            return false;
++        }
++
++        // make sure that we don't override any existing tasks by accident
++        let path = Path::new(TASK_TARGET_FILE);
++        if path.exists() {
++            if force_override {
++                return delete_vs_task_file(path);
++            }
++
++            eprintln!(
++                "error: there is already a `task.json` file inside the `{}` directory",
++                VSCODE_DIR
++            );
++            println!("info: use the `--force-override` flag to override the existing `task.json` file");
++            return false;
++        }
++    } else {
++        match fs::create_dir(vs_dir_path) {
++            Ok(_) => {
++                println!("info: created `{}` directory for clippy", VSCODE_DIR);
++            },
++            Err(err) => {
++                eprintln!(
++                    "error: the task target directory `{}` could not be created ({})",
++                    VSCODE_DIR, err
++                );
++            },
++        }
++    }
++
++    true
++}
++
++pub fn remove_tasks() {
++    let path = Path::new(TASK_TARGET_FILE);
++    if path.exists() {
++        if delete_vs_task_file(path) {
++            try_delete_vs_directory_if_empty();
++            println!("vscode tasks successfully removed");
++        }
++    } else {
++        println!("no vscode tasks were found");
++    }
++}
++
++fn delete_vs_task_file(path: &Path) -> bool {
++    if let Err(err) = fs::remove_file(path) {
++        eprintln!("error: unable to delete the existing `tasks.json` file ({})", err);
++        return false;
++    }
++
++    true
++}
++
++/// This function will try to delete the `.vscode` directory if it's empty.
++/// It may fail silently.
++fn try_delete_vs_directory_if_empty() {
++    let path = Path::new(VSCODE_DIR);
++    if path.read_dir().map_or(false, |mut iter| iter.next().is_none()) {
++        // The directory is empty. We just try to delete it but allow a silence
++        // fail as an empty `.vscode` directory is still valid
++        let _silence_result = fs::remove_dir(path);
++    } else {
++        // The directory is not empty or could not be read. Either way don't take
++        // any further actions
++    }
++}
index edf6c5f57a49766d44f8a353c55937ac0b015354,0000000000000000000000000000000000000000..db467c26f15466de263ddd1c5a00bac7b1f995e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,149 -1,0 +1,152 @@@
-                 l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
 +use crate::{
 +    gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list,
 +    replace_region_in_file, Lint, DOCS_LINK,
 +};
 +use std::path::Path;
 +
 +#[derive(Clone, Copy, PartialEq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +pub fn run(update_mode: UpdateMode) {
 +    let lint_list: Vec<Lint> = gather_all().collect();
 +
 +    let internal_lints = Lint::internal_lints(&lint_list);
 +    let deprecated_lints = Lint::deprecated_lints(&lint_list);
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let mut sorted_usable_lints = usable_lints.clone();
 +    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    let usable_lint_count = round_to_fifty(usable_lints.len());
 +
 +    let mut file_change = false;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("README.md"),
 +        &format!(
 +            r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
 +            DOCS_LINK
 +        ),
 +        "",
 +        true,
 +        update_mode == UpdateMode::Change,
 +        || {
 +            vec![format!(
 +                "[There are over {} lints included in this crate!]({})",
 +                usable_lint_count, DOCS_LINK
 +            )]
 +        },
 +    )
 +    .changed;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->",
 +        "<!-- end autogenerated links to lint list -->",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
 +    )
 +    .changed;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "begin deprecated lints",
 +        "end deprecated lints",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_deprecated(deprecated_lints.iter()),
 +    )
 +    .changed;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "begin register lints",
 +        "end register lints",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
 +    )
 +    .changed;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "begin lints modules",
 +        "end lints modules",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_modules_list(usable_lints.iter()),
 +    )
 +    .changed;
 +
 +    // Generate lists of lints in the clippy::all lint group
 +    file_change |= replace_region_in_file(
 +        Path::new("clippy_lints/src/lib.rs"),
 +        r#"store.register_group\(true, "clippy::all""#,
 +        r#"\]\);"#,
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || {
 +            // clippy::all should only include the following lint groups:
 +            let all_group_lints = usable_lints.iter().filter(|l| {
++                matches!(
++                    &*l.group,
++                    "correctness" | "suspicious" | "style" | "complexity" | "perf"
++                )
 +            });
 +
 +            gen_lint_group_list(all_group_lints)
 +        },
 +    )
 +    .changed;
 +
 +    // Generate the list of lints for all other lint groups
 +    for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
 +        file_change |= replace_region_in_file(
 +            Path::new("clippy_lints/src/lib.rs"),
 +            &format!("store.register_group\\(true, \"clippy::{}\"", lint_group),
 +            r#"\]\);"#,
 +            false,
 +            update_mode == UpdateMode::Change,
 +            || gen_lint_group_list(lints.iter()),
 +        )
 +        .changed;
 +    }
 +
 +    if update_mode == UpdateMode::Check && file_change {
 +        println!(
 +            "Not all lints defined properly. \
 +             Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +        );
 +        std::process::exit(1);
 +    }
 +}
 +
 +pub fn print_lints() {
 +    let lint_list: Vec<Lint> = gather_all().collect();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        if lint_group == "Deprecated" {
 +            continue;
 +        }
 +        println!("\n## {}", lint_group);
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {} lints", usable_lint_count);
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
index 48f2972ec58d1887c166ebdd6f1e6d043c3adb74,0000000000000000000000000000000000000000..42cf7547f519459a6cb470ed8a963a181490d7fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,41 @@@
- version = "0.1.54"
 +[package]
 +name = "clippy_lints"
 +# begin automatic update
++version = "0.1.55"
 +# 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"
++unicode-script = { version = "0.5.3", default-features = false }
 +semver = "0.11"
 +rustc-semver = "1.1.0"
 +# NOTE: cargo requires serde feat in its url dep
 +# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 +url = { version = "2.1.0", features = ["serde"] }
 +
 +[features]
 +deny-warnings = ["clippy_utils/deny-warnings"]
 +# build clippy with internal lints enabled, off by default
 +internal-lints = ["clippy_utils/internal-lints"]
 +metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index bc6eec0051a41d872c466b2a0a4f5becee8b5585,0000000000000000000000000000000000000000..a8c527fe2e353fd131defe916887f5fd8cdd6f05
mode 100644,000000..100644
--- /dev/null
@@@ -1,264 -1,0 +1,264 @@@
-     complexity,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method};
 +use clippy_utils::{higher, paths, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
 +    /// patterns.
 +    ///
 +    /// **Why is this bad?** These can be written as the shorter `a op= b`.
 +    ///
 +    /// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
 +    /// implementations that differ from the regular `Op` impl.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 0;
 +    /// // ...
 +    /// // Bad
 +    /// a = a + b;
 +    ///
 +    /// // Good
 +    /// a += b;
 +    /// ```
 +    pub ASSIGN_OP_PATTERN,
 +    style,
 +    "assigning the result of an operation on a variable to that same variable"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
 +    ///
 +    /// **Why is this bad?** Most likely these are bugs where one meant to write `a
 +    /// op= b`.
 +    ///
 +    /// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
 +    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
 +    /// If `a op= a op b` is really the correct behaviour it should be
 +    /// written as `a = a op a op b` as it's less confusing.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 2;
 +    /// // ...
 +    /// a += a + b;
 +    /// ```
 +    pub MISREFACTORED_ASSIGN_OP,
++    suspicious,
 +    "having a variable on both sides of an assign op"
 +}
 +
 +declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for AssignOps {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        match &expr.kind {
 +            hir::ExprKind::AssignOp(op, lhs, rhs) => {
 +                if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
 +                    if op.node != binop.node {
 +                        return;
 +                    }
 +                    // lhs op= l op r
 +                    if eq_expr_value(cx, lhs, l) {
 +                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
 +                    }
 +                    // lhs op= l commutative_op r
 +                    if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
 +                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
 +                    }
 +                }
 +            },
 +            hir::ExprKind::Assign(assignee, e, _) => {
 +                if let hir::ExprKind::Binary(op, l, r) = &e.kind {
 +                    let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
 +                        let ty = cx.typeck_results().expr_ty(assignee);
 +                        let rty = cx.typeck_results().expr_ty(rhs);
 +                        macro_rules! ops {
 +                            ($op:expr,
 +                             $cx:expr,
 +                             $ty:expr,
 +                             $rty:expr,
 +                             $($trait_name:ident),+) => {
 +                                match $op {
 +                                    $(hir::BinOpKind::$trait_name => {
 +                                        let [krate, module] = paths::OPS_MODULE;
 +                                        let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")];
 +                                        let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
 +                                            trait_id
 +                                        } else {
 +                                            return; // useless if the trait doesn't exist
 +                                        };
 +                                        // check that we are not inside an `impl AssignOp` of this exact operation
 +                                        let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
 +                                        if_chain! {
 +                                            if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
 +                                            if trait_ref.path.res.def_id() == trait_id;
 +                                            then { return; }
 +                                        }
 +                                        implements_trait($cx, $ty, trait_id, &[$rty])
 +                                    },)*
 +                                    _ => false,
 +                                }
 +                            }
 +                        }
 +                        if ops!(
 +                            op.node,
 +                            cx,
 +                            ty,
 +                            rty.into(),
 +                            Add,
 +                            Sub,
 +                            Mul,
 +                            Div,
 +                            Rem,
 +                            And,
 +                            Or,
 +                            BitAnd,
 +                            BitOr,
 +                            BitXor,
 +                            Shr,
 +                            Shl
 +                        ) {
 +                            span_lint_and_then(
 +                                cx,
 +                                ASSIGN_OP_PATTERN,
 +                                expr.span,
 +                                "manual implementation of an assign operation",
 +                                |diag| {
 +                                    if let (Some(snip_a), Some(snip_r)) =
 +                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
 +                                    {
 +                                        diag.span_suggestion(
 +                                            expr.span,
 +                                            "replace it with",
 +                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
 +                                            Applicability::MachineApplicable,
 +                                        );
 +                                    }
 +                                },
 +                            );
 +                        }
 +                    };
 +
 +                    let mut visitor = ExprVisitor {
 +                        assignee,
 +                        counter: 0,
 +                        cx,
 +                    };
 +
 +                    walk_expr(&mut visitor, e);
 +
 +                    if visitor.counter == 1 {
 +                        // a = a op b
 +                        if eq_expr_value(cx, assignee, l) {
 +                            lint(assignee, r);
 +                        }
 +                        // a = b commutative_op a
 +                        // Limited to primitive type as these ops are know to be commutative
 +                        if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
 +                            match op.node {
 +                                hir::BinOpKind::Add
 +                                | hir::BinOpKind::Mul
 +                                | hir::BinOpKind::And
 +                                | hir::BinOpKind::Or
 +                                | hir::BinOpKind::BitXor
 +                                | hir::BinOpKind::BitAnd
 +                                | hir::BinOpKind::BitOr => {
 +                                    lint(assignee, l);
 +                                },
 +                                _ => {},
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn lint_misrefactored_assign_op(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    op: hir::BinOp,
 +    rhs: &hir::Expr<'_>,
 +    assignee: &hir::Expr<'_>,
 +    rhs_other: &hir::Expr<'_>,
 +) {
 +    span_lint_and_then(
 +        cx,
 +        MISREFACTORED_ASSIGN_OP,
 +        expr.span,
 +        "variable appears on both sides of an assignment operation",
 +        |diag| {
 +            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
 +                let a = &sugg::Sugg::hir(cx, assignee, "..");
 +                let r = &sugg::Sugg::hir(cx, rhs, "..");
 +                let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
 +                diag.span_suggestion(
 +                    expr.span,
 +                    &format!(
 +                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
 +                        snip_a,
 +                        snip_a,
 +                        op.node.as_str(),
 +                        snip_r,
 +                        long
 +                    ),
 +                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
 +                    Applicability::MaybeIncorrect,
 +                );
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "or",
 +                    long,
 +                    Applicability::MaybeIncorrect, // snippet
 +                );
 +            }
 +        },
 +    );
 +}
 +
 +#[must_use]
 +fn is_commutative(op: hir::BinOpKind) -> bool {
 +    use rustc_hir::BinOpKind::{
 +        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
 +    };
 +    match op {
 +        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
 +        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
 +    }
 +}
 +
 +struct ExprVisitor<'a, 'tcx> {
 +    assignee: &'a hir::Expr<'a>,
 +    counter: u8,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if eq_expr_value(self.cx, self.assignee, expr) {
 +            self.counter += 1;
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 932cd58bf6259c5783ac210af6814232a746913b,0000000000000000000000000000000000000000..f272ed010a1b03df405044a4750f5b0afe4f64b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,643 -1,0 +1,643 @@@
-     style,
 +//! checks for attributes
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::match_panic_def_id;
 +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 +use if_chain::if_chain;
 +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::{Symbol, SymbolStr};
 +use semver::Version;
 +
 +static UNIX_SYSTEMS: &[&str] = &[
 +    "android",
 +    "dragonfly",
 +    "emscripten",
 +    "freebsd",
 +    "fuchsia",
 +    "haiku",
 +    "illumos",
 +    "ios",
 +    "l4re",
 +    "linux",
 +    "macos",
 +    "netbsd",
 +    "openbsd",
 +    "redox",
 +    "solaris",
 +    "vxworks",
 +];
 +
 +// NOTE: windows is excluded from the list because it's also a valid target family.
 +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for items annotated with `#[inline(always)]`,
 +    /// unless the annotated function is empty or simply panics.
 +    ///
 +    /// **Why is this bad?** While there are valid uses of this annotation (and once
 +    /// you know when to use it, by all means `allow` this lint), it's a common
 +    /// newbie-mistake to pepper one's code with it.
 +    ///
 +    /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
 +    /// measure if that additional function call really affects your runtime profile
 +    /// sufficiently to make up for the increase in compile time.
 +    ///
 +    /// **Known problems:** False positives, big time. This lint is meant to be
 +    /// deactivated by everyone doing serious performance work. This means having
 +    /// done the measurement.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// #[inline(always)]
 +    /// fn not_quite_hot_code(..) { ... }
 +    /// ```
 +    pub INLINE_ALWAYS,
 +    pedantic,
 +    "use of `#[inline(always)]`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `extern crate` and `use` items annotated with
 +    /// lint attributes.
 +    ///
 +    /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
 +    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
 +    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
 +    /// `extern crate` items with a `#[macro_use]` attribute.
 +    ///
 +    /// **Why is this bad?** Lint attributes have no effect on crate imports. Most
 +    /// likely a `!` was forgotten.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// // Bad
 +    /// #[deny(dead_code)]
 +    /// extern crate foo;
 +    /// #[forbid(dead_code)]
 +    /// use foo::bar;
 +    ///
 +    /// // Ok
 +    /// #[allow(unused_imports)]
 +    /// use foo::baz;
 +    /// #[allow(unused_imports)]
 +    /// #[macro_use]
 +    /// extern crate baz;
 +    /// ```
 +    pub USELESS_ATTRIBUTE,
 +    correctness,
 +    "use of lint attributes on `extern crate` items"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `#[deprecated]` annotations with a `since`
 +    /// field that is not a valid semantic version.
 +    ///
 +    /// **Why is this bad?** For checking the version of the deprecation, it must be
 +    /// a valid semver. Failing that, the contained information is useless.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// #[deprecated(since = "forever")]
 +    /// fn something_else() { /* ... */ }
 +    /// ```
 +    pub DEPRECATED_SEMVER,
 +    correctness,
 +    "use of `#[deprecated(since = \"x\")]` where x is not semver"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for empty lines after outer attributes
 +    ///
 +    /// **Why is this bad?**
 +    /// Most likely the attribute was meant to be an inner attribute using a '!'.
 +    /// If it was meant to be an outer attribute, then the following item
 +    /// should not be separated by empty lines.
 +    ///
 +    /// **Known problems:** Can cause false positives.
 +    ///
 +    /// From the clippy side it's difficult to detect empty lines between an attributes and the
 +    /// following item because empty lines and comments are not part of the AST. The parsing
 +    /// currently works for basic cases but is not perfect.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// // Good (as inner attribute)
 +    /// #![allow(dead_code)]
 +    ///
 +    /// fn this_is_fine() { }
 +    ///
 +    /// // Bad
 +    /// #[allow(dead_code)]
 +    ///
 +    /// fn not_quite_good_code() { }
 +    ///
 +    /// // Good (as outer attribute)
 +    /// #[allow(dead_code)]
 +    /// fn this_is_fine_too() { }
 +    /// ```
 +    pub EMPTY_LINE_AFTER_OUTER_ATTR,
 +    nursery,
 +    "empty line after outer attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
 +    ///
 +    /// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
 +    /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// Bad:
 +    /// ```rust
 +    /// #![deny(clippy::restriction)]
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #![deny(clippy::as_conversions)]
 +    /// ```
 +    pub BLANKET_CLIPPY_RESTRICTION_LINTS,
++    suspicious,
 +    "enabling the complete restriction group"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
 +    /// with `#[rustfmt::skip]`.
 +    ///
 +    /// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
 +    /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
 +    ///
 +    /// **Known problems:** This lint doesn't detect crate level inner attributes, because they get
 +    /// processed before the PreExpansionPass lints get executed. See
 +    /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
 +    ///
 +    /// **Example:**
 +    ///
 +    /// Bad:
 +    /// ```rust
 +    /// #[cfg_attr(rustfmt, rustfmt_skip)]
 +    /// fn main() { }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #[rustfmt::skip]
 +    /// fn main() { }
 +    /// ```
 +    pub DEPRECATED_CFG_ATTR,
 +    complexity,
 +    "usage of `cfg_attr(rustfmt)` instead of tool attributes"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for cfg attributes having operating systems used in target family position.
 +    ///
 +    /// **Why is this bad?** The configuration option will not be recognised and the related item will not be included
 +    /// by the conditional compilation engine.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// Bad:
 +    /// ```rust
 +    /// #[cfg(linux)]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// #[cfg(target_os = "linux")]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Or:
 +    /// ```rust
 +    /// #[cfg(unix)]
 +    /// fn conditional() { }
 +    /// ```
 +    /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
 +    pub MISMATCHED_TARGET_OS,
 +    correctness,
 +    "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
 +}
 +
 +declare_lint_pass!(Attributes => [
 +    INLINE_ALWAYS,
 +    DEPRECATED_SEMVER,
 +    USELESS_ATTRIBUTE,
 +    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Attributes {
 +    fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
 +        if let Some(items) = &attr.meta_item_list() {
 +            if let Some(ident) = attr.ident() {
 +                if is_lint_level(ident.name) {
 +                    check_clippy_lint_names(cx, ident.name, items);
 +                }
 +                if items.is_empty() || !attr.has_name(sym::deprecated) {
 +                    return;
 +                }
 +                for item in items {
 +                    if_chain! {
 +                        if let NestedMetaItem::MetaItem(mi) = &item;
 +                        if let MetaItemKind::NameValue(lit) = &mi.kind;
 +                        if mi.has_name(sym::since);
 +                        then {
 +                            check_semver(cx, item.span(), lit);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        if is_relevant_item(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, attrs);
 +        }
 +        match item.kind {
 +            ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
 +                let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
 +
 +                for attr in attrs {
 +                    if in_external_macro(cx.sess(), attr.span) {
 +                        return;
 +                    }
 +                    if let Some(lint_list) = &attr.meta_item_list() {
 +                        if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
 +                            // permit `unused_imports`, `deprecated`, `unreachable_pub`,
 +                            // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
 +                            // and `unused_imports` for `extern crate` items with `macro_use`
 +                            for lint in lint_list {
 +                                match item.kind {
 +                                    ItemKind::Use(..) => {
 +                                        if is_word(lint, sym!(unused_imports))
 +                                            || is_word(lint, sym::deprecated)
 +                                            || is_word(lint, sym!(unreachable_pub))
 +                                            || is_word(lint, sym!(unused))
 +                                            || extract_clippy_lint(lint).map_or(false, |s| s == "wildcard_imports")
 +                                            || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use")
 +                                        {
 +                                            return;
 +                                        }
 +                                    },
 +                                    ItemKind::ExternCrate(..) => {
 +                                        if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
 +                                            return;
 +                                        }
 +                                        if is_word(lint, sym!(unused_extern_crates)) {
 +                                            return;
 +                                        }
 +                                    },
 +                                    _ => {},
 +                                }
 +                            }
 +                            let line_span = first_line_of_span(cx, attr.span);
 +
 +                            if let Some(mut sugg) = snippet_opt(cx, line_span) {
 +                                if sugg.contains("#[") {
 +                                    span_lint_and_then(
 +                                        cx,
 +                                        USELESS_ATTRIBUTE,
 +                                        line_span,
 +                                        "useless lint attribute",
 +                                        |diag| {
 +                                            sugg = sugg.replacen("#[", "#![", 1);
 +                                            diag.span_suggestion(
 +                                                line_span,
 +                                                "if you just forgot a `!`, use",
 +                                                sugg,
 +                                                Applicability::MaybeIncorrect,
 +                                            );
 +                                        },
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if is_relevant_impl(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if is_relevant_trait(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +}
 +
 +/// Returns the lint name if it is clippy lint.
 +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
 +    if_chain! {
 +        if let Some(meta_item) = lint.meta_item();
 +        if meta_item.path.segments.len() > 1;
 +        if let tool_name = meta_item.path.segments[0].ident;
 +        if tool_name.name == sym::clippy;
 +        then {
 +            let lint_name = meta_item.path.segments.last().unwrap().ident.name;
 +            return Some(lint_name.as_str());
 +        }
 +    }
 +    None
 +}
 +
 +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
 +    for lint in items {
 +        if let Some(lint_name) = extract_clippy_lint(lint) {
 +            if lint_name == "restriction" && name != sym::allow {
 +                span_lint_and_help(
 +                    cx,
 +                    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +                    lint.span(),
 +                    "restriction lints are not meant to be all enabled",
 +                    None,
 +                    "try enabling only the lints you really need",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if let ItemKind::Fn(_, _, eid) = item.kind {
 +        is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
 +    } else {
 +        true
 +    }
 +}
 +
 +fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
 +    match item.kind {
 +        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
 +    match item.kind {
 +        TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
 +        TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
 +            is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
 +    block.stmts.first().map_or(
 +        block
 +            .expr
 +            .as_ref()
 +            .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
 +        |stmt| match &stmt.kind {
 +            StmtKind::Local(_) => true,
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
 +            StmtKind::Item(_) => false,
 +        },
 +    )
 +}
 +
 +fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
 +    match &expr.kind {
 +        ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
 +        ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
 +        ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
 +        ExprKind::Call(path_expr, _) => {
 +            if let ExprKind::Path(qpath) = &path_expr.kind {
 +                typeck_results
 +                    .qpath_res(qpath, path_expr.hir_id)
 +                    .opt_def_id()
 +                    .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id))
 +            } else {
 +                true
 +            }
 +        },
 +        _ => true,
 +    }
 +}
 +
 +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
 +    if span.from_expansion() {
 +        return;
 +    }
 +
 +    for attr in attrs {
 +        if let Some(values) = attr.meta_item_list() {
 +            if values.len() != 1 || !attr.has_name(sym::inline) {
 +                continue;
 +            }
 +            if is_word(&values[0], sym::always) {
 +                span_lint(
 +                    cx,
 +                    INLINE_ALWAYS,
 +                    attr.span,
 +                    &format!(
 +                        "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
 +                        name
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) {
 +    if let LitKind::Str(is, _) = lit.kind {
 +        if Version::parse(&is.as_str()).is_ok() {
 +            return;
 +        }
 +    }
 +    span_lint(
 +        cx,
 +        DEPRECATED_SEMVER,
 +        span,
 +        "the since field must contain a semver-compliant version",
 +    );
 +}
 +
 +fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 +    if let NestedMetaItem::MetaItem(mi) = &nmi {
 +        mi.is_word() && mi.has_name(expected)
 +    } else {
 +        false
 +    }
 +}
 +
 +declare_lint_pass!(EarlyAttributes => [
 +    DEPRECATED_CFG_ATTR,
 +    MISMATCHED_TARGET_OS,
 +    EMPTY_LINE_AFTER_OUTER_ATTR,
 +]);
 +
 +impl EarlyLintPass for EarlyAttributes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +        check_empty_line_after_outer_attr(cx, item);
 +    }
 +
 +    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
 +        check_deprecated_cfg_attr(cx, attr);
 +        check_mismatched_target_os(cx, attr);
 +    }
 +}
 +
 +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +    for attr in &item.attrs {
 +        let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr
 +        } else {
 +            return;
 +        };
 +
 +        if attr.style == AttrStyle::Outer {
 +            if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
 +                return;
 +            }
 +
 +            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt());
 +            let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt());
 +
 +            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
 +                let lines = snippet.split('\n').collect::<Vec<_>>();
 +                let lines = without_block_comments(lines);
 +
 +                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
 +                    span_lint(
 +                        cx,
 +                        EMPTY_LINE_AFTER_OUTER_ATTR,
 +                        begin_of_attr_to_item,
 +                        "found an empty line after an outer attribute. \
 +                        Perhaps you forgot to add a `!` to make it an inner attribute?",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
 +    if_chain! {
 +        // check cfg_attr
 +        if attr.has_name(sym::cfg_attr);
 +        if let Some(items) = attr.meta_item_list();
 +        if items.len() == 2;
 +        // check for `rustfmt`
 +        if let Some(feature_item) = items[0].meta_item();
 +        if feature_item.has_name(sym::rustfmt);
 +        // check for `rustfmt_skip` and `rustfmt::skip`
 +        if let Some(skip_item) = &items[1].meta_item();
 +        if skip_item.has_name(sym!(rustfmt_skip)) ||
 +            skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
 +        // Only lint outer attributes, because custom inner attributes are unstable
 +        // Tracking issue: https://github.com/rust-lang/rust/issues/54726
 +        if let AttrStyle::Outer = attr.style;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DEPRECATED_CFG_ATTR,
 +                attr.span,
 +                "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
 +                "use",
 +                "#[rustfmt::skip]".to_string(),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
 +    fn find_os(name: &str) -> Option<&'static str> {
 +        UNIX_SYSTEMS
 +            .iter()
 +            .chain(NON_UNIX_SYSTEMS.iter())
 +            .find(|&&os| os == name)
 +            .copied()
 +    }
 +
 +    fn is_unix(name: &str) -> bool {
 +        UNIX_SYSTEMS.iter().any(|&os| os == name)
 +    }
 +
 +    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
 +        let mut mismatched = Vec::new();
 +
 +        for item in items {
 +            if let NestedMetaItem::MetaItem(meta) = item {
 +                match &meta.kind {
 +                    MetaItemKind::List(list) => {
 +                        mismatched.extend(find_mismatched_target_os(list));
 +                    },
 +                    MetaItemKind::Word => {
 +                        if_chain! {
 +                            if let Some(ident) = meta.ident();
 +                            if let Some(os) = find_os(&*ident.name.as_str());
 +                            then {
 +                                mismatched.push((os, ident.span));
 +                            }
 +                        }
 +                    },
 +                    MetaItemKind::NameValue(..) => {},
 +                }
 +            }
 +        }
 +
 +        mismatched
 +    }
 +
 +    if_chain! {
 +        if attr.has_name(sym::cfg);
 +        if let Some(list) = attr.meta_item_list();
 +        let mismatched = find_mismatched_target_os(&list);
 +        if !mismatched.is_empty();
 +        then {
 +            let mess = "operating system used in target family position";
 +
 +            span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
 +                // Avoid showing the unix suggestion multiple times in case
 +                // we have more than one mismatch for unix-like systems
 +                let mut unix_suggested = false;
 +
 +                for (os, span) in mismatched {
 +                    let sugg = format!("target_os = \"{}\"", os);
 +                    diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
 +
 +                    if !unix_suggested && is_unix(os) {
 +                        diag.help("did you mean `unix`?");
 +                        unix_suggested = true;
 +                    }
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +fn is_lint_level(symbol: Symbol) -> bool {
 +    matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid)
 +}
index b26ef33e056981ccd084623f97c13356dadec84b,0000000000000000000000000000000000000000..8eb94f3c28e447943dcbc5702085a4c1bba9614e
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,76 @@@
- use clippy_utils::diagnostics::span_lint;
++use clippy_utils::{diagnostics::span_lint, is_test_module_or_function};
 +use rustc_data_structures::fx::FxHashSet;
- use rustc_hir::{Pat, PatKind};
++use rustc_hir::{Item, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for usage of blacklisted names for variables, such
 +    /// as `foo`.
 +    ///
 +    /// **Why is this bad?** These names are usually placeholder names and should be
 +    /// avoided.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let foo = 3.14;
 +    /// ```
 +    pub BLACKLISTED_NAME,
 +    style,
 +    "usage of a blacklisted/placeholder name"
 +}
 +
 +#[derive(Clone, Debug)]
 +pub struct BlacklistedName {
 +    blacklist: FxHashSet<String>,
++    test_modules_deep: u32,
 +}
 +
 +impl BlacklistedName {
 +    pub fn new(blacklist: FxHashSet<String>) -> Self {
-         Self { blacklist }
++        Self {
++            blacklist,
++            test_modules_deep: 0,
++        }
++    }
++
++    fn in_test_module(&self) -> bool {
++        self.test_modules_deep != 0
 +    }
 +}
 +
 +impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
 +
 +impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
++    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        if is_test_module_or_function(cx.tcx, item) {
++            self.test_modules_deep = self.test_modules_deep.saturating_add(1);
++        }
++    }
++
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
++        // Check whether we are under the `test` attribute.
++        if self.in_test_module() {
++            return;
++        }
++
 +        if let PatKind::Binding(.., ident, _) = pat.kind {
 +            if self.blacklist.contains(&ident.name.to_string()) {
 +                span_lint(
 +                    cx,
 +                    BLACKLISTED_NAME,
 +                    ident.span,
 +                    &format!("use of a blacklisted/placeholder name `{}`", ident.name),
 +                );
 +            }
 +        }
 +    }
++
++    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        if is_test_module_or_function(cx.tcx, item) {
++            self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
++        }
++    }
 +}
index 877ae002d36e9de67bb360716112cf266929ae4d,0000000000000000000000000000000000000000..4f7ffdcdfb499ac972c9dbd9eeced0153b492ddc
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,94 @@@
- use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::match_type;
- use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
++use clippy_utils::visitors::LocalUsedVisitor;
++use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
- use rustc_span::Symbol;
++use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, UintTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
-             if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for naive byte counts
 +    ///
 +    /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
 +    /// crate has methods to count your bytes faster, especially for large slices.
 +    ///
 +    /// **Known problems:** If you have predominantly small slices, the
 +    /// `bytecount::count(..)` method may actually be slower. However, if you can
 +    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
 +    /// faster in those cases.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// # let vec = vec![1_u8];
 +    /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
 +    /// ```
 +    pub NAIVE_BYTECOUNT,
 +    pedantic,
 +    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
 +}
 +
 +declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ByteCount {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
-             if count_args.len() == 1;
-             if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
++            if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
 +            if count.ident.name == sym!(count);
-             if filter_args.len() == 2;
-             if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
++            if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
 +            if filter.ident.name == sym!(filter);
-             if body.params.len() == 1;
-             if let Some(argname) = get_pat_name(body.params[0].pat);
++            if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
 +            let body = cx.tcx.hir().body(body_id);
-                        cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
++            if let [param] = body.params;
++            if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
 +            if let ExprKind::Binary(ref op, l, r) = body.value.kind;
 +            if op.node == BinOpKind::Eq;
 +            if match_type(cx,
-                 let needle = match get_path_name(l) {
-                     Some(name) if check_arg(name, argname, r) => r,
-                     _ => match get_path_name(r) {
-                         Some(name) if check_arg(name, argname, l) => l,
-                         _ => { return; }
-                     }
-                 };
-                 if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
-                     return;
-                 }
++                       cx.typeck_results().expr_ty(filter_recv).peel_refs(),
 +                       &paths::SLICE_ITER);
++            let operand_is_arg = |expr| {
++                let expr = peel_ref_operators(cx, remove_blocks(expr));
++                path_to_local_id(expr, arg_id)
++            };
++            let needle = if operand_is_arg(l) {
++                r
++            } else if operand_is_arg(r) {
++                l
++            } else {
++                return;
++            };
++            if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
++            if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
 +            then {
-                         filter_args[0].kind {
 +                let haystack = if let ExprKind::MethodCall(path, _, args, _) =
-                         &filter_args[0]
++                        filter_recv.kind {
 +                    let p = path.ident.name;
 +                    if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
 +                        &args[0]
 +                    } else {
-                     &filter_args[0]
++                        &filter_recv
 +                    }
 +                } else {
- fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool {
-     name == arg && !contains_name(name, needle)
- }
- fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> {
-     match expr.kind {
-         ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => {
-             get_path_name(e)
-         },
-         ExprKind::Block(b, _) => {
-             if b.stmts.is_empty() {
-                 b.expr.as_ref().and_then(|p| get_path_name(p))
-             } else {
-                 None
-             }
-         },
-         ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
-         _ => None,
-     }
- }
++                    &filter_recv
 +                };
 +                let mut applicability = Applicability::MaybeIncorrect;
 +                span_lint_and_sugg(
 +                    cx,
 +                    NAIVE_BYTECOUNT,
 +                    expr.span,
 +                    "you appear to be counting bytes the naive way",
 +                    "consider using the bytecount crate",
 +                    format!("bytecount::count({}, {})",
 +                            snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
 +                            snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
 +                    applicability,
 +                );
 +            }
 +        };
 +    }
 +}
index ab22578abd674efec12970050170789ec492d5d9,0000000000000000000000000000000000000000..a6c3a5b0e83c466bc8b98f14e55ca7070a876da1
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,164 @@@
- use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::visitors::LocalUsedVisitor;
- use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
++use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_hir::LangItem::OptionNone;
- use rustc_middle::ty::TypeckResults;
++use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
-         if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{MultiSpan, Span};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
 +    /// without adding any branches.
 +    ///
 +    /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
 +    /// cases where merging would most likely make the code more readable.
 +    ///
 +    /// **Why is this bad?** It is unnecessarily verbose and complex.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(n) => match n {
 +    ///             Ok(n) => n,
 +    ///             _ => return,
 +    ///         }
 +    ///         None => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(Ok(n)) => n,
 +    ///         _ => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    pub COLLAPSIBLE_MATCH,
 +    style,
 +    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
 +}
 +
 +declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +        if let ExprKind::Match(_expr, arms, _source) = expr.kind {
 +            if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
 +                for arm in arms {
 +                    check_arm(arm, wild_arm, cx);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
 +    let expr = strip_singleton_blocks(arm.body);
 +    if_chain! {
 +        if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
 +        // the outer arm pattern and the inner match
 +        if expr_in.span.ctxt() == arm.pat.span.ctxt();
 +        // there must be no more than two arms in the inner match for this lint
 +        if arms_inner.len() == 2;
 +        // no if guards on the inner match
 +        if arms_inner.iter().all(|arm| arm.guard.is_none());
 +        // match expression must be a local binding
 +        // match <local> { .. }
- /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
- /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
- fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
-     loop {
-         match expr.kind {
-             ExprKind::AddrOf(_, _, e) => expr = e,
-             ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
-             _ => break,
-         }
-     }
-     expr
- }
++        if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
 +        // one of the branches must be "wild-like"
 +        if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
 +        let (wild_inner_arm, non_wild_inner_arm) =
 +            (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
 +        if !pat_contains_or(non_wild_inner_arm.pat);
 +        // the binding must come from the pattern of the containing match arm
 +        // ..<local>.. => match <local> { .. }
 +        if let Some(binding_span) = find_pat_binding(arm.pat, binding_id);
 +        // the "wild-like" branches must be equal
 +        if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
 +        // the binding must not be used in the if guard
 +        let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
 +        if match arm.guard {
 +            None => true,
 +            Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
 +        };
 +        // ...or anywhere in the inner match
 +        if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm));
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                COLLAPSIBLE_MATCH,
 +                expr.span,
 +                "unnecessary nested match",
 +                |diag| {
 +                    let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]);
 +                    help_span.push_span_label(binding_span, "replace this binding".into());
 +                    help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into());
 +                    diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    while let ExprKind::Block(block, _) = expr.kind {
 +        match (block.stmts, block.expr) {
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
 +                _ => break,
 +            },
 +            ([], Some(e)) => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +/// A "wild-like" pattern is wild ("_") or `None`.
 +/// For this lint to apply, both the outer and inner match expressions
 +/// must have "wild-like" branches that can be combined.
 +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    if arm.guard.is_some() {
 +        return false;
 +    }
 +    match arm.pat.kind {
 +        PatKind::Binding(..) | PatKind::Wild => true,
 +        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        _ => false,
 +    }
 +}
 +
 +fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
 +    let mut span = None;
 +    pat.walk_short(|p| match &p.kind {
 +        // ignore OR patterns
 +        PatKind::Or(_) => false,
 +        PatKind::Binding(_bm, _, _ident, _) => {
 +            let found = p.hir_id == hir_id;
 +            if found {
 +                span = Some(p.span);
 +            }
 +            !found
 +        },
 +        _ => true,
 +    });
 +    span
 +}
 +
 +fn pat_contains_or(pat: &Pat<'_>) -> bool {
 +    let mut result = false;
 +    pat.walk(|p| {
 +        let is_or = matches!(p.kind, PatKind::Or(_));
 +        result |= is_or;
 +        !is_or
 +    });
 +    result
 +}
index 7a53d390bb45f00159ce682c485377c7f80b6504,0000000000000000000000000000000000000000..947479db8f5d78e77fdc1b6fa60a08357fb609fa
mode 100644,000000..100644
--- /dev/null
@@@ -1,280 -1,0 +1,279 @@@
- use rustc_middle::lint::in_external_macro;
 +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};
-                 if !in_external_macro(cx.tcx.sess, expr.span);
 +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_macro(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 759f7d4062d448ba64e3218b6435c65841fb864a,0000000000000000000000000000000000000000..a125376bffa9fa405089ba1cc483e3543496aee9
mode 100644,000000..100644
--- /dev/null
@@@ -1,236 -1,0 +1,238 @@@
- use rustc_lint::{LateContext, LateLintPass};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
 +    Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
 +};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::{
 +    hir::map::Map,
++    lint::in_external_macro,
 +    ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
 +    /// inference.
 +    ///
 +    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
 +    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
 +    /// floating type is bound to `f64`.
 +    ///
 +    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
 +    ///
 +    /// **Why is this bad?** For those who are very careful about types, default numeric fallback
 +    /// can be a pitfall that cause unexpected runtime behavior.
 +    ///
 +    /// **Known problems:** This lint can only be allowed at the function level or above.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let i = 10;
 +    /// let f = 1.23;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let i = 10i32;
 +    /// let f = 1.23f64;
 +    /// ```
 +    pub DEFAULT_NUMERIC_FALLBACK,
 +    restriction,
 +    "usage of unconstrained numeric literals which may cause default numeric fallback."
 +}
 +
 +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 +
 +impl LateLintPass<'_> for DefaultNumericFallback {
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let mut visitor = NumericFallbackVisitor::new(cx);
 +        visitor.visit_body(body);
 +    }
 +}
 +
 +struct NumericFallbackVisitor<'a, 'tcx> {
 +    /// Stack manages type bound of exprs. The top element holds current expr type.
 +    ty_bounds: Vec<TyBound<'tcx>>,
 +
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            ty_bounds: vec![TyBound::Nothing],
 +            cx,
 +        }
 +    }
 +
 +    /// Check whether a passed literal has potential to cause fallback or not.
 +    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
 +        if_chain! {
++                if !in_external_macro(self.cx.sess(), lit.span);
 +                if let Some(ty_bound) = self.ty_bounds.last();
 +                if matches!(lit.node,
 +                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
 +                if !ty_bound.is_integral();
 +                then {
 +                    let suffix = match lit_ty.kind() {
 +                        ty::Int(IntTy::I32) => "i32",
 +                        ty::Float(FloatTy::F64) => "f64",
 +                        // Default numeric fallback never results in other types.
 +                        _ => return,
 +                    };
 +
 +                    let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
 +                    span_lint_and_sugg(
 +                        self.cx,
 +                        DEFAULT_NUMERIC_FALLBACK,
 +                        lit.span,
 +                        "default numeric fallback might occur",
 +                        "consider adding suffix",
 +                        sugg,
 +                        Applicability::MaybeIncorrect,
 +                    );
 +                }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        match &expr.kind {
 +            ExprKind::Call(func, args) => {
 +                if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
 +                    for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
 +                        // Push found arg type, then visit arg.
 +                        self.ty_bounds.push(TyBound::Ty(bound));
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
 +            },
 +
 +            ExprKind::MethodCall(_, _, args, _) => {
 +                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
 +                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
 +                    for (expr, bound) in iter::zip(*args, fn_sig.inputs()) {
 +                        self.ty_bounds.push(TyBound::Ty(bound));
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
 +            },
 +
 +            ExprKind::Struct(_, fields, base) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                if_chain! {
 +                    if let Some(adt_def) = ty.ty_adt_def();
 +                    if adt_def.is_struct();
 +                    if let Some(variant) = adt_def.variants.iter().next();
 +                    then {
 +                        let fields_def = &variant.fields;
 +
 +                        // Push field type then visit each field expr.
 +                        for field in fields.iter() {
 +                            let bound =
 +                                fields_def
 +                                    .iter()
 +                                    .find_map(|f_def| {
 +                                        if f_def.ident == field.ident
 +                                            { Some(self.cx.tcx.type_of(f_def.did)) }
 +                                        else { None }
 +                                    });
 +                            self.ty_bounds.push(bound.into());
 +                            self.visit_expr(field.expr);
 +                            self.ty_bounds.pop();
 +                        }
 +
 +                        // Visit base with no bound.
 +                        if let Some(base) = base {
 +                            self.ty_bounds.push(TyBound::Nothing);
 +                            self.visit_expr(base);
 +                            self.ty_bounds.pop();
 +                        }
 +                        return;
 +                    }
 +                }
 +            },
 +
 +            ExprKind::Lit(lit) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                self.check_lit(lit, ty);
 +                return;
 +            },
 +
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if local.ty.is_some() {
 +                    self.ty_bounds.push(TyBound::Any);
 +                } else {
 +                    self.ty_bounds.push(TyBound::Nothing);
 +                }
 +            },
 +
 +            _ => self.ty_bounds.push(TyBound::Nothing),
 +        }
 +
 +        walk_stmt(self, stmt);
 +        self.ty_bounds.pop();
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
 +    let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
 +    // We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs.
 +    match node_ty.kind() {
 +        ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
 +        ty::FnPtr(fn_sig) => Some(*fn_sig),
 +        _ => None,
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum TyBound<'tcx> {
 +    Any,
 +    Ty(Ty<'tcx>),
 +    Nothing,
 +}
 +
 +impl<'tcx> TyBound<'tcx> {
 +    fn is_integral(self) -> bool {
 +        match self {
 +            TyBound::Any => true,
 +            TyBound::Ty(t) => t.is_integral(),
 +            TyBound::Nothing => false,
 +        }
 +    }
 +}
 +
 +impl<'tcx> From<Option<Ty<'tcx>>> for TyBound<'tcx> {
 +    fn from(v: Option<Ty<'tcx>>) -> Self {
 +        match v {
 +            Some(t) => TyBound::Ty(t),
 +            None => TyBound::Nothing,
 +        }
 +    }
 +}
index 04f3d77464f98aa764a9549279bbb9d0e63c741c,0000000000000000000000000000000000000000..2933fbc93418add393c3e579405cf233845b6d1b
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,162 @@@
-     "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items"
 +/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
 +/// enables the simple extraction of the metadata without changing the current deprecation
 +/// declaration.
 +pub struct ClippyDeprecatedLint;
 +
 +macro_rules! declare_deprecated_lint {
 +    { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => {
 +        $(#[$attr])*
 +        #[allow(dead_code)]
 +        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {};
 +    }
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
 +    /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
 +    pub SHOULD_ASSERT_EQ,
 +    "`assert!()` will be more flexible with RFC 2011"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than
 +    /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
 +    pub EXTEND_FROM_SLICE,
 +    "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's
 +    /// an infinite iterator, which is better expressed by `iter::repeat`,
 +    /// but the method has been removed for `Iterator::step_by` which panics
 +    /// if given a zero
 +    pub RANGE_STEP_BY_ZERO,
 +    "`iterator.step_by(0)` panics nowadays"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good
 +    /// stable alternatives. `Vec::as_slice` has now been stabilized.
 +    pub UNSTABLE_AS_SLICE,
 +    "`Vec::as_slice` has been stabilized in 1.7"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good
 +    /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
 +    pub UNSTABLE_AS_MUT_SLICE,
 +    "`Vec::as_mut_slice` has been stabilized in 1.7"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting
 +    /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
 +    /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
 +    /// cast_ptr_alignment and transmute_ptr_to_ptr.
 +    pub MISALIGNED_TRANSMUTE,
 +    "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy.
 +    /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
 +    /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
 +    pub ASSIGN_OPS,
 +    "using compound assignment operators (e.g., `+=`) is harmless"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** The original rule will only lint for `if let`. After
 +    /// making it support to lint `match`, naming as `if let` is not suitable for it.
 +    /// So, this lint is deprecated.
 +    pub IF_LET_REDUNDANT_PATTERN_MATCHING,
 +    "this lint has been changed to redundant_pattern_matching"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint used to suggest replacing `let mut vec =
 +    /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
 +    /// replacement has very different performance characteristics so the lint is
 +    /// deprecated.
 +    pub UNSAFE_VECTOR_INITIALIZATION,
 +    "the replacement suggested by this lint had substantially different behavior"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc.
 +    pub UNUSED_COLLECT,
 +    "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** Associated-constants are now preferred.
 +    pub REPLACE_CONSTS,
 +    "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** The regex! macro does not exist anymore.
 +    pub REGEX_MACRO,
 +    "the regex! macro has been removed from the regex crate in 2018"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
 +    /// more specific lint.
 +    pub FIND_MAP,
 +    "this lint has been replaced by `manual_find_map`, a more specific lint"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a
 +    /// more specific lint.
 +    pub FILTER_MAP,
 +    "this lint has been replaced by `manual_filter_map`, a more specific lint"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which
 +    /// enables the `enum_variant_names` lint for public items.
 +    /// ```
 +    pub PUB_ENUM_VARIANT_NAMES,
-     "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items"
++    "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
 +}
 +
 +declare_deprecated_lint! {
 +    /// **What it does:** Nothing. This lint has been deprecated.
 +    ///
 +    /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which
 +    /// enables the `wrong_self_conversion` lint for public items.
 +    pub WRONG_PUB_SELF_CONVENTION,
++    "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
 +}
index 729941f345a9217ad6b6304062965b3b9052b5b0,0000000000000000000000000000000000000000..3ac20fd9849eef656fb2cccb4ea2f7f53c426fb7
mode 100644,000000..100644
--- /dev/null
@@@ -1,427 -1,0 +1,424 @@@
-             match block.rules {
-                 BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => {
-                     self.has_unsafe = true;
-                 },
-                 _ => {},
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, is_copy};
 +use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
 +use if_chain::if_chain;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
 +use rustc_hir::{
 +    BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// **Why is this bad?** The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// **Why is this bad?** The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// **Why is this bad?** To avoid surprising behaviour, these traits should
 +    /// agree and the behaviour of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// **Why is this bad?** Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let attrs = cx.tcx.hir().attrs(item.hir_id());
 +            let is_automatically_derived = is_automatically_derived(attrs);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, def_id, &paths::HASH);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
 +    let copy_id = match cx.tcx.lang_items().copy_trait() {
 +        Some(id) => id,
 +        None => return,
 +    };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
 +    trait_ref: &TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
 +        cx.tcx.hir().expect_item(hir_id)
 +    }
 +
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did.as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did)
 +            .iter()
 +            .map(|imp_did| item_from_def_id(cx, *imp_did))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if let Unsafety::Unsafe = header.unsafety;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, span, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
++            if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
++                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +}
index ded0a0fff54b24573df89db5978904dbb71d2d1b,0000000000000000000000000000000000000000..aa1a609afedc0b91edfac0fbd4e3720d327f423b
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,95 @@@
- use rustc_hir::Expr;
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::fn_def_id;
 +
 +use rustc_data_structures::fx::FxHashSet;
-     /// **Known problems:** Currently, you must write each function as a
-     /// fully-qualified path. This lint doesn't support aliases or reexported
-     /// names; be aware that many types in `std` are actually reexports.
-     ///
-     /// For example, if you want to disallow `Duration::as_secs`, your clippy.toml
-     /// configuration would look like
-     /// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
-     /// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
++use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Symbol;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Denies the configured methods and functions in clippy.toml
 +    ///
 +    /// **Why is this bad?** Some methods are undesirable in certain contexts,
 +    /// and it's beneficial to lint for them as needed.
 +    ///
-     /// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
++    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
-             let func_path = cx.get_def_path(def_id);
-             if self.disallowed.contains(&func_path) {
++    /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// // Example code where clippy issues a warning
 +    /// let xs = vec![1, 2, 3, 4];
 +    /// xs.leak(); // Vec::leak is disallowed in the config.
 +    ///
 +    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // Example code which does not raise clippy warning
 +    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
 +    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
 +    /// ```
 +    pub DISALLOWED_METHOD,
 +    nursery,
 +    "use of a disallowed method call"
 +}
 +
 +#[derive(Clone, Debug)]
 +pub struct DisallowedMethod {
 +    disallowed: FxHashSet<Vec<Symbol>>,
++    def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
 +}
 +
 +impl DisallowedMethod {
 +    pub fn new(disallowed: &FxHashSet<String>) -> Self {
 +        Self {
 +            disallowed: disallowed
 +                .iter()
 +                .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
 +                .collect(),
++            def_ids: FxHashSet::default(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
++    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
++        for path in &self.disallowed {
++            let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
++            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
++            {
++                self.def_ids.insert((id, path.clone()));
++            }
++        }
++    }
++
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some(def_id) = fn_def_id(cx, expr) {
++            if self.def_ids.iter().any(|(id, _)| def_id == *id) {
++                let func_path = cx.get_def_path(def_id);
 +                let func_path_string = func_path
 +                    .into_iter()
 +                    .map(Symbol::to_ident_string)
 +                    .collect::<Vec<_>>()
 +                    .join("::");
 +
 +                span_lint(
 +                    cx,
 +                    DISALLOWED_METHOD,
 +                    expr.span,
 +                    &format!("use of a disallowed method `{}`", func_path_string),
 +                );
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12c525634c51d77b348c9612564fa1c27ba14855
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_lint::{EarlyContext, EarlyLintPass, Level};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use unicode_script::{Script, UnicodeScript};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of unicode scripts other than those explicitly allowed
++    /// by the lint config.
++    ///
++    /// This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`.
++    /// It also ignores the `Common` script type.
++    /// While configuring, be sure to use official script name [aliases] from
++    /// [the list of supported scripts][supported_scripts].
++    ///
++    /// See also: [`non_ascii_idents`].
++    ///
++    /// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases
++    /// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html
++    ///
++    /// **Why is this bad?** It may be not desired to have many different scripts for
++    /// identifiers in the codebase.
++    ///
++    /// Note that if you only want to allow plain English, you might want to use
++    /// built-in [`non_ascii_idents`] lint instead.
++    ///
++    /// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Assuming that `clippy.toml` contains the following line:
++    /// // allowed-locales = ["Latin", "Cyrillic"]
++    /// let counter = 10; // OK, latin is allowed.
++    /// let счётчик = 10; // OK, cyrillic is allowed.
++    /// let zähler = 10; // OK, it's still latin.
++    /// let カウンタ = 10; // Will spawn the lint.
++    /// ```
++    pub DISALLOWED_SCRIPT_IDENTS,
++    restriction,
++    "usage of non-allowed Unicode scripts"
++}
++
++#[derive(Clone, Debug)]
++pub struct DisallowedScriptIdents {
++    whitelist: FxHashSet<Script>,
++}
++
++impl DisallowedScriptIdents {
++    pub fn new(whitelist: &[String]) -> Self {
++        let whitelist = whitelist
++            .iter()
++            .map(String::as_str)
++            .filter_map(Script::from_full_name)
++            .collect();
++        Self { whitelist }
++    }
++}
++
++impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
++
++impl EarlyLintPass for DisallowedScriptIdents {
++    fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
++        // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
++        // https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs
++
++        let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow;
++        if !check_disallowed_script_idents {
++            return;
++        }
++
++        let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock();
++        // Sort by `Span` so that error messages make sense with respect to the
++        // order of identifier locations in the code.
++        let mut symbols: Vec<_> = symbols.iter().collect();
++        symbols.sort_unstable_by_key(|k| k.1);
++
++        for (symbol, &span) in &symbols {
++            // Note: `symbol.as_str()` is an expensive operation, thus should not be called
++            // more than once for a single symbol.
++            let symbol_str = symbol.as_str();
++            if symbol_str.is_ascii() {
++                continue;
++            }
++
++            for c in symbol_str.chars() {
++                // We want to iterate through all the scripts associated with this character
++                // and check whether at least of one scripts is in the whitelist.
++                let forbidden_script = c
++                    .script_extension()
++                    .iter()
++                    .find(|script| !self.whitelist.contains(script));
++                if let Some(script) = forbidden_script {
++                    span_lint(
++                        cx,
++                        DISALLOWED_SCRIPT_IDENTS,
++                        span,
++                        &format!(
++                            "identifier `{}` has a Unicode script that is not allowed by configuration: {}",
++                            symbol_str,
++                            script.full_name()
++                        ),
++                    );
++                    // We don't want to spawn warning multiple times over a single identifier.
++                    break;
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4a88c6324ebfdac3958c208b1d2251194deb6e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,126 @@@
++use clippy_utils::diagnostics::span_lint;
++
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::{
++    def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{Span, Symbol};
++
++declare_clippy_lint! {
++    /// **What it does:** Denies the configured types in clippy.toml.
++    ///
++    /// **Why is this bad?** Some types are undesirable in certain contexts.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// N.B. There is no way to ban primitive types.
++    ///
++    /// **Example:**
++    ///
++    /// An example clippy.toml configuration:
++    /// ```toml
++    /// # clippy.toml
++    /// disallowed-methods = ["std::collections::BTreeMap"]
++    /// ```
++    ///
++    /// ```rust,ignore
++    /// use std::collections::BTreeMap;
++    /// // or its use
++    /// let x = std::collections::BTreeMap::new();
++    /// ```
++    /// Use instead:
++    /// ```rust,ignore
++    /// // A similar type that is allowed by the config
++    /// use std::collections::HashMap;
++    /// ```
++    pub DISALLOWED_TYPE,
++    nursery,
++    "use of a disallowed type"
++}
++#[derive(Clone, Debug)]
++pub struct DisallowedType {
++    disallowed: FxHashSet<Vec<Symbol>>,
++    def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
++}
++
++impl DisallowedType {
++    pub fn new(disallowed: &FxHashSet<String>) -> Self {
++        Self {
++            disallowed: disallowed
++                .iter()
++                .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
++                .collect(),
++            def_ids: FxHashSet::default(),
++        }
++    }
++}
++
++impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
++
++impl<'tcx> LateLintPass<'tcx> for DisallowedType {
++    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
++        for path in &self.disallowed {
++            let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
++            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
++            {
++                self.def_ids.insert((id, path.clone()));
++            }
++        }
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if_chain! {
++            if let ItemKind::Use(path, UseKind::Single) = &item.kind;
++            if let Res::Def(_, did) = path.res;
++            if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
++            then {
++                emit(cx, name, item.span,);
++            }
++        }
++    }
++
++    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
++        if_chain! {
++            if let TyKind::Path(path) = &ty.kind;
++            if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id();
++            if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
++            then {
++                emit(cx, name, path.span());
++            }
++        }
++    }
++
++    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
++        if_chain! {
++            if let Res::Def(_, did) = poly.trait_ref.path.res;
++            if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
++            then {
++                emit(cx, name, poly.trait_ref.path.span);
++            }
++        }
++    }
++
++    // TODO: if non primitive const generics are a thing
++    // fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) {
++    //     match arg {
++    //         GenericArg::Const(c) => {},
++    //     }
++    // }
++    // fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) {
++    //     match param.kind {
++    //         GenericParamKind::Const { .. } => {},
++    //     }
++    // }
++}
++
++fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) {
++    let name = name.iter().map(|s| s.to_ident_string()).collect::<Vec<_>>().join("::");
++    span_lint(
++        cx,
++        DISALLOWED_TYPE,
++        span,
++        &format!("`{}` is not allowed according to config", name),
++    );
++}
index e67ec4e06c547d3455f6cd83507ddd227d503a3e,0000000000000000000000000000000000000000..4e164d33a0512b5ef15a909c82f52247520876df
mode 100644,000000..100644
--- /dev/null
@@@ -1,754 -1,0 +1,790 @@@
- use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
++use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
++use clippy_utils::source::first_line_of_span;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
 +use rustc_ast::token::CommentKind;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_data_structures::sync::Lrc;
 +use rustc_errors::emitter::EmitterWriter;
 +use rustc_errors::Handler;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 +use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_parse::maybe_new_parser_from_source_str;
 +use rustc_parse::parser::ForceCollect;
 +use rustc_session::parse::ParseSess;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::edition::Edition;
 +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
 +use rustc_span::{sym, FileName, Pos};
 +use std::io;
 +use std::ops::Range;
 +use url::Url;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for the presence of `_`, `::` or camel-case words
 +    /// outside ticks in documentation.
 +    ///
 +    /// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
 +    /// camel-case probably indicates some code which should be included between
 +    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
 +    /// consider that.
 +    ///
 +    /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
-     /// for is limited, and there are still false positives.
++    /// for is limited, and there are still false positives. HTML elements and their
++    /// content are not linted.
 +    ///
 +    /// In addition, when writing documentation comments, including `[]` brackets
 +    /// inside a link text would trip the parser. Therfore, documenting link with
 +    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
 +    /// would fail.
 +    ///
 +    /// **Examples:**
 +    /// ```rust
 +    /// /// Do something with the foo_bar parameter. See also
 +    /// /// that::other::module::foo.
 +    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
 +    /// fn doit(foo_bar: usize) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Link text with `[]` brackets should be written as following:
 +    /// /// Consume the array and return the inner
 +    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
 +    /// /// [SmallVec]: SmallVec
 +    /// fn main() {}
 +    /// ```
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "presence of `_`, `::` or camel-case outside backticks in documentation"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for the doc comments of publicly visible
 +    /// unsafe functions and warns if there is no `# Safety` section.
 +    ///
 +    /// **Why is this bad?** Unsafe functions should document their safety
 +    /// preconditions, so that users can be sure they are using them safely.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Examples:**
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// At least write a line about safety:
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    pub MISSING_SAFETY_DOC,
 +    style,
 +    "`pub unsafe fn` without `# Safety` docs"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks the doc comments of publicly visible functions that
 +    /// return a `Result` type and warns if there is no `# Errors` section.
 +    ///
 +    /// **Why is this bad?** Documenting the type of errors that can be returned from a
 +    /// function can help callers write code to handle the errors appropriately.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Examples:**
 +    ///
 +    /// Since the following function returns a `Result` it has an `# Errors` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    ///# use std::io;
 +    /// /// # Errors
 +    /// ///
 +    /// /// Will return `Err` if `filename` does not exist or the user does not have
 +    /// /// permission to read it.
 +    /// pub fn read(filename: String) -> io::Result<String> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    pub MISSING_ERRORS_DOC,
 +    pedantic,
 +    "`pub fn` returns `Result` without `# Errors` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks the doc comments of publicly visible functions that
 +    /// may panic and warns if there is no `# Panics` section.
 +    ///
 +    /// **Why is this bad?** Documenting the scenarios in which panicking occurs
 +    /// can help callers who do not want to panic to avoid those situations.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Examples:**
 +    ///
 +    /// Since the following function may panic it has a `# Panics` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    /// /// # Panics
 +    /// ///
 +    /// /// Will panic if y is 0
 +    /// pub fn divide_by(x: i32, y: i32) -> i32 {
 +    ///     if y == 0 {
 +    ///         panic!("Cannot divide by 0")
 +    ///     } else {
 +    ///         x / y
 +    ///     }
 +    /// }
 +    /// ```
 +    pub MISSING_PANICS_DOC,
 +    pedantic,
 +    "`pub fn` may panic without `# Panics` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `fn main() { .. }` in doctests
 +    ///
 +    /// **Why is this bad?** The test can be shorter (and likely more readable)
 +    /// if the `fn main()` is left implicit.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Examples:**
 +    /// ``````rust
 +    /// /// An example of a doctest with a `main()` function
 +    /// ///
 +    /// /// # Examples
 +    /// ///
 +    /// /// ```
 +    /// /// fn main() {
 +    /// ///     // this needs not be in an `fn`
 +    /// /// }
 +    /// /// ```
 +    /// fn needless_main() {
 +    ///     unimplemented!();
 +    /// }
 +    /// ``````
 +    pub NEEDLESS_DOCTEST_MAIN,
 +    style,
 +    "presence of `fn main() {` in code examples"
 +}
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Clone)]
 +pub struct DocMarkdown {
 +    valid_idents: FxHashSet<String>,
 +    in_trait_impl: bool,
 +}
 +
 +impl DocMarkdown {
 +    pub fn new(valid_idents: FxHashSet<String>) -> Self {
 +        Self {
 +            valid_idents,
 +            in_trait_impl: false,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DocMarkdown =>
 +    [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
 +);
 +
 +impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>, _: &'tcx hir::Crate<'_>) {
 +        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
 +        check_attrs(cx, &self.valid_idents, attrs);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        match item.kind {
 +            hir::ItemKind::Fn(ref sig, _, body_id) => {
 +                if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut fpu = FindPanicUnwrap {
 +                        cx,
 +                        typeck_results: cx.tcx.typeck(item.def_id),
 +                        panic_span: None,
 +                    };
 +                    fpu.visit_expr(&body.value);
 +                    lint_for_missing_headers(
 +                        cx,
 +                        item.hir_id(),
 +                        item.span,
 +                        sig,
 +                        headers,
 +                        Some(body_id),
 +                        fpu.panic_span,
 +                    );
 +                }
 +            },
 +            hir::ItemKind::Impl(ref impl_) => {
 +                self.in_trait_impl = impl_.of_trait.is_some();
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl { .. } = item.kind {
 +            self.in_trait_impl = false;
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            if !in_external_macro(cx.tcx.sess, item.span) {
 +                lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, None, None);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let body = cx.tcx.hir().body(body_id);
 +            let mut fpu = FindPanicUnwrap {
 +                cx,
 +                typeck_results: cx.tcx.typeck(item.def_id),
 +                panic_span: None,
 +            };
 +            fpu.visit_expr(&body.value);
 +            lint_for_missing_headers(
 +                cx,
 +                item.hir_id(),
 +                item.span,
 +                sig,
 +                headers,
 +                Some(body_id),
 +                fpu.panic_span,
 +            );
 +        }
 +    }
 +}
 +
 +fn lint_for_missing_headers<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    hir_id: hir::HirId,
 +    span: impl Into<MultiSpan> + Copy,
 +    sig: &hir::FnSig<'_>,
 +    headers: DocHeaders,
 +    body_id: Option<hir::BodyId>,
 +    panic_span: Option<Span>,
 +) {
 +    if !cx.access_levels.is_exported(hir_id) {
 +        return; // Private functions do not require doc comments
 +    }
 +    if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
 +        span_lint(
 +            cx,
 +            MISSING_SAFETY_DOC,
 +            span,
 +            "unsafe function's docs miss `# Safety` section",
 +        );
 +    }
 +    if !headers.panics && panic_span.is_some() {
 +        span_lint_and_note(
 +            cx,
 +            MISSING_PANICS_DOC,
 +            span,
 +            "docs for function which may panic missing `# Panics` section",
 +            panic_span,
 +            "first possible panic found here",
 +        );
 +    }
 +    if !headers.errors {
 +        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
 +            span_lint(
 +                cx,
 +                MISSING_ERRORS_DOC,
 +                span,
 +                "docs for function returning `Result` missing `# Errors` section",
 +            );
 +        } else {
 +            if_chain! {
 +                if let Some(body_id) = body_id;
 +                if let Some(future) = cx.tcx.lang_items().future_trait();
 +                let typeck = cx.tcx.typeck_body(body_id);
 +                let body = cx.tcx.hir().body(body_id);
 +                let ret_ty = typeck.expr_ty(&body.value);
 +                if implements_trait(cx, ret_ty, future, &[]);
 +                if let ty::Opaque(_, subs) = ret_ty.kind();
 +                if let Some(gen) = subs.types().next();
 +                if let ty::Generator(_, subs, _) = gen.kind();
 +                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::result_type);
 +                then {
 +                    span_lint(
 +                        cx,
 +                        MISSING_ERRORS_DOC,
 +                        span,
 +                        "docs for function returning `Result` missing `# Errors` section",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Cleanup documentation decoration.
 +///
 +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
 +/// need to keep track of
 +/// the spans but this function is inspired from the later.
 +#[allow(clippy::cast_possible_truncation)]
 +#[must_use]
 +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
 +    // one-line comments lose their prefix
 +    if comment_kind == CommentKind::Line {
 +        let mut doc = doc.to_owned();
 +        doc.push('\n');
 +        let len = doc.len();
 +        // +3 skips the opening delimiter
 +        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
 +    }
 +
 +    let mut sizes = vec![];
 +    let mut contains_initial_stars = false;
 +    for line in doc.lines() {
 +        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
 +        debug_assert_eq!(offset as u32 as usize, offset);
 +        contains_initial_stars |= line.trim_start().starts_with('*');
 +        // +1 adds the newline, +3 skips the opening delimiter
 +        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
 +    }
 +    if !contains_initial_stars {
 +        return (doc.to_string(), sizes);
 +    }
 +    // remove the initial '*'s if any
 +    let mut no_stars = String::with_capacity(doc.len());
 +    for line in doc.lines() {
 +        let mut chars = line.chars();
 +        for c in &mut chars {
 +            if c.is_whitespace() {
 +                no_stars.push(c);
 +            } else {
 +                no_stars.push(if c == '*' { ' ' } else { c });
 +                break;
 +            }
 +        }
 +        no_stars.push_str(chars.as_str());
 +        no_stars.push('\n');
 +    }
 +
 +    (no_stars, sizes)
 +}
 +
 +#[derive(Copy, Clone)]
 +struct DocHeaders {
 +    safety: bool,
 +    errors: bool,
 +    panics: bool,
 +}
 +
 +fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
 +    let mut doc = String::new();
 +    let mut spans = vec![];
 +
 +    for attr in attrs {
 +        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
 +            let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
 +            spans.extend_from_slice(&current_spans);
 +            doc.push_str(&comment);
 +        } else if attr.has_name(sym::doc) {
 +            // ignore mix of sugared and non-sugared doc
 +            // don't trigger the safety or errors check
 +            return DocHeaders {
 +                safety: true,
 +                errors: true,
 +                panics: true,
 +            };
 +        }
 +    }
 +
 +    let mut current = 0;
 +    for &mut (ref mut offset, _) in &mut spans {
 +        let offset_copy = *offset;
 +        *offset = current;
 +        current += offset_copy;
 +    }
 +
 +    if doc.is_empty() {
 +        return DocHeaders {
 +            safety: false,
 +            errors: false,
 +            panics: false,
 +        };
 +    }
 +
 +    let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter();
 +    // Iterate over all `Events` and combine consecutive events into one
 +    let events = parser.coalesce(|previous, current| {
 +        use pulldown_cmark::Event::Text;
 +
 +        let previous_range = previous.1;
 +        let current_range = current.1;
 +
 +        match (previous.0, current.0) {
 +            (Text(previous), Text(current)) => {
 +                let mut previous = previous.to_string();
 +                previous.push_str(&current);
 +                Ok((Text(previous.into()), previous_range))
 +            },
 +            (previous, current) => Err(((previous, previous_range), (current, current_range))),
 +        }
 +    });
 +    check_doc(cx, valid_idents, events, &spans)
 +}
 +
 +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 +
 +fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
 +    cx: &LateContext<'_>,
 +    valid_idents: &FxHashSet<String>,
 +    events: Events,
 +    spans: &[(usize, Span)],
 +) -> DocHeaders {
 +    // true if a safety header was found
-     use pulldown_cmark::CodeBlockKind;
 +    use pulldown_cmark::Event::{
 +        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 +    };
-     use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
++    use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
++    use pulldown_cmark::{CodeBlockKind, CowStr};
 +
 +    let mut headers = DocHeaders {
 +        safety: false,
 +        errors: false,
 +        panics: false,
 +    };
 +    let mut in_code = false;
 +    let mut in_link = None;
 +    let mut in_heading = false;
 +    let mut is_rust = false;
 +    let mut edition = None;
++    let mut ticks_unbalanced = false;
++    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
++    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
 +    for (event, range) in events {
 +        match event {
 +            Start(CodeBlock(ref kind)) => {
 +                in_code = true;
 +                if let CodeBlockKind::Fenced(lang) = kind {
 +                    for item in lang.split(',') {
 +                        if item == "ignore" {
 +                            is_rust = false;
 +                            break;
 +                        }
 +                        if let Some(stripped) = item.strip_prefix("edition") {
 +                            is_rust = true;
 +                            edition = stripped.parse::<Edition>().ok();
 +                        } else if item.is_empty() || RUST_CODE.contains(&item) {
 +                            is_rust = true;
 +                        }
 +                    }
 +                }
 +            },
 +            End(CodeBlock(_)) => {
 +                in_code = false;
 +                is_rust = false;
 +            },
 +            Start(Link(_, url, _)) => in_link = Some(url),
 +            End(Link(..)) => in_link = None,
-             Start(Heading(_)) => in_heading = true,
-             End(Heading(_)) => in_heading = false,
++            Start(Heading(_) | Paragraph | Item) => {
++                if let Start(Heading(_)) = event {
++                    in_heading = true;
++                }
++                ticks_unbalanced = false;
++                let (_, span) = get_current_span(spans, range.start);
++                paragraph_span = first_line_of_span(cx, span);
++            },
++            End(Heading(_) | Paragraph | Item) => {
++                if let End(Heading(_)) = event {
++                    in_heading = false;
++                }
++                if ticks_unbalanced {
++                    span_lint_and_help(
++                        cx,
++                        DOC_MARKDOWN,
++                        paragraph_span,
++                        "backticks are unbalanced",
++                        None,
++                        "a backtick may be missing a pair",
++                    );
++                } else {
++                    for (text, span) in text_to_check {
++                        check_text(cx, valid_idents, &text, span);
++                    }
++                }
++                text_to_check = Vec::new();
++            },
 +            Start(_tag) | End(_tag) => (), // We don't care about other tags
 +            Html(_html) => (),             // HTML is weird, just ignore it
 +            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
 +            FootnoteReference(text) | Text(text) => {
-                 if Some(&text) == in_link.as_ref() {
++                let (begin, span) = get_current_span(spans, range.start);
++                paragraph_span = paragraph_span.with_hi(span.hi());
++                ticks_unbalanced |= text.contains('`');
++                if Some(&text) == in_link.as_ref() || ticks_unbalanced {
 +                    // Probably a link of the form `<http://example.com>`
 +                    // Which are represented as a link to "http://example.com" with
 +                    // text "http://example.com" by pulldown-cmark
 +                    continue;
 +                }
 +                headers.safety |= in_heading && text.trim() == "Safety";
 +                headers.errors |= in_heading && text.trim() == "Errors";
 +                headers.panics |= in_heading && text.trim() == "Panics";
-                 let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
-                     Ok(o) => o,
-                     Err(e) => e - 1,
-                 };
-                 let (begin, span) = spans[index];
 +                if in_code {
 +                    if is_rust {
 +                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
 +                        check_code(cx, &text, edition, span);
 +                    }
 +                } else {
 +                    // Adjust for the beginning of the current `Event`
 +                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
-                     check_text(cx, valid_idents, &text, span);
++                    text_to_check.push((text, span));
 +                }
 +            },
 +        }
 +    }
 +    headers
 +}
 +
++fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
++    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
++        Ok(o) => o,
++        Err(e) => e - 1,
++    };
++    spans[index]
++}
++
 +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
 +    fn has_needless_main(code: &str, edition: Edition) -> bool {
 +        rustc_driver::catch_fatal_errors(|| {
 +            rustc_span::with_session_globals(edition, || {
 +                let filename = FileName::anon_source_code(code);
 +
 +                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +                let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
 +                let handler = Handler::with_emitter(false, None, box emitter);
 +                let sess = ParseSess::with_span_handler(handler, sm);
 +
 +                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
 +                    Ok(p) => p,
 +                    Err(errs) => {
 +                        for mut err in errs {
 +                            err.cancel();
 +                        }
 +                        return false;
 +                    },
 +                };
 +
 +                let mut relevant_main_found = false;
 +                loop {
 +                    match parser.parse_item(ForceCollect::No) {
 +                        Ok(Some(item)) => match &item.kind {
 +                            // Tests with one of these items are ignored
 +                            ItemKind::Static(..)
 +                            | ItemKind::Const(..)
 +                            | ItemKind::ExternCrate(..)
 +                            | ItemKind::ForeignMod(..) => return false,
 +                            // We found a main function ...
 +                            ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => {
 +                                let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
 +                                let returns_nothing = match &sig.decl.output {
 +                                    FnRetTy::Default(..) => true,
 +                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
 +                                    FnRetTy::Ty(_) => false,
 +                                };
 +
 +                                if returns_nothing && !is_async && !block.stmts.is_empty() {
 +                                    // This main function should be linted, but only if there are no other functions
 +                                    relevant_main_found = true;
 +                                } else {
 +                                    // This main function should not be linted, we're done
 +                                    return false;
 +                                }
 +                            },
 +                            // Another function was found; this case is ignored too
 +                            ItemKind::Fn(..) => return false,
 +                            _ => {},
 +                        },
 +                        Ok(None) => break,
 +                        Err(mut e) => {
 +                            e.cancel();
 +                            return false;
 +                        },
 +                    }
 +                }
 +
 +                relevant_main_found
 +            })
 +        })
 +        .ok()
 +        .unwrap_or_default()
 +    }
 +
 +    if has_needless_main(text, edition) {
 +        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
 +    }
 +}
 +
 +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
 +    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
 +        // Trim punctuation as in `some comment (see foo::bar).`
 +        //                                                   ^^
 +        // Or even as in `_foo bar_` which is emphasized.
 +        let word = word.trim_matches(|c: char| !c.is_alphanumeric());
 +
 +        if valid_idents.contains(word) {
 +            continue;
 +        }
 +
 +        // Adjust for the current word
 +        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
 +        let span = Span::new(
 +            span.lo() + BytePos::from_usize(offset),
 +            span.lo() + BytePos::from_usize(offset + word.len()),
 +            span.ctxt(),
 +        );
 +
 +        check_word(cx, word, span);
 +    }
 +}
 +
 +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
 +    /// Checks if a string is camel-case, i.e., contains at least two uppercase
 +    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
 +    /// Plurals are also excluded (`IDs` is ok).
 +    fn is_camel_case(s: &str) -> bool {
 +        if s.starts_with(|c: char| c.is_digit(10)) {
 +            return false;
 +        }
 +
 +        let s = s.strip_suffix('s').unwrap_or(s);
 +
 +        s.chars().all(char::is_alphanumeric)
 +            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
 +            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
 +    }
 +
 +    fn has_underscore(s: &str) -> bool {
 +        s != "_" && !s.contains("\\_") && s.contains('_')
 +    }
 +
 +    fn has_hyphen(s: &str) -> bool {
 +        s != "-" && s.contains('-')
 +    }
 +
 +    if let Ok(url) = Url::parse(word) {
 +        // try to get around the fact that `foo::bar` parses as a valid URL
 +        if !url.cannot_be_a_base() {
 +            span_lint(
 +                cx,
 +                DOC_MARKDOWN,
 +                span,
 +                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
 +            );
 +
 +            return;
 +        }
 +    }
 +
 +    // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343)
 +    if has_underscore(word) && has_hyphen(word) {
 +        return;
 +    }
 +
 +    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
 +        span_lint(
 +            cx,
 +            DOC_MARKDOWN,
 +            span,
 +            &format!("you should put `{}` between ticks in the documentation", word),
 +        );
 +    }
 +}
 +
 +struct FindPanicUnwrap<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    panic_span: Option<Span>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.panic_span.is_some() {
 +            return;
 +        }
 +
 +        // check for `begin_panic`
 +        if_chain! {
 +            if let ExprKind::Call(func_expr, _) = expr.kind;
 +            if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
 +            if let Some(path_def_id) = path.res.opt_def_id();
 +            if match_panic_def_id(self.cx, path_def_id);
 +            if is_expn_of(expr.span, "unreachable").is_none();
 +            if !is_expn_of_debug_assertions(expr.span);
 +            then {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // check for `assert_eq` or `assert_ne`
 +        if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
 +            self.panic_span = Some(expr.span);
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
 +            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
 +            if is_type_diagnostic_item(self.cx, reciever_ty, sym::option_type)
 +                || is_type_diagnostic_item(self.cx, reciever_ty, sym::result_type)
 +            {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    // Panics in const blocks will cause compilation to fail.
 +    fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +fn is_expn_of_debug_assertions(span: Span) -> bool {
 +    const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
 +    MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
 +}
index 5fdf5bc9e9d118d366f2a2ebaeafb8767a1fb40b,0000000000000000000000000000000000000000..03a8b40df555fde5f0f4c1c1480571d000d9dc91
mode 100644,000000..100644
--- /dev/null
@@@ -1,351 -1,0 +1,351 @@@
-     complexity,
 +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,
++    suspicious,
 +    "whether a variable read occurs before a write depends on sub-expression evaluation order"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for diverging calls that are not match arms or
 +    /// statements.
 +    ///
 +    /// **Why is this bad?** It is often confusing to read. In addition, the
 +    /// sub-expression evaluation order for Rust is not well documented.
 +    ///
 +    /// **Known problems:** Someone might want to use `some_bool || panic!()` as a
 +    /// shorthand.
 +    ///
 +    /// **Example:**
 +    /// ```rust,no_run
 +    /// # fn b() -> bool { true }
 +    /// # fn c() -> bool { true }
 +    /// let a = b() || panic!() || c();
 +    /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
 +    /// let x = (a, b, c, panic!());
 +    /// // can simply be replaced by `panic!()`
 +    /// ```
 +    pub DIVERGING_SUB_EXPRESSION,
 +    complexity,
 +    "whether an expression contains a diverging sub expression"
 +}
 +
 +declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Find a write to a local variable.
 +        let var = if_chain! {
 +            if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
 +            if let Some(var) = path_to_local(lhs);
 +            if expr.span.desugaring_kind().is_none();
 +            then { var } else { return; }
 +        };
 +        let mut visitor = ReadVisitor {
 +            cx,
 +            var,
 +            write_expr: expr,
 +            last_expr: expr,
 +        };
 +        check_for_unsequenced_reads(&mut visitor);
 +    }
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if let Local { init: Some(e), .. } = local {
 +                    DivergenceVisitor { cx }.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
 +            StmtKind::Item(..) => {},
 +        }
 +    }
 +}
 +
 +struct DivergenceVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
 +    fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Closure(..) => {},
 +            ExprKind::Match(e, arms, _) => {
 +                self.visit_expr(e);
 +                for arm in arms {
 +                    if let Some(Guard::If(if_expr)) = arm.guard {
 +                        self.visit_expr(if_expr);
 +                    }
 +                    // make sure top level arm expressions aren't linted
 +                    self.maybe_walk_expr(&*arm.body);
 +                }
 +            },
 +            _ => walk_expr(self, e),
 +        }
 +    }
 +    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
 +        span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
 +            ExprKind::Call(func, _) => {
 +                let typ = self.cx.typeck_results().expr_ty(func);
 +                match typ.kind() {
 +                    ty::FnDef(..) | ty::FnPtr(_) => {
 +                        let sig = typ.fn_sig(self.cx.tcx);
 +                        if let ty::Never = self.cx.tcx.erase_late_bound_regions(sig).output().kind() {
 +                            self.report_diverging_sub_expr(e);
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            },
 +            ExprKind::MethodCall(..) => {
 +                let borrowed_table = self.cx.typeck_results();
 +                if borrowed_table.expr_ty(e).is_never() {
 +                    self.report_diverging_sub_expr(e);
 +                }
 +            },
 +            _ => {
 +                // do not lint expressions referencing objects of type `!`, as that required a
 +                // diverging expression
 +                // to begin with
 +            },
 +        }
 +        self.maybe_walk_expr(e);
 +    }
 +    fn visit_block(&mut self, _: &'tcx Block<'_>) {
 +        // don't continue over blocks, LateLintPass already does that
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Walks up the AST from the given write expression (`vis.write_expr`) looking
 +/// for reads to the same variable that are unsequenced relative to the write.
 +///
 +/// This means reads for which there is a common ancestor between the read and
 +/// the write such that
 +///
 +/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
 +///   and `|| x = 1` don't necessarily evaluate `x`), and
 +///
 +/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
 +///   loops, `match`es, and the short-circuiting logical operators are considered to have a defined
 +///   evaluation order.
 +///
 +/// When such a read is found, the lint is triggered.
 +fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
 +    let map = &vis.cx.tcx.hir();
 +    let mut cur_id = vis.write_expr.hir_id;
 +    loop {
 +        let parent_id = map.get_parent_node(cur_id);
 +        if parent_id == cur_id {
 +            break;
 +        }
 +        let parent_node = match map.find(parent_id) {
 +            Some(parent) => parent,
 +            None => break,
 +        };
 +
 +        let stop_early = match parent_node {
 +            Node::Expr(expr) => check_expr(vis, expr),
 +            Node::Stmt(stmt) => check_stmt(vis, stmt),
 +            Node::Item(_) => {
 +                // We reached the top of the function, stop.
 +                break;
 +            },
 +            _ => StopEarly::KeepGoing,
 +        };
 +        match stop_early {
 +            StopEarly::Stop => break,
 +            StopEarly::KeepGoing => {},
 +        }
 +
 +        cur_id = parent_id;
 +    }
 +}
 +
 +/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
 +/// `check_expr` weren't an independent function, this would be unnecessary and
 +/// we could just use `break`).
 +enum StopEarly {
 +    KeepGoing,
 +    Stop,
 +}
 +
 +fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
 +    if expr.hir_id == vis.last_expr.hir_id {
 +        return StopEarly::KeepGoing;
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Array(_)
 +        | ExprKind::Tup(_)
 +        | ExprKind::MethodCall(..)
 +        | ExprKind::Call(_, _)
 +        | ExprKind::Assign(..)
 +        | ExprKind::Index(_, _)
 +        | ExprKind::Repeat(_, _)
 +        | ExprKind::Struct(_, _, _) => {
 +            walk_expr(vis, expr);
 +        },
 +        ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
 +            if op.node == BinOpKind::And || op.node == BinOpKind::Or {
 +                // x && y and x || y always evaluate x first, so these are
 +                // strictly sequenced.
 +            } else {
 +                walk_expr(vis, expr);
 +            }
 +        },
 +        ExprKind::Closure(_, _, _, _, _) => {
 +            // Either
 +            //
 +            // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
 +            //   function and can stop, or
 +            //
 +            // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
 +            //   its body, we don't necessarily have a write, so we need to stop to avoid generating false
 +            //   positives.
 +            //
 +            // This is also the only place we need to stop early (grrr).
 +            return StopEarly::Stop;
 +        },
 +        // All other expressions either have only one child or strictly
 +        // sequence the evaluation order of their sub-expressions.
 +        _ => {},
 +    }
 +
 +    vis.last_expr = expr;
 +
 +    StopEarly::KeepGoing
 +}
 +
 +fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
 +    match stmt.kind {
 +        StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
 +        // If the declaration is of a local variable, check its initializer
 +        // expression if it has one. Otherwise, keep going.
 +        StmtKind::Local(local) => local
 +            .init
 +            .as_ref()
 +            .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
 +        StmtKind::Item(..) => StopEarly::KeepGoing,
 +    }
 +}
 +
 +/// A visitor that looks for reads from a variable.
 +struct ReadVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// The ID of the variable we're looking for.
 +    var: HirId,
 +    /// The expressions where the write to the variable occurred (for reporting
 +    /// in the lint).
 +    write_expr: &'tcx Expr<'tcx>,
 +    /// The last (highest in the AST) expression we've checked, so we know not
 +    /// to recheck it.
 +    last_expr: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if expr.hir_id == self.last_expr.hir_id {
 +            return;
 +        }
 +
 +        if path_to_local_id(expr, self.var) {
 +            // Check that this is a read, not a write.
 +            if !is_in_assignment_position(self.cx, expr) {
 +                span_lint_and_note(
 +                    self.cx,
 +                    EVAL_ORDER_DEPENDENCE,
 +                    expr.span,
 +                    &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
 +                    Some(self.write_expr.span),
 +                    "whether read occurs before this write depends on evaluation order",
 +                );
 +            }
 +        }
 +        match expr.kind {
 +            // We're about to descend a closure. Since we don't know when (or
 +            // if) the closure will be evaluated, any reads in it might not
 +            // occur here (or ever). Like above, bail to avoid false positives.
 +            ExprKind::Closure(_, _, _, _, _) |
 +
 +            // We want to avoid a false positive when a variable name occurs
 +            // only to have its address taken, so we stop here. Technically,
 +            // this misses some weird cases, eg.
 +            //
 +            // ```rust
 +            // let mut x = 0;
 +            // let a = foo(&{x = 1; x}, x);
 +            // ```
 +            //
 +            // TODO: fix this
 +            ExprKind::AddrOf(_, _, _) => {
 +                return;
 +            }
 +            _ => {}
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
 +fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::Assign(lhs, ..) = parent.kind {
 +            return lhs.hir_id == expr.hir_id;
 +        }
 +    }
 +    false
 +}
index b5ebe5f90ba25c6733ccafe8f054db109f08ed1f,0000000000000000000000000000000000000000..1e503cc795ccbb70d9ec7c2fb002b703d5ef5c24
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,113 @@@
-     correctness,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{match_def_path, paths, sugg};
 +use if_chain::if_chain;
 +use rustc_ast::util::parser::AssocOp;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or
 +     /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
 +     ///
 +     /// **Why is this bad?** The code without `.abs()` is more likely to have a bug.
 +     ///
 +     /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is
 +     /// technically unneccessary. However, it will make the code more robust and doesn't have any
 +     /// large performance implications. If the abs call was deliberately left out for performance
 +     /// reasons, it is probably better to state this explicitly in the code, which then can be done
 +     /// with an allow.
 +     ///
 +     /// **Example:**
 +     ///
 +     /// ```rust
 +     /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
 +     ///     (a - b) < f32::EPSILON
 +     /// }
 +     /// ```
 +     /// Use instead:
 +     /// ```rust
 +     /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
 +     ///     (a - b).abs() < f32::EPSILON
 +     /// }
 +     /// ```
 +    pub FLOAT_EQUALITY_WITHOUT_ABS,
++    suspicious,
 +    "float equality check without `.abs()`"
 +}
 +
 +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let lhs;
 +        let rhs;
 +
 +        // check if expr is a binary expression with a lt or gt operator
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            match op.node {
 +                BinOpKind::Lt => {
 +                    lhs = left;
 +                    rhs = right;
 +                },
 +                BinOpKind::Gt => {
 +                    lhs = right;
 +                    rhs = left;
 +                },
 +                _ => return,
 +            };
 +        } else {
 +            return;
 +        }
 +
 +        if_chain! {
 +
 +            // left hand side is a substraction
 +            if let ExprKind::Binary(
 +                Spanned {
 +                    node: BinOpKind::Sub,
 +                    ..
 +                },
 +                val_l,
 +                val_r,
 +            ) = lhs.kind;
 +
 +            // right hand side matches either f32::EPSILON or f64::EPSILON
 +            if let ExprKind::Path(ref epsilon_path) = rhs.kind;
 +            if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
 +            if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
 +
 +            // values of the substractions on the left hand side are of the type float
 +            let t_val_l = cx.typeck_results().expr_ty(val_l);
 +            let t_val_r = cx.typeck_results().expr_ty(val_r);
 +            if let ty::Float(_) = t_val_l.kind();
 +            if let ty::Float(_) = t_val_r.kind();
 +
 +            then {
 +                let sug_l = sugg::Sugg::hir(cx, val_l, "..");
 +                let sug_r = sugg::Sugg::hir(cx, val_r, "..");
 +                // format the suggestion
 +                let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
 +                // spans the lint
 +                span_lint_and_then(
 +                    cx,
 +                    FLOAT_EQUALITY_WITHOUT_ABS,
 +                    expr.span,
 +                    "float equality check without `.abs()`",
 +                    | diag | {
 +                        diag.span_suggestion(
 +                            lhs.span,
 +                            "add `.abs()`",
 +                            suggestion,
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                    }
 +                );
 +            }
 +        }
 +    }
 +}
index 3bd6a09d3653aeb0a60887d8df220196f910d16b,0000000000000000000000000000000000000000..8aefb8d46f6e843115d9d90d3658d1ae4f280dd0
mode 100644,000000..100644
--- /dev/null
@@@ -1,328 -1,0 +1,328 @@@
-     style,
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
 +use clippy_utils::differing_macro_contexts;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +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 use of the non-existent `=*`, `=!` and `=-`
 +    /// operators.
 +    ///
 +    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
 +    /// confusing.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
 +    /// ```
 +    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
-     style,
++    suspicious,
 +    "suspicious formatting of `*=`, `-=` or `!=`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks the formatting of a unary operator on the right hand side
 +    /// of a binary operator. It lints if there is no space between the binary and unary operators,
 +    /// but there is a space between the unary and its operand.
 +    ///
 +    /// **Why is this bad?** This is either a typo in the binary operator or confusing.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
 +    /// }
 +    ///
 +    /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_UNARY_OP_FORMATTING,
-     style,
++    suspicious,
 +    "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
 +    /// is followed immediately by a newline or the `else` seems to be missing.
 +    ///
 +    /// **Why is this bad?** This is probably some refactoring remnant, even if the
 +    /// code is correct, it might look confusing.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// if foo {
 +    /// } { // looks like an `else` is missing here
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } if bar { // looks like an `else` is missing here
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } else
 +    ///
 +    /// { // this is the `else` block of the previous `if`, but should it be?
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } else
 +    ///
 +    /// if bar { // this is the `else` block of the previous `if`, but should it be?
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_ELSE_FORMATTING,
++    suspicious,
 +    "suspicious formatting of `else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for possible missing comma in an array. It lints if
 +    /// an array element is a binary operator expression and it lies on two lines.
 +    ///
 +    /// **Why is this bad?** This could lead to unexpected results.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// let a = &[
 +    ///     -1, -2, -3 // <= no comma here
 +    ///     -4, -5, -6
 +    /// ];
 +    /// ```
 +    pub POSSIBLE_MISSING_COMMA,
 +    correctness,
 +    "possible missing comma in array"
 +}
 +
 +declare_lint_pass!(Formatting => [
 +    SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    SUSPICIOUS_UNARY_OP_FORMATTING,
 +    SUSPICIOUS_ELSE_FORMATTING,
 +    POSSIBLE_MISSING_COMMA
 +]);
 +
 +impl EarlyLintPass for Formatting {
 +    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
 +        for w in block.stmts.windows(2) {
 +            if let (StmtKind::Expr(first), StmtKind::Expr(second) | StmtKind::Semi(second)) = (&w[0].kind, &w[1].kind) {
 +                check_missing_else(cx, first, second);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        check_assign(cx, expr);
 +        check_unop(cx, expr);
 +        check_else(cx, expr);
 +        check_array(cx, expr);
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
 +fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
 +        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
 +            let eq_span = lhs.span.between(rhs.span);
 +            if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
 +                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
 +                    let op = UnOp::to_string(op);
 +                    let eqop_span = lhs.span.between(sub_rhs.span);
 +                    if eq_snippet.ends_with('=') {
 +                        span_lint_and_note(
 +                            cx,
 +                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +                            eqop_span,
 +                            &format!(
 +                                "this looks like you are trying to use `.. {op}= ..`, but you \
 +                                 really are doing `.. = ({op} ..)`",
 +                                op = op
 +                            ),
 +                            None,
 +                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
 +fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if_chain! {
 +        if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
 +        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
 +        // span between BinOp LHS and RHS
 +        let binop_span = lhs.span.between(rhs.span);
 +        // if RHS is a UnOp
 +        if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
 +        // from UnOp operator to UnOp operand
 +        let unop_operand_span = rhs.span.until(un_rhs.span);
 +        if let Some(binop_snippet) = snippet_opt(cx, binop_span);
 +        if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
 +        let binop_str = BinOpKind::to_string(&binop.node);
 +        // no space after BinOp operator and space after UnOp operator
 +        if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
 +        then {
 +            let unop_str = UnOp::to_string(op);
 +            let eqop_span = lhs.span.between(un_rhs.span);
 +            span_lint_and_help(
 +                cx,
 +                SUSPICIOUS_UNARY_OP_FORMATTING,
 +                eqop_span,
 +                &format!(
 +                    "by not having a space between `{binop}` and `{unop}` it looks like \
 +                     `{binop}{unop}` is a single operator",
 +                    binop = binop_str,
 +                    unop = unop_str
 +                ),
 +                None,
 +                &format!(
 +                    "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
 +                    binop = binop_str,
 +                    unop = unop_str
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
 +fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if_chain! {
 +        if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
 +        if is_block(else_) || is_if(else_);
 +        if !differing_macro_contexts(then.span, else_.span);
 +        if !then.span.from_expansion() && !in_external_macro(cx.sess, expr.span);
 +
 +        // workaround for rust-lang/rust#43081
 +        if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
 +
 +        // this will be a span from the closing ‘}’ of the “then” block (excluding) to
 +        // the “if” of the “else if” block (excluding)
 +        let else_span = then.span.between(else_.span);
 +
 +        // the snippet should look like " else \n    " with maybe comments anywhere
 +        // it’s bad when there is a ‘\n’ after the “else”
 +        if let Some(else_snippet) = snippet_opt(cx, else_span);
 +        if let Some((pre_else, post_else)) = else_snippet.split_once("else");
 +        if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
 +
 +        then {
 +            // Allow allman style braces `} \n else \n {`
 +            if_chain! {
 +                if is_block(else_);
 +                if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
 +                // Exactly one eol before and after the else
 +                if !pre_else_post_eol.contains('\n');
 +                if !post_else_post_eol.contains('\n');
 +                then {
 +                    return;
 +                }
 +            }
 +
 +            let else_desc = if is_if(else_) { "if" } else { "{..}" };
 +            span_lint_and_note(
 +                cx,
 +                SUSPICIOUS_ELSE_FORMATTING,
 +                else_span,
 +                &format!("this is an `else {}` but the formatting might hide it", else_desc),
 +                None,
 +                &format!(
 +                    "to remove this lint, remove the `else` or remove the new line between \
 +                     `else` and `{}`",
 +                    else_desc,
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +#[must_use]
 +fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
 +    // &, *, -
 +    bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
 +}
 +
 +fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
 +    cx.sess.source_map().lookup_char_pos(span.lo()).col.0
 +}
 +
 +/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
 +fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if let ExprKind::Array(ref array) = expr.kind {
 +        for element in array {
 +            if_chain! {
 +                if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
 +                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
 +                let space_span = lhs.span.between(op.span);
 +                if let Some(space_snippet) = snippet_opt(cx, space_span);
 +                let lint_span = lhs.span.with_lo(lhs.span.hi());
 +                if space_snippet.contains('\n');
 +                if indentation(cx, op.span) <= indentation(cx, lhs.span);
 +                then {
 +                    span_lint_and_note(
 +                        cx,
 +                        POSSIBLE_MISSING_COMMA,
 +                        lint_span,
 +                        "possibly missing a comma here",
 +                        None,
 +                        "to remove this lint, add a comma or write the expr in a single line",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
 +    if !differing_macro_contexts(first.span, second.span)
 +        && !first.span.from_expansion()
 +        && is_if(first)
 +        && (is_block(second) || is_if(second))
 +    {
 +        // where the else would be
 +        let else_span = first.span.between(second.span);
 +
 +        if let Some(else_snippet) = snippet_opt(cx, else_span) {
 +            if !else_snippet.contains('\n') {
 +                let (looks_like, next_thing) = if is_if(second) {
 +                    ("an `else if`", "the second `if`")
 +                } else {
 +                    ("an `else {..}`", "the next block")
 +                };
 +
 +                span_lint_and_note(
 +                    cx,
 +                    SUSPICIOUS_ELSE_FORMATTING,
 +                    else_span,
 +                    &format!("this looks like {} but the `else` is missing", looks_like),
 +                    None,
 +                    &format!(
 +                        "to remove this lint, add the missing `else` or add a new line before {}",
 +                        next_thing,
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_block(expr: &Expr) -> bool {
 +    matches!(expr.kind, ExprKind::Block(..))
 +}
 +
 +/// Check if the expression is an `if` or `if let`
 +fn is_if(expr: &Expr) -> bool {
 +    matches!(expr.kind, ExprKind::If(..))
 +}
index 3707e792177d3317408cb59dcf6c9c51022011d5,0000000000000000000000000000000000000000..8e45fdfecc447a641ec2882c80f41a2fe779f88b
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,107 @@@
-             if arg_lhs_path.ident.name == sym!(len);
 +//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::SpanlessEq;
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// **Why is this bad?** Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // Bad
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    ///
 +    /// // Good
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.last();
 +    /// ```
 +    pub GET_LAST_WITH_LEN,
 +    complexity,
 +    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
 +}
 +
 +declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for GetLastWithLen {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // Is a method call
 +            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
 +
 +            // Method name is "get"
 +            if path.ident.name == sym!(get);
 +
 +            // Argument 0 (the struct we're calling the method on) is a vector
 +            if let Some(struct_calling_on) = args.get(0);
 +            let struct_ty = cx.typeck_results().expr_ty(struct_calling_on);
 +            if is_type_diagnostic_item(cx, struct_ty, sym::vec_type);
 +
 +            // Argument to "get" is a subtraction
 +            if let Some(get_index_arg) = args.get(1);
 +            if let ExprKind::Binary(
 +                Spanned {
 +                    node: BinOpKind::Sub,
 +                    ..
 +                },
 +                lhs,
 +                rhs,
 +            ) = &get_index_arg.kind;
 +
 +            // LHS of subtraction is "x.len()"
 +            if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args, _) = &lhs.kind;
++            if arg_lhs_path.ident.name == sym::len;
 +            if let Some(arg_lhs_struct) = lhs_args.get(0);
 +
 +            // The two vectors referenced (x in x.get(...) and in x.len())
 +            if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct);
 +
 +            // RHS of subtraction is 1
 +            if let ExprKind::Lit(rhs_lit) = &rhs.kind;
 +            if let LitKind::Int(1, ..) = rhs_lit.node;
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let vec_name = snippet_with_applicability(
 +                    cx,
 +                    struct_calling_on.span, "vec",
 +                    &mut applicability,
 +                );
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    GET_LAST_WITH_LEN,
 +                    expr.span,
 +                    &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name),
 +                    "try",
 +                    format!("{}.last()", vec_name),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index f661f7ede821a6d486a33448eab7c56fd8e106da,0000000000000000000000000000000000000000..5403d76ea30c8774c4f5c64e2b26a73a8821c3ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,153 @@@
- /// Checks if `Mutex::lock` is called in the `if let _ = expr.
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::SpanlessEq;
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
 +use rustc_hir::{Expr, ExprKind, MatchSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `Mutex::lock` calls in `if let` expression
 +    /// with lock calls in any of the else blocks.
 +    ///
 +    /// **Why is this bad?** The Mutex lock remains held for the whole
 +    /// `if let ... else` block and deadlocks.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust,ignore
 +    /// if let Ok(thing) = mutex.lock() {
 +    ///     do_thing();
 +    /// } else {
 +    ///     mutex.lock();
 +    /// }
 +    /// ```
 +    /// Should be written
 +    /// ```rust,ignore
 +    /// let locked = mutex.lock();
 +    /// if let Ok(thing) = locked {
 +    ///     do_thing(thing);
 +    /// } else {
 +    ///     use_locked(locked);
 +    /// }
 +    /// ```
 +    pub IF_LET_MUTEX,
 +    correctness,
 +    "locking a `Mutex` in an `if let` block can cause deadlocks"
 +}
 +
 +declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
 +        let mut arm_visit = ArmVisitor {
 +            mutex_lock_called: false,
 +            found_mutex: None,
 +            cx,
 +        };
 +        let mut op_visit = OppVisitor {
 +            mutex_lock_called: false,
 +            found_mutex: None,
 +            cx,
 +        };
 +        if let ExprKind::Match(
 +            op,
 +            arms,
 +            MatchSource::IfLetDesugar {
 +                contains_else_clause: true,
 +            },
 +        ) = ex.kind
 +        {
 +            op_visit.visit_expr(op);
 +            if op_visit.mutex_lock_called {
 +                for arm in arms {
 +                    arm_visit.visit_arm(arm);
 +                }
 +
 +                if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
 +                    span_lint_and_help(
 +                        cx,
 +                        IF_LET_MUTEX,
 +                        ex.span,
 +                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
 +                        None,
 +                        "move the lock call outside of the `if let ...` expression",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
++/// Checks if `Mutex::lock` is called in the `if let` expr.
 +pub struct OppVisitor<'a, 'tcx> {
 +    mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
 +            self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Checks if `Mutex::lock` is called in any of the branches.
 +pub struct ArmVisitor<'a, 'tcx> {
 +    mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
 +            self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
 +    fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
 +        self.found_mutex
 +            .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
 +    }
 +}
 +
 +fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind;
 +        if path.ident.as_str() == "lock";
 +        let ty = cx.typeck_results().expr_ty(&args[0]);
 +        if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
 +        then {
 +            Some(&args[0])
 +        } else {
 +            None
 +        }
 +    }
 +}
index 583514b22f9bf65a01897fa38618d25d910913be,0000000000000000000000000000000000000000..d69187f67466dd991ed1c5b046861255be1da678
mode 100644,000000..100644
--- /dev/null
@@@ -1,499 -1,0 +1,501 @@@
-             if item.ident.as_str() == "len";
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefIdSet;
 +use rustc_hir::{
 +    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
 +    ItemKind, Mutability, Node, TraitItemRef, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, AssocKind, FnSig, Ty, TyS};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{
 +    source_map::{Span, Spanned, Symbol},
 +    symbol::sym,
 +};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for getting the length of something via `.len()`
 +    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
 +    /// than calculating their length. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// if x.len() == 0 {
 +    ///     ..
 +    /// }
 +    /// if y.len() != 0 {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// instead use
 +    /// ```ignore
 +    /// if x.is_empty() {
 +    ///     ..
 +    /// }
 +    /// if !y.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub LEN_ZERO,
 +    style,
 +    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for items that implement `.len()` but not
 +    /// `.is_empty()`.
 +    ///
 +    /// **Why is this bad?** It is good custom to have both methods, because for
 +    /// some data structures, asking about the length will be a costly operation,
 +    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
 +    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
 +    /// lint will ignore such entities.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// impl X {
 +    ///     pub fn len(&self) -> usize {
 +    ///         ..
 +    ///     }
 +    /// }
 +    /// ```
 +    pub LEN_WITHOUT_IS_EMPTY,
 +    style,
 +    "traits or impls with a public `len` method but no corresponding `is_empty` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for comparing to an empty slice such as `""` or `[]`,
 +    /// and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
 +    /// than checking for equality. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```ignore
 +    /// if s == "" {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr == [] {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// if s.is_empty() {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub COMPARISON_TO_EMPTY,
 +    style,
 +    "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
 +}
 +
 +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LenZero {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if item.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
 +            check_trait_items(cx, item, trait_items);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if_chain! {
-     fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: &str) -> bool {
-         item.ident.name.as_str() == name
++            if item.ident.name == sym::len;
 +            if let ImplItemKind::Fn(sig, _) = &item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if cx.access_levels.is_exported(item.hir_id());
 +            if matches!(sig.decl.output, FnRetTy::Return(_));
 +            if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
 +            if imp.of_trait.is_none();
 +            if let TyKind::Path(ty_path) = &imp.self_ty.kind;
 +            if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
 +            if let Some(local_id) = ty_id.as_local();
 +            let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
 +            if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
 +            if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder());
 +            then {
 +                let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
 +                    Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
 +                    Some(Node::Item(x)) => match x.kind {
 +                        ItemKind::Struct(..) => (x.ident.name, "struct"),
 +                        ItemKind::Enum(..) => (x.ident.name, "enum"),
 +                        ItemKind::Union(..) => (x.ident.name, "union"),
 +                        _ => (x.ident.name, "type"),
 +                    }
 +                    _ => return,
 +                };
 +                check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind)
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
 +            match cmp {
 +                BinOpKind::Eq => {
 +                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
 +                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
 +                },
 +                BinOpKind::Ne => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
 +                },
 +                BinOpKind::Gt => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
 +                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
 +                },
 +                BinOpKind::Lt => {
 +                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
 +                },
 +                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
 +                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
 +                _ => (),
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
-     if cx.access_levels.is_exported(visited_trait.hir_id()) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
++    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
++        item.ident.name == name
 +            && if let AssocItemKind::Fn { has_self } = item.kind {
 +                has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 }
 +            } else {
 +                false
 +            }
 +    }
 +
 +    // fill the set with current and super traits
 +    fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
 +        if set.insert(traitt) {
 +            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
 +                fill_trait_set(supertrait, set, cx);
 +            }
 +        }
 +    }
 +
-         if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
++    if cx.access_levels.is_exported(visited_trait.hir_id())
++        && trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
++    {
 +        let mut current_and_super_traits = DefIdSet::default();
 +        fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
 +
 +        let is_empty_method_found = current_and_super_traits
 +            .iter()
 +            .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order())
 +            .any(|i| {
 +                i.kind == ty::AssocKind::Fn
 +                    && i.fn_has_self_parameter
 +                    && i.ident.name == sym!(is_empty)
 +                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
 +            });
 +
 +        if !is_empty_method_found {
 +            span_lint(
 +                cx,
 +                LEN_WITHOUT_IS_EMPTY,
 +                visited_trait.span,
 +                &format!(
 +                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
 +                    visited_trait.ident.name
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum LenOutput<'tcx> {
 +    Integral,
 +    Option(DefId),
 +    Result(DefId, Ty<'tcx>),
 +}
 +fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
 +    match *sig.output().kind() {
 +        ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => {
 +            subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did))
 +        },
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) => subs
 +            .type_at(0)
 +            .is_integral()
 +            .then(|| LenOutput::Result(adt.did, subs.type_at(1))),
 +        _ => None,
 +    }
 +}
 +
 +impl LenOutput<'_> {
 +    fn matches_is_empty_output(self, ty: Ty<'_>) -> bool {
 +        match (self, ty.kind()) {
 +            (_, &ty::Bool) => true,
 +            (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did => subs.type_at(0).is_bool(),
 +            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did => {
 +                subs.type_at(0).is_bool() && TyS::same_type(subs.type_at(1), err_ty)
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
 +        let self_ref = match self_kind {
 +            ImplicitSelfKind::ImmRef => "&",
 +            ImplicitSelfKind::MutRef => "&mut ",
 +            _ => "",
 +        };
 +        match self {
 +            Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref),
 +            Self::Option(_) => format!(
 +                "expected signature: `({}self) -> bool` or `({}self) -> Option<bool>",
 +                self_ref, self_ref
 +            ),
 +            Self::Result(..) => format!(
 +                "expected signature: `({}self) -> bool` or `({}self) -> Result<bool>",
 +                self_ref, self_ref
 +            ),
 +        }
 +    }
 +}
 +
 +/// Checks if the given signature matches the expectations for `is_empty`
 +fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool {
 +    match &**sig.inputs_and_output {
 +        [arg, res] if len_output.matches_is_empty_output(res) => {
 +            matches!(
 +                (arg.kind(), self_kind),
 +                (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
 +                    | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef)
 +            ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the given type has an `is_empty` method with the appropriate signature.
 +fn check_for_is_empty(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    self_kind: ImplicitSelfKind,
 +    output: LenOutput<'_>,
 +    impl_ty: DefId,
 +    item_name: Symbol,
 +    item_kind: &str,
 +) {
 +    let is_empty = Symbol::intern("is_empty");
 +    let is_empty = cx
 +        .tcx
 +        .inherent_impls(impl_ty)
 +        .iter()
 +        .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
 +        .find(|item| item.kind == AssocKind::Fn);
 +
 +    let (msg, is_empty_span, self_kind) = match is_empty {
 +        None => (
 +            format!(
 +                "{} `{}` has a public `len` method, but no `is_empty` method",
 +                item_kind,
 +                item_name.as_str(),
 +            ),
 +            None,
 +            None,
 +        ),
 +        Some(is_empty)
 +            if !cx
 +                .access_levels
 +                .is_exported(cx.tcx.hir().local_def_id_to_hir_id(is_empty.def_id.expect_local())) =>
 +        {
 +            (
 +                format!(
 +                    "{} `{}` has a public `len` method, but a private `is_empty` method",
 +                    item_kind,
 +                    item_name.as_str(),
 +                ),
 +                Some(cx.tcx.def_span(is_empty.def_id)),
 +                None,
 +            )
 +        },
 +        Some(is_empty)
 +            if !(is_empty.fn_has_self_parameter
 +                && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) =>
 +        {
 +            (
 +                format!(
 +                    "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
 +                    item_kind,
 +                    item_name.as_str(),
 +                ),
 +                Some(cx.tcx.def_span(is_empty.def_id)),
 +                Some(self_kind),
 +            )
 +        },
 +        Some(_) => return,
 +    };
 +
 +    span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| {
 +        if let Some(span) = is_empty_span {
 +            db.span_note(span, "`is_empty` defined here");
 +        }
 +        if let Some(self_kind) = self_kind {
 +            db.note(&output.expected_sig(self_kind));
 +        }
 +    });
 +}
 +
 +fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
 +    if let (&ExprKind::MethodCall(method_path, _, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
 +        // check if we are in an is_empty() method
 +        if let Some(name) = get_item_name(cx, method) {
 +            if name.as_str() == "is_empty" {
 +                return;
 +            }
 +        }
 +
 +        check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to);
 +    } else {
 +        check_empty_expr(cx, span, method, lit, op);
 +    }
 +}
 +
 +fn check_len(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    method_name: Symbol,
 +    args: &[Expr<'_>],
 +    lit: &LitKind,
 +    op: &str,
 +    compare_to: u32,
 +) {
 +    if let LitKind::Int(lit, _) = *lit {
 +        // check if length is compared to the specified number
 +        if lit != u128::from(compare_to) {
 +            return;
 +        }
 +
++        if method_name == sym::len && args.len() == 1 && has_is_empty(cx, &args[0]) {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                LEN_ZERO,
 +                span,
 +                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
 +                &format!("using `{}is_empty` is clearer and more explicit", op),
 +                format!(
 +                    "{}{}.is_empty()",
 +                    op,
 +                    snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
 +    if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            COMPARISON_TO_EMPTY,
 +            span,
 +            "comparison to empty slice",
 +            &format!("using `{}is_empty` is clearer and more explicit", op),
 +            format!(
 +                "{}{}.is_empty()",
 +                op,
 +                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn is_empty_string(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Lit(ref lit) = expr.kind {
 +        if let LitKind::Str(lit, _) = lit.node {
 +            let lit = lit.as_str();
 +            return lit == "";
 +        }
 +    }
 +    false
 +}
 +
 +fn is_empty_array(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Array(arr) = expr.kind {
 +        return arr.is_empty();
 +    }
 +    false
 +}
 +
 +/// Checks if this type has an `is_empty` method.
 +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
 +    fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
 +        if let ty::AssocKind::Fn = item.kind {
 +            if item.ident.name.as_str() == "is_empty" {
 +                let sig = cx.tcx.fn_sig(item.def_id);
 +                let ty = sig.skip_binder();
 +                ty.inputs().len() == 1
 +            } else {
 +                false
 +            }
 +        } else {
 +            false
 +        }
 +    }
 +
 +    /// Checks the inherent impl's items for an `is_empty(self)` method.
 +    fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
 +        cx.tcx.inherent_impls(id).iter().any(|imp| {
 +            cx.tcx
 +                .associated_items(*imp)
 +                .in_definition_order()
 +                .any(|item| is_is_empty(cx, item))
 +        })
 +    }
 +
 +    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
 +    match ty.kind() {
 +        ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
 +            cx.tcx
 +                .associated_items(principal.def_id())
 +                .in_definition_order()
 +                .any(|item| is_is_empty(cx, item))
 +        }),
 +        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
 +        ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
 +        ty::Array(..) | ty::Slice(..) | ty::Str => true,
 +        _ => false,
 +    }
 +}
index e7dd3952b3ac96b19bf7aae3ad288730663deb2f,0000000000000000000000000000000000000000..e0325738466bec902564ee0c4138862bfd4365d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,2155 -1,0 +1,2183 @@@
- /// 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.
 +// error-pattern:cargo-clippy
 +
 +#![feature(box_patterns)]
 +#![feature(box_syntax)]
 +#![feature(drain_filter)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![feature(control_flow_enum)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir;
 +extern crate rustc_parse;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
- mod map_identity;
++/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
++/// enabled by default. As said in the README.md of this repository, if the lint level mapping
++/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// **What it does:** Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// **Why is this bad?** Supply the reason for linting the code.
 +///     ///
 +///     /// **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, suspicious, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++        }
++    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "metadata-collector-lint")]
 +mod deprecated_lints;
 +mod utils;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod absurd_extreme_comparisons;
 +mod approx_const;
 +mod arithmetic;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assign_ops;
 +mod async_yields_async;
 +mod 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 disallowed_script_idents;
++mod disallowed_type;
 +mod doc;
 +mod double_comparison;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duration_subsec;
 +mod else_if_without_else;
 +mod empty_enum;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod eq_op;
 +mod erasing_op;
 +mod escape;
 +mod eta_reduction;
 +mod eval_order_dependence;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod 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;
-         "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items",
 +mod map_unit_fn;
 +mod match_on_vec_items;
 +mod matches;
 +mod mem_discriminant;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod missing_const_for_fn;
 +mod missing_doc;
++mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod modulo_arithmetic;
 +mod multiple_crate_versions;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_mutex_lock;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bitwise_bool;
 +mod needless_bool;
 +mod needless_borrow;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
++mod nonstandard_macro_braces;
 +mod open_options;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod pass_by_ref_or_value;
 +mod path_buf_push_overwrite;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_eq;
 +mod ptr_offset_with_cast;
 +mod question_mark;
 +mod ranges;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod repeat_once;
 +mod returns;
 +mod self_assignment;
 +mod 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_async;
 +mod unused_io_amount;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wildcard_dependencies;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::TryConf;
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +    store.register_pre_expansion_pass(|| box write::Write::default());
 +    store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
 +    store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.struct_err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            error
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[allow(clippy::too_many_lines)]
 +#[rustfmt::skip]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    // begin deprecated lints, do not remove this comment, it’s used in `update_lints`
 +    store.register_removed(
 +        "clippy::should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "clippy::extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "clippy::range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "clippy::unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "clippy::unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "clippy::misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "clippy::assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "clippy::if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "clippy::unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "clippy::unused_collect",
 +        "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
 +    );
 +    store.register_removed(
 +        "clippy::replace_consts",
 +        "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
 +    );
 +    store.register_removed(
 +        "clippy::regex_macro",
 +        "the regex! macro has been removed from the regex crate in 2018",
 +    );
 +    store.register_removed(
 +        "clippy::find_map",
 +        "this lint has been replaced by `manual_find_map`, a more specific lint",
 +    );
 +    store.register_removed(
 +        "clippy::filter_map",
 +        "this lint has been replaced by `manual_filter_map`, a more specific lint",
 +    );
 +    store.register_removed(
 +        "clippy::pub_enum_variant_names",
-         "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items",
++        "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
 +    );
 +    store.register_removed(
 +        "clippy::wrong_pub_self_convention",
-         map_identity::MAP_IDENTITY,
++        "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
 +    );
 +    // end deprecated lints, do not remove this comment, it’s used in `update_lints`
 +
 +    // begin register lints, do not remove this comment, it’s used in `update_lints`
 +    store.register_lints(&[
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::DEFAULT_LINT,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::IF_CHAIN_STYLE,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::INVALID_PATHS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::PRODUCE_ICE,
 +        #[cfg(feature = "internal-lints")]
 +        utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +        absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
 +        approx_const::APPROX_CONSTANT,
 +        arithmetic::FLOAT_ARITHMETIC,
 +        arithmetic::INTEGER_ARITHMETIC,
 +        as_conversions::AS_CONVERSIONS,
 +        asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +        asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +        assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +        assign_ops::ASSIGN_OP_PATTERN,
 +        assign_ops::MISREFACTORED_ASSIGN_OP,
 +        async_yields_async::ASYNC_YIELDS_ASYNC,
 +        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,
++        disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
++        disallowed_type::DISALLOWED_TYPE,
 +        doc::DOC_MARKDOWN,
 +        doc::MISSING_ERRORS_DOC,
 +        doc::MISSING_PANICS_DOC,
 +        doc::MISSING_SAFETY_DOC,
 +        doc::NEEDLESS_DOCTEST_MAIN,
 +        double_comparison::DOUBLE_COMPARISONS,
 +        double_parens::DOUBLE_PARENS,
 +        drop_forget_ref::DROP_COPY,
 +        drop_forget_ref::DROP_REF,
 +        drop_forget_ref::FORGET_COPY,
 +        drop_forget_ref::FORGET_REF,
 +        duration_subsec::DURATION_SUBSEC,
 +        else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +        empty_enum::EMPTY_ENUM,
 +        entry::MAP_ENTRY,
 +        enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +        enum_variants::ENUM_VARIANT_NAMES,
 +        enum_variants::MODULE_INCEPTION,
 +        enum_variants::MODULE_NAME_REPETITIONS,
 +        eq_op::EQ_OP,
 +        eq_op::OP_REF,
 +        erasing_op::ERASING_OP,
 +        escape::BOXED_LOCAL,
 +        eta_reduction::REDUNDANT_CLOSURE,
 +        eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +        eval_order_dependence::DIVERGING_SUB_EXPRESSION,
 +        eval_order_dependence::EVAL_ORDER_DEPENDENCE,
 +        excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +        excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +        exhaustive_items::EXHAUSTIVE_ENUMS,
 +        exhaustive_items::EXHAUSTIVE_STRUCTS,
 +        exit::EXIT,
 +        explicit_write::EXPLICIT_WRITE,
 +        fallible_impl_from::FALLIBLE_IMPL_FROM,
 +        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,
-         LintId::of(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::APPEND_INSTEAD_OF_EXTEND,
 +        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::MANUAL_STR_REPEAT,
 +        methods::MAP_COLLECT_RESULT_UNIT,
 +        methods::MAP_FLATTEN,
++        methods::MAP_IDENTITY,
 +        methods::MAP_UNWRAP_OR,
 +        methods::NEW_RET_NO_SELF,
 +        methods::OK_EXPECT,
 +        methods::OPTION_AS_REF_DEREF,
 +        methods::OPTION_FILTER_MAP,
 +        methods::OPTION_MAP_OR_NONE,
 +        methods::OR_FUN_CALL,
 +        methods::RESULT_MAP_OR_INTO_OPTION,
 +        methods::SEARCH_IS_SOME,
 +        methods::SHOULD_IMPLEMENT_TRAIT,
 +        methods::SINGLE_CHAR_ADD_STR,
 +        methods::SINGLE_CHAR_PATTERN,
 +        methods::SKIP_WHILE_NEXT,
 +        methods::STRING_EXTEND_CHARS,
 +        methods::SUSPICIOUS_MAP,
 +        methods::SUSPICIOUS_SPLITN,
 +        methods::UNINIT_ASSUMED_INIT,
 +        methods::UNNECESSARY_FILTER_MAP,
 +        methods::UNNECESSARY_FOLD,
 +        methods::UNNECESSARY_LAZY_EVALUATIONS,
 +        methods::UNWRAP_USED,
 +        methods::USELESS_ASREF,
 +        methods::WRONG_SELF_CONVENTION,
 +        methods::ZST_OFFSET,
 +        minmax::MIN_MAX,
 +        misc::CMP_NAN,
 +        misc::CMP_OWNED,
 +        misc::FLOAT_CMP,
 +        misc::FLOAT_CMP_CONST,
 +        misc::MODULO_ONE,
 +        misc::SHORT_CIRCUIT_STATEMENT,
 +        misc::TOPLEVEL_REF_ARG,
 +        misc::USED_UNDERSCORE_BINDING,
 +        misc::ZERO_PTR,
 +        misc_early::BUILTIN_TYPE_SHADOW,
 +        misc_early::DOUBLE_NEG,
 +        misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +        misc_early::MIXED_CASE_HEX_LITERALS,
 +        misc_early::REDUNDANT_PATTERN,
 +        misc_early::UNNEEDED_FIELD_PATTERN,
 +        misc_early::UNNEEDED_WILDCARD_PATTERN,
 +        misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +        misc_early::ZERO_PREFIXED_LITERAL,
 +        missing_const_for_fn::MISSING_CONST_FOR_FN,
 +        missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
++        missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +        missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +        modulo_arithmetic::MODULO_ARITHMETIC,
 +        multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
 +        mut_key::MUTABLE_KEY_TYPE,
 +        mut_mut::MUT_MUT,
 +        mut_mutex_lock::MUT_MUTEX_LOCK,
 +        mut_reference::UNNECESSARY_MUT_PASSED,
 +        mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +        mutex_atomic::MUTEX_ATOMIC,
 +        mutex_atomic::MUTEX_INTEGER,
 +        needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +        needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
 +        needless_bool::BOOL_COMPARISON,
 +        needless_bool::NEEDLESS_BOOL,
 +        needless_borrow::NEEDLESS_BORROW,
 +        needless_borrow::REF_BINDING_TO_REFERENCE,
 +        needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +        needless_continue::NEEDLESS_CONTINUE,
 +        needless_for_each::NEEDLESS_FOR_EACH,
 +        needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +        needless_question_mark::NEEDLESS_QUESTION_MARK,
 +        needless_update::NEEDLESS_UPDATE,
 +        neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +        neg_multiply::NEG_MULTIPLY,
 +        new_without_default::NEW_WITHOUT_DEFAULT,
 +        no_effect::NO_EFFECT,
 +        no_effect::UNNECESSARY_OPERATION,
 +        non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +        non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +        non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +        non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +        non_expressive_names::SIMILAR_NAMES,
 +        non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
++        nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
 +        open_options::NONSENSICAL_OPEN_OPTIONS,
 +        option_env_unwrap::OPTION_ENV_UNWRAP,
 +        option_if_let_else::OPTION_IF_LET_ELSE,
 +        overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +        panic_in_result_fn::PANIC_IN_RESULT_FN,
 +        panic_unimplemented::PANIC,
 +        panic_unimplemented::TODO,
 +        panic_unimplemented::UNIMPLEMENTED,
 +        panic_unimplemented::UNREACHABLE,
 +        partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +        pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +        pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +        path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +        pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +        precedence::PRECEDENCE,
 +        ptr::CMP_NULL,
 +        ptr::INVALID_NULL_PTR_USAGE,
 +        ptr::MUT_FROM_REF,
 +        ptr::PTR_ARG,
 +        ptr_eq::PTR_EQ,
 +        ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +        question_mark::QUESTION_MARK,
 +        ranges::MANUAL_RANGE_CONTAINS,
 +        ranges::RANGE_MINUS_ONE,
 +        ranges::RANGE_PLUS_ONE,
 +        ranges::RANGE_ZIP_WITH_LEN,
 +        ranges::REVERSED_EMPTY_RANGES,
 +        redundant_clone::REDUNDANT_CLONE,
 +        redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +        redundant_else::REDUNDANT_ELSE,
 +        redundant_field_names::REDUNDANT_FIELD_NAMES,
 +        redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +        redundant_slicing::REDUNDANT_SLICING,
 +        redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +        ref_option_ref::REF_OPTION_REF,
 +        reference::DEREF_ADDROF,
 +        reference::REF_IN_DEREF,
 +        regex::INVALID_REGEX,
 +        regex::TRIVIAL_REGEX,
 +        repeat_once::REPEAT_ONCE,
 +        returns::LET_AND_RETURN,
 +        returns::NEEDLESS_RETURN,
 +        self_assignment::SELF_ASSIGNMENT,
 +        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_async::UNUSED_ASYNC,
 +        unused_io_amount::UNUSED_IO_AMOUNT,
 +        unused_self::UNUSED_SELF,
 +        unused_unit::UNUSED_UNIT,
 +        unwrap::PANICKING_UNWRAP,
 +        unwrap::UNNECESSARY_UNWRAP,
 +        unwrap_in_result::UNWRAP_IN_RESULT,
 +        upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +        use_self::USE_SELF,
 +        useless_conversion::USELESS_CONVERSION,
 +        vec::USELESS_VEC,
 +        vec_init_then_push::VEC_INIT_THEN_PUSH,
 +        vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
 +        verbose_file_reads::VERBOSE_FILE_READS,
 +        wildcard_dependencies::WILDCARD_DEPENDENCIES,
 +        wildcard_imports::ENUM_GLOB_USE,
 +        wildcard_imports::WILDCARD_IMPORTS,
 +        write::PRINTLN_EMPTY_STRING,
 +        write::PRINT_LITERAL,
 +        write::PRINT_STDERR,
 +        write::PRINT_STDOUT,
 +        write::PRINT_WITH_NEWLINE,
 +        write::USE_DEBUG,
 +        write::WRITELN_EMPTY_STRING,
 +        write::WRITE_LITERAL,
 +        write::WRITE_WITH_NEWLINE,
 +        zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +        zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +    ]);
 +    // end register lints, do not remove this comment, it’s used in `update_lints`
 +
 +    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +        LintId::of(arithmetic::FLOAT_ARITHMETIC),
 +        LintId::of(arithmetic::INTEGER_ARITHMETIC),
 +        LintId::of(as_conversions::AS_CONVERSIONS),
 +        LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +        LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +        LintId::of(create_dir::CREATE_DIR),
 +        LintId::of(dbg_macro::DBG_MACRO),
 +        LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
++        LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +        LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +        LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +        LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +        LintId::of(exit::EXIT),
 +        LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +        LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +        LintId::of(implicit_return::IMPLICIT_RETURN),
 +        LintId::of(indexing_slicing::INDEXING_SLICING),
 +        LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +        LintId::of(integer_division::INTEGER_DIVISION),
 +        LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +        LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +        LintId::of(map_err_ignore::MAP_ERR_IGNORE),
 +        LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +        LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +        LintId::of(mem_forget::MEM_FORGET),
 +        LintId::of(methods::CLONE_ON_REF_PTR),
 +        LintId::of(methods::EXPECT_USED),
 +        LintId::of(methods::FILETYPE_IS_FILE),
 +        LintId::of(methods::GET_UNWRAP),
 +        LintId::of(methods::UNWRAP_USED),
 +        LintId::of(misc::FLOAT_CMP_CONST),
 +        LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +        LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
++        LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +        LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +        LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
 +        LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +        LintId::of(panic_unimplemented::PANIC),
 +        LintId::of(panic_unimplemented::TODO),
 +        LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +        LintId::of(panic_unimplemented::UNREACHABLE),
 +        LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +        LintId::of(shadow::SHADOW_REUSE),
 +        LintId::of(shadow::SHADOW_SAME),
 +        LintId::of(strings::STRING_ADD),
 +        LintId::of(strings::STRING_TO_STRING),
 +        LintId::of(strings::STR_TO_STRING),
 +        LintId::of(types::RC_BUFFER),
 +        LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +        LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +        LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
 +        LintId::of(write::PRINT_STDERR),
 +        LintId::of(write::PRINT_STDOUT),
 +        LintId::of(write::USE_DEBUG),
 +    ]);
 +
 +    store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
 +        LintId::of(attrs::INLINE_ALWAYS),
 +        LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +        LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +        LintId::of(bit_mask::VERBOSE_BIT_MASK),
 +        LintId::of(bytecount::NAIVE_BYTECOUNT),
 +        LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +        LintId::of(casts::CAST_LOSSLESS),
 +        LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +        LintId::of(casts::CAST_POSSIBLE_WRAP),
 +        LintId::of(casts::CAST_PRECISION_LOSS),
 +        LintId::of(casts::CAST_PTR_ALIGNMENT),
 +        LintId::of(casts::CAST_SIGN_LOSS),
 +        LintId::of(casts::PTR_AS_PTR),
 +        LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +        LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +        LintId::of(copy_iterator::COPY_ITERATOR),
 +        LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +        LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +        LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +        LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +        LintId::of(doc::DOC_MARKDOWN),
 +        LintId::of(doc::MISSING_ERRORS_DOC),
 +        LintId::of(doc::MISSING_PANICS_DOC),
 +        LintId::of(empty_enum::EMPTY_ENUM),
 +        LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
 +        LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
 +        LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
 +        LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
 +        LintId::of(functions::MUST_USE_CANDIDATE),
 +        LintId::of(functions::TOO_MANY_LINES),
 +        LintId::of(if_not_else::IF_NOT_ELSE),
 +        LintId::of(implicit_hasher::IMPLICIT_HASHER),
 +        LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +        LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +        LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +        LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +        LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +        LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +        LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +        LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +        LintId::of(literal_representation::UNREADABLE_LITERAL),
 +        LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +        LintId::of(loops::EXPLICIT_ITER_LOOP),
 +        LintId::of(macro_use::MACRO_USE_IMPORTS),
 +        LintId::of(manual_ok_or::MANUAL_OK_OR),
 +        LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
 +        LintId::of(matches::MATCH_BOOL),
 +        LintId::of(matches::MATCH_SAME_ARMS),
 +        LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +        LintId::of(matches::MATCH_WILD_ERR_ARM),
 +        LintId::of(matches::SINGLE_MATCH_ELSE),
 +        LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
 +        LintId::of(methods::FILTER_MAP_NEXT),
 +        LintId::of(methods::FLAT_MAP_OPTION),
++        LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +        LintId::of(methods::IMPLICIT_CLONE),
 +        LintId::of(methods::INEFFICIENT_TO_STRING),
 +        LintId::of(methods::MAP_FLATTEN),
 +        LintId::of(methods::MAP_UNWRAP_OR),
 +        LintId::of(misc::USED_UNDERSCORE_BINDING),
 +        LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +        LintId::of(mut_mut::MUT_MUT),
 +        LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
 +        LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
 +        LintId::of(needless_continue::NEEDLESS_CONTINUE),
 +        LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
 +        LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
 +        LintId::of(non_expressive_names::SIMILAR_NAMES),
 +        LintId::of(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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +        LintId::of(shadow::SHADOW_UNRELATED),
 +        LintId::of(strings::STRING_ADD_ASSIGN),
 +        LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
 +        LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
 +        LintId::of(types::LINKEDLIST),
 +        LintId::of(types::OPTION_OPTION),
 +        LintId::of(unicode::NON_ASCII_LITERAL),
 +        LintId::of(unicode::UNICODE_NOT_NFC),
 +        LintId::of(unit_types::LET_UNIT_VALUE),
 +        LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
 +        LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
 +        LintId::of(unused_async::UNUSED_ASYNC),
 +        LintId::of(unused_self::UNUSED_SELF),
 +        LintId::of(wildcard_imports::ENUM_GLOB_USE),
 +        LintId::of(wildcard_imports::WILDCARD_IMPORTS),
 +        LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
 +    ]);
 +
 +    #[cfg(feature = "internal-lints")]
 +    store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
 +        LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
 +        LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
 +        LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
 +        LintId::of(utils::internal_lints::DEFAULT_LINT),
 +        LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
 +        LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
 +        LintId::of(utils::internal_lints::INVALID_PATHS),
 +        LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
 +        LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
 +        LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
 +        LintId::of(utils::internal_lints::PRODUCE_ICE),
 +        LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
 +    ]);
 +
 +    store.register_group(true, "clippy::all", Some("clippy"), vec![
 +        LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
 +        LintId::of(approx_const::APPROX_CONSTANT),
 +        LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +        LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +        LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +        LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +        LintId::of(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(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(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +        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::APPEND_INSTEAD_OF_EXTEND),
 +        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(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +        LintId::of(methods::INSPECT_FOR_EACH),
 +        LintId::of(methods::INTO_ITER_ON_REF),
 +        LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +        LintId::of(methods::ITER_CLONED_COLLECT),
 +        LintId::of(methods::ITER_COUNT),
 +        LintId::of(methods::ITER_NEXT_SLICE),
 +        LintId::of(methods::ITER_NTH),
 +        LintId::of(methods::ITER_NTH_ZERO),
 +        LintId::of(methods::ITER_SKIP_NEXT),
 +        LintId::of(methods::MANUAL_FILTER_MAP),
 +        LintId::of(methods::MANUAL_FIND_MAP),
 +        LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +        LintId::of(methods::MANUAL_STR_REPEAT),
 +        LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
++        LintId::of(methods::MAP_IDENTITY),
 +        LintId::of(methods::NEW_RET_NO_SELF),
 +        LintId::of(methods::OK_EXPECT),
 +        LintId::of(methods::OPTION_AS_REF_DEREF),
 +        LintId::of(methods::OPTION_FILTER_MAP),
 +        LintId::of(methods::OPTION_MAP_OR_NONE),
 +        LintId::of(methods::OR_FUN_CALL),
 +        LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +        LintId::of(methods::SEARCH_IS_SOME),
 +        LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +        LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +        LintId::of(methods::SINGLE_CHAR_PATTERN),
 +        LintId::of(methods::SKIP_WHILE_NEXT),
 +        LintId::of(methods::STRING_EXTEND_CHARS),
 +        LintId::of(methods::SUSPICIOUS_MAP),
 +        LintId::of(methods::SUSPICIOUS_SPLITN),
 +        LintId::of(methods::UNINIT_ASSUMED_INIT),
 +        LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +        LintId::of(methods::UNNECESSARY_FOLD),
 +        LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +        LintId::of(methods::USELESS_ASREF),
 +        LintId::of(methods::WRONG_SELF_CONVENTION),
 +        LintId::of(methods::ZST_OFFSET),
 +        LintId::of(minmax::MIN_MAX),
 +        LintId::of(misc::CMP_NAN),
 +        LintId::of(misc::CMP_OWNED),
 +        LintId::of(misc::FLOAT_CMP),
 +        LintId::of(misc::MODULO_ONE),
 +        LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +        LintId::of(misc::TOPLEVEL_REF_ARG),
 +        LintId::of(misc::ZERO_PTR),
 +        LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +        LintId::of(misc_early::DOUBLE_NEG),
 +        LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +        LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +        LintId::of(misc_early::REDUNDANT_PATTERN),
 +        LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +        LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +        LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +        LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +        LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +        LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +        LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +        LintId::of(needless_bool::BOOL_COMPARISON),
 +        LintId::of(needless_bool::NEEDLESS_BOOL),
 +        LintId::of(needless_borrow::NEEDLESS_BORROW),
 +        LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +        LintId::of(needless_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(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
 +        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_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(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
-         LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
-         LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +        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(loops::EMPTY_LOOP),
 +        LintId::of(from_over_into::FROM_OVER_INTO),
 +        LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +        LintId::of(functions::DOUBLE_MUST_USE),
 +        LintId::of(functions::MUST_USE_UNIT),
 +        LintId::of(functions::RESULT_UNIT_ERR),
 +        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
 +        LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +        LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +        LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +        LintId::of(len_zero::LEN_ZERO),
 +        LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +        LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
-         LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +        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(assign_ops::MISREFACTORED_ASSIGN_OP),
 +        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(needless_borrow::NEEDLESS_BORROW),
 +        LintId::of(neg_multiply::NEG_MULTIPLY),
 +        LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +        LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +        LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++        LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
 +        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(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(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +        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(loops::MUT_RANGE_BOUND),
 +        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(map_identity::MAP_IDENTITY),
 +        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(methods::SUSPICIOUS_MAP),
 +        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::MAP_IDENTITY),
 +        LintId::of(methods::OPTION_AS_REF_DEREF),
 +        LintId::of(methods::OPTION_FILTER_MAP),
 +        LintId::of(methods::SEARCH_IS_SOME),
 +        LintId::of(methods::SKIP_WHILE_NEXT),
-         LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +        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(loops::FOR_LOOPS_OVER_FALLIBLES),
 +        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(mut_key::MUTABLE_KEY_TYPE),
 +        LintId::of(loops::ITER_NEXT_LOOP),
 +        LintId::of(loops::NEVER_LOOP),
 +        LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +        LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
 +        LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +        LintId::of(methods::CLONE_DOUBLE_REF),
 +        LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +        LintId::of(methods::SUSPICIOUS_SPLITN),
 +        LintId::of(methods::UNINIT_ASSUMED_INIT),
 +        LintId::of(methods::ZST_OFFSET),
 +        LintId::of(minmax::MIN_MAX),
 +        LintId::of(misc::CMP_NAN),
 +        LintId::of(misc::FLOAT_CMP),
 +        LintId::of(misc::MODULO_ONE),
-         LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
-         LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +        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),
-     store.register_late_pass(|| box map_identity::MapIdentity);
 +        LintId::of(swap::ALMOST_SWAPPED),
 +        LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
 +        LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +        LintId::of(transmute::WRONG_TRANSMUTE),
 +        LintId::of(transmuting_null::TRANSMUTING_NULL),
 +        LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
 +        LintId::of(unicode::INVISIBLE_CHARACTERS),
 +        LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +        LintId::of(unit_types::UNIT_CMP),
 +        LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +        LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +        LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +        LintId::of(unwrap::PANICKING_UNWRAP),
 +        LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    ]);
 +
++    store.register_group(true, "clippy::suspicious", None, vec![
++        LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
++        LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
++        LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
++        LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
++        LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
++        LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
++        LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
++        LintId::of(loops::EMPTY_LOOP),
++        LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
++        LintId::of(loops::MUT_RANGE_BOUND),
++        LintId::of(methods::SUSPICIOUS_MAP),
++        LintId::of(mut_key::MUTABLE_KEY_TYPE),
++        LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++        LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++    ]);
++
 +    store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
 +        LintId::of(entry::MAP_ENTRY),
 +        LintId::of(escape::BOXED_LOCAL),
 +        LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +        LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +        LintId::of(loops::MANUAL_MEMCPY),
 +        LintId::of(loops::NEEDLESS_COLLECT),
++        LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
 +        LintId::of(methods::EXPECT_FUN_CALL),
 +        LintId::of(methods::ITER_NTH),
 +        LintId::of(methods::MANUAL_STR_REPEAT),
 +        LintId::of(methods::OR_FUN_CALL),
 +        LintId::of(methods::SINGLE_CHAR_PATTERN),
 +        LintId::of(misc::CMP_OWNED),
 +        LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +        LintId::of(redundant_clone::REDUNDANT_CLONE),
 +        LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +        LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +        LintId::of(types::BOX_VEC),
 +        LintId::of(types::REDUNDANT_ALLOCATION),
 +        LintId::of(vec::USELESS_VEC),
 +        LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    ]);
 +
 +    store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
 +        LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
 +        LintId::of(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(disallowed_type::DISALLOWED_TYPE),
 +        LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +        LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +        LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +        LintId::of(future_not_send::FUTURE_NOT_SEND),
 +        LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +        LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +        LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +        LintId::of(mutex_atomic::MUTEX_INTEGER),
 +        LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
 +        LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
 +        LintId::of(regex::TRIVIAL_REGEX),
 +        LintId::of(strings::STRING_LIT_AS_BYTES),
 +        LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +        LintId::of(transmute::USELESS_TRANSMUTE),
 +        LintId::of(use_self::USE_SELF),
 +    ]);
 +
 +    #[cfg(feature = "metadata-collector-lint")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new());
 +            return;
 +        }
 +    }
 +
 +    // 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);
 +    }
 +
 +    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 needless_bitwise_bool::NeedlessBitwiseBool);
 +    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
 +        })
 +    });
 +
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move || box methods::Methods::new(avoid_breaking_exported_api, 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,
 +        conf.avoid_breaking_exported_api,
 +        &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);
 +    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(move || box unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api));
 +    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_late_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api));
 +    store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive));
 +    store.register_late_pass(|| box default::Default::default());
 +    store.register_late_pass(|| 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,
 +    });
++    let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
++    store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(&macro_matcher));
 +    store.register_late_pass(|| box macro_use::MacroUseImports::default());
 +    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_late_pass(|| box unused_async::UnusedAsync);
++    let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
++    store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));
++    let import_renames = conf.enforced_import_renames.clone();
++    store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone()));
++    let scripts = conf.allowed_scripts.clone();
++    store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
 +}
 +
 +#[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 c91fe88757ec719d1b59d3162c14e06a7818f511,0000000000000000000000000000000000000000..a98e2dc1372db0d3782898625e3db5267cd8b8f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,455 -1,0 +1,455 @@@
-             if method.ident.name == sym!(len);
 +use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::walk_block;
 +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Pat, PatKind, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::sym;
 +use std::iter::Iterator;
 +
 +/// Checks for for loops that sequentially copy items from one slice-like
 +/// object to another.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> bool {
 +    if let Some(higher::Range {
 +        start: Some(start),
 +        end: Some(end),
 +        limits,
 +    }) = higher::range(arg)
 +    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
 +            let mut starts = vec![Start {
 +                id: canonical_id,
 +                kind: StartKind::Range,
 +            }];
 +
 +            // This is one of few ways to return different iterators
 +            // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
 +            let mut iter_a = None;
 +            let mut iter_b = None;
 +
 +            if let ExprKind::Block(block, _) = body.kind {
 +                if let Some(loop_counters) = get_loop_counters(cx, block, expr) {
 +                    starts.extend(loop_counters);
 +                }
 +                iter_a = Some(get_assignments(block, &starts));
 +            } else {
 +                iter_b = Some(get_assignment(body));
 +            }
 +
 +            let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter());
 +
 +            let big_sugg = assignments
 +                // The only statements in the for loops can be indexed assignments from
 +                // indexed retrievals (except increments of loop counters).
 +                .map(|o| {
 +                    o.and_then(|(lhs, rhs)| {
 +                        let rhs = fetch_cloned_expr(rhs);
 +                        if_chain! {
 +                            if let ExprKind::Index(base_left, idx_left) = lhs.kind;
 +                            if let ExprKind::Index(base_right, idx_right) = rhs.kind;
 +                            if is_slice_like(cx, cx.typeck_results().expr_ty(base_left));
 +                            if is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
 +                            if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts);
 +                            if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
 +
 +                            // Source and destination must be different
 +                            if path_to_local(base_left) != path_to_local(base_right);
 +                            then {
 +                                Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
 +                                    IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
 +                            } else {
 +                                None
 +                            }
 +                        }
 +                    })
 +                })
 +                .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src)))
 +                .collect::<Option<Vec<_>>>()
 +                .filter(|v| !v.is_empty())
 +                .map(|v| v.join("\n    "));
 +
 +            if let Some(big_sugg) = big_sugg {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_MEMCPY,
 +                    get_span_of_entire_for_loop(expr),
 +                    "it looks like you're manually copying between slices",
 +                    "try replacing the loop by",
 +                    big_sugg,
 +                    Applicability::Unspecified,
 +                );
 +                return true;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +fn build_manual_memcpy_suggestion<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    start: &Expr<'_>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    dst: &IndexExpr<'_>,
 +    src: &IndexExpr<'_>,
 +) -> String {
 +    fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> {
 +        if offset.as_str() == "0" {
 +            sugg::EMPTY.into()
 +        } else {
 +            offset
 +        }
 +    }
 +
 +    let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
 +        if_chain! {
 +            if let ExprKind::MethodCall(method, _, len_args, _) = end.kind;
++            if method.ident.name == sym::len;
 +            if len_args.len() == 1;
 +            if let Some(arg) = len_args.get(0);
 +            if path_to_local(arg) == path_to_local(base);
 +            then {
 +                if sugg.as_str() == end_str {
 +                    sugg::EMPTY.into()
 +                } else {
 +                    sugg
 +                }
 +            } else {
 +                match limits {
 +                    ast::RangeLimits::Closed => {
 +                        sugg + &sugg::ONE.into()
 +                    },
 +                    ast::RangeLimits::HalfOpen => sugg,
 +                }
 +            }
 +        }
 +    };
 +
 +    let start_str = Sugg::hir(cx, start, "").into();
 +    let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into();
 +
 +    let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx {
 +        StartKind::Range => (
 +            print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(),
 +            print_limit(
 +                end,
 +                end_str.as_str(),
 +                idx_expr.base,
 +                apply_offset(&end_str, &idx_expr.idx_offset),
 +            )
 +            .into_sugg(),
 +        ),
 +        StartKind::Counter { initializer } => {
 +            let counter_start = Sugg::hir(cx, initializer, "").into();
 +            (
 +                print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(),
 +                print_limit(
 +                    end,
 +                    end_str.as_str(),
 +                    idx_expr.base,
 +                    apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str,
 +                )
 +                .into_sugg(),
 +            )
 +        },
 +    };
 +
 +    let (dst_offset, dst_limit) = print_offset_and_limit(dst);
 +    let (src_offset, src_limit) = print_offset_and_limit(src);
 +
 +    let dst_base_str = snippet(cx, dst.base.span, "???");
 +    let src_base_str = snippet(cx, src.base.span, "???");
 +
 +    let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY {
 +        dst_base_str
 +    } else {
 +        format!(
 +            "{}[{}..{}]",
 +            dst_base_str,
 +            dst_offset.maybe_par(),
 +            dst_limit.maybe_par()
 +        )
 +        .into()
 +    };
 +
 +    format!(
 +        "{}.clone_from_slice(&{}[{}..{}]);",
 +        dst,
 +        src_base_str,
 +        src_offset.maybe_par(),
 +        src_limit.maybe_par()
 +    )
 +}
 +
 +/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`;
 +/// and also, it avoids subtracting a variable from the same one by replacing it with `0`.
 +/// it exists for the convenience of the overloaded operators while normal functions can do the
 +/// same.
 +#[derive(Clone)]
 +struct MinifyingSugg<'a>(Sugg<'a>);
 +
 +impl<'a> MinifyingSugg<'a> {
 +    fn as_str(&self) -> &str {
 +        // HACK: Don't sync to Clippy! Required because something with the `or_patterns` feature
 +        // changed and this would now require parentheses.
 +        match &self.0 {
 +            Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) => s.as_ref(),
 +        }
 +    }
 +
 +    fn into_sugg(self) -> Sugg<'a> {
 +        self.0
 +    }
 +}
 +
 +impl<'a> From<Sugg<'a>> for MinifyingSugg<'a> {
 +    fn from(sugg: Sugg<'a>) -> Self {
 +        Self(sugg)
 +    }
 +}
 +
 +impl std::ops::Add for &MinifyingSugg<'static> {
 +    type Output = MinifyingSugg<'static>;
 +    fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
 +        match (self.as_str(), rhs.as_str()) {
 +            ("0", _) => rhs.clone(),
 +            (_, "0") => self.clone(),
 +            (_, _) => (&self.0 + &rhs.0).into(),
 +        }
 +    }
 +}
 +
 +impl std::ops::Sub for &MinifyingSugg<'static> {
 +    type Output = MinifyingSugg<'static>;
 +    fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
 +        match (self.as_str(), rhs.as_str()) {
 +            (_, "0") => self.clone(),
 +            ("0", _) => (-rhs.0.clone()).into(),
 +            (x, y) if x == y => sugg::ZERO.into(),
 +            (_, _) => (&self.0 - &rhs.0).into(),
 +        }
 +    }
 +}
 +
 +impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
 +    type Output = MinifyingSugg<'static>;
 +    fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
 +        match (self.as_str(), rhs.as_str()) {
 +            ("0", _) => rhs.clone(),
 +            (_, "0") => self,
 +            (_, _) => (self.0 + &rhs.0).into(),
 +        }
 +    }
 +}
 +
 +impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
 +    type Output = MinifyingSugg<'static>;
 +    fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
 +        match (self.as_str(), rhs.as_str()) {
 +            (_, "0") => self,
 +            ("0", _) => (-rhs.0.clone()).into(),
 +            (x, y) if x == y => sugg::ZERO.into(),
 +            (_, _) => (self.0 - &rhs.0).into(),
 +        }
 +    }
 +}
 +
 +/// a wrapper around `MinifyingSugg`, which carries a operator like currying
 +/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`).
 +struct Offset {
 +    value: MinifyingSugg<'static>,
 +    sign: OffsetSign,
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OffsetSign {
 +    Positive,
 +    Negative,
 +}
 +
 +impl Offset {
 +    fn negative(value: Sugg<'static>) -> Self {
 +        Self {
 +            value: value.into(),
 +            sign: OffsetSign::Negative,
 +        }
 +    }
 +
 +    fn positive(value: Sugg<'static>) -> Self {
 +        Self {
 +            value: value.into(),
 +            sign: OffsetSign::Positive,
 +        }
 +    }
 +
 +    fn empty() -> Self {
 +        Self::positive(sugg::ZERO)
 +    }
 +}
 +
 +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> {
 +    match rhs.sign {
 +        OffsetSign::Positive => lhs + &rhs.value,
 +        OffsetSign::Negative => lhs - &rhs.value,
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum StartKind<'hir> {
 +    Range,
 +    Counter { initializer: &'hir Expr<'hir> },
 +}
 +
 +struct IndexExpr<'hir> {
 +    base: &'hir Expr<'hir>,
 +    idx: StartKind<'hir>,
 +    idx_offset: Offset,
 +}
 +
 +struct Start<'hir> {
 +    id: HirId,
 +    kind: StartKind<'hir>,
 +}
 +
 +fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
 +    let is_slice = match ty.kind() {
 +        ty::Ref(_, subty, _) => is_slice_like(cx, subty),
 +        ty::Slice(..) | ty::Array(..) => true,
 +        _ => false,
 +    };
 +
 +    is_slice || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
 +}
 +
 +fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
 +        if method.ident.name == sym::clone;
 +        if args.len() == 1;
 +        if let Some(arg) = args.get(0);
 +        then { arg } else { expr }
 +    }
 +}
 +
 +fn get_details_from_idx<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    idx: &Expr<'_>,
 +    starts: &[Start<'tcx>],
 +) -> Option<(StartKind<'tcx>, Offset)> {
 +    fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> {
 +        let id = path_to_local(e)?;
 +        starts.iter().find(|start| start.id == id).map(|start| start.kind)
 +    }
 +
 +    fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<Sugg<'static>> {
 +        match &e.kind {
 +            ExprKind::Lit(l) => match l.node {
 +                ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())),
 +                _ => None,
 +            },
 +            ExprKind::Path(..) if get_start(e, starts).is_none() => Some(Sugg::hir(cx, e, "???")),
 +            _ => None,
 +        }
 +    }
 +
 +    match idx.kind {
 +        ExprKind::Binary(op, lhs, rhs) => match op.node {
 +            BinOpKind::Add => {
 +                let offset_opt = get_start(lhs, starts)
 +                    .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o)))
 +                    .or_else(|| get_start(rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o))));
 +
 +                offset_opt.map(|(s, o)| (s, Offset::positive(o)))
 +            },
 +            BinOpKind::Sub => {
 +                get_start(lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o))))
 +            },
 +            _ => None,
 +        },
 +        ExprKind::Path(..) => get_start(idx, starts).map(|s| (s, Offset::empty())),
 +        _ => None,
 +    }
 +}
 +
 +fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    if let ExprKind::Assign(lhs, rhs, _) = e.kind {
 +        Some((lhs, rhs))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Get assignments from the given block.
 +/// The returned iterator yields `None` if no assignment expressions are there,
 +/// filtering out the increments of the given whitelisted loop counters;
 +/// because its job is to make sure there's nothing other than assignments and the increments.
 +fn get_assignments<'a, 'tcx>(
 +    Block { stmts, expr, .. }: &'tcx Block<'tcx>,
 +    loop_counters: &'a [Start<'tcx>],
 +) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'a {
 +    // As the `filter` and `map` below do different things, I think putting together
 +    // just increases complexity. (cc #3188 and #4193)
 +    stmts
 +        .iter()
 +        .filter_map(move |stmt| match stmt.kind {
 +            StmtKind::Local(..) | StmtKind::Item(..) => None,
 +            StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
 +        })
 +        .chain((*expr).into_iter())
 +        .filter(move |e| {
 +            if let ExprKind::AssignOp(_, place, _) = e.kind {
 +                path_to_local(place).map_or(false, |id| {
 +                    !loop_counters
 +                        .iter()
 +                        // skip the first item which should be `StartKind::Range`
 +                        // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop.
 +                        .skip(1)
 +                        .any(|counter| counter.id == id)
 +                })
 +            } else {
 +                true
 +            }
 +        })
 +        .map(get_assignment)
 +}
 +
 +fn get_loop_counters<'a, 'tcx>(
 +    cx: &'a LateContext<'tcx>,
 +    body: &'tcx Block<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<impl Iterator<Item = Start<'tcx>> + 'a> {
 +    // Look for variables that are incremented once per loop iteration.
 +    let mut increment_visitor = IncrementVisitor::new(cx);
 +    walk_block(&mut increment_visitor, body);
 +
 +    // For each candidate, check the parent block to see if
 +    // it's initialized to zero at the start of the loop.
 +    get_enclosing_block(cx, expr.hir_id).and_then(|block| {
 +        increment_visitor
 +            .into_results()
 +            .filter_map(move |var_id| {
 +                let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id);
 +                walk_block(&mut initialize_visitor, block);
 +
 +                initialize_visitor.get_result().map(|(_, initializer)| Start {
 +                    id: var_id,
 +                    kind: StartKind::Counter { initializer },
 +                })
 +            })
 +            .into()
 +    })
 +}
index a4bc3e6bd100cd4c47c9b31febd8144209b183a2,0000000000000000000000000000000000000000..56a123b69c6aec1de3d3bf1ea6140a13c29da0eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,624 -1,0 +1,624 @@@
-     correctness,
 +mod empty_loop;
 +mod explicit_counter_loop;
 +mod explicit_into_iter_loop;
 +mod explicit_iter_loop;
 +mod for_kv_map;
 +mod for_loops_over_fallibles;
 +mod iter_next_loop;
 +mod manual_flatten;
 +mod manual_memcpy;
 +mod mut_range_bound;
 +mod needless_collect;
 +mod needless_range_loop;
 +mod never_loop;
 +mod same_item_push;
 +mod single_element_loop;
 +mod utils;
 +mod while_immutable_condition;
 +mod while_let_loop;
 +mod while_let_on_iterator;
 +
 +use clippy_utils::higher;
 +use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for for-loops that manually copy items between
 +    /// slices that could be optimized by having a memcpy.
 +    ///
 +    /// **Why is this bad?** It is not as fast as a memcpy.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// for i in 0..src.len() {
 +    ///     dst[i + 64] = src[i];
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
 +    /// ```
 +    pub MANUAL_MEMCPY,
 +    perf,
 +    "manually copying items between slices"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for looping over the range of `0..len` of some
 +    /// collection just to get the values by index.
 +    ///
 +    /// **Why is this bad?** Just iterating the collection itself makes the intent
 +    /// more clear and is probably faster.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in 0..vec.len() {
 +    ///     println!("{}", vec[i]);
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in vec {
 +    ///     println!("{}", i);
 +    /// }
 +    /// ```
 +    pub NEEDLESS_RANGE_LOOP,
 +    style,
 +    "for-looping over a range of indices where an iterator over items would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// **Why is this bad?** Readability.
 +    ///
 +    /// **Known problems:** False negatives. We currently only warn on some known
 +    /// types.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// // with `y` a `Vec` or slice:
 +    /// # let y = vec![1];
 +    /// for x in y.iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in &y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub EXPLICIT_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// **Why is this bad?** Readability.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// // with `y` a `Vec` or slice:
 +    /// for x in y.into_iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub EXPLICIT_INTO_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.into_iter()` when `_` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for loops on `x.next()`.
 +    ///
 +    /// **Why is this bad?** `next()` returns either `Some(value)` if there was a
 +    /// value, or `None` otherwise. The insidious thing is that `Option<_>`
 +    /// implements `IntoIterator`, so that possibly one value will be iterated,
 +    /// leading to some hard to find bugs. No one will want to write such code
 +    /// [except to win an Underhanded Rust
 +    /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// for x in y.next() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub ITER_NEXT_LOOP,
 +    correctness,
 +    "for-looping over `_.next()` which is probably not intended"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `for` loops over `Option` or `Result` values.
 +    ///
 +    /// **Why is this bad?** Readability. This is more clearly expressed as an `if
 +    /// let`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// for x in opt {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Some(x) = opt {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<i32, std::io::Error> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// for x in &res {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Ok(x) = res {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub FOR_LOOPS_OVER_FALLIBLES,
-     style,
++    suspicious,
 +    "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Detects `loop + match` combinations that are easier
 +    /// written as a `while let` loop.
 +    ///
 +    /// **Why is this bad?** The `while let` loop is usually shorter and more
 +    /// readable.
 +    ///
 +    /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
 +    ///
 +    /// **Example:**
 +    /// ```rust,no_run
 +    /// # let y = Some(1);
 +    /// loop {
 +    ///     let x = match y {
 +    ///         Some(x) => x,
 +    ///         None => break,
 +    ///     };
 +    ///     // .. do something with x
 +    /// }
 +    /// // is easier written as
 +    /// while let Some(x) = y {
 +    ///     // .. do something with x
 +    /// };
 +    /// ```
 +    pub WHILE_LET_LOOP,
 +    complexity,
 +    "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for functions collecting an iterator when collect
 +    /// is not needed.
 +    ///
 +    /// **Why is this bad?** `collect` causes the allocation of a new data structure,
 +    /// when this allocation may not be needed.
 +    ///
 +    /// **Known problems:**
 +    /// None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let iterator = vec![1].into_iter();
 +    /// let len = iterator.clone().collect::<Vec<_>>().len();
 +    /// // should be
 +    /// let len = iterator.count();
 +    /// ```
 +    pub NEEDLESS_COLLECT,
 +    perf,
 +    "collecting an iterator when collect is not needed"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks `for` loops over slices with an explicit counter
 +    /// and suggests the use of `.enumerate()`.
 +    ///
 +    /// **Why is it bad?** Using `.enumerate()` makes the intent more clear,
 +    /// declutters the code and may be faster in some instances.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// let mut i = 0;
 +    /// for item in &v {
 +    ///     bar(i, *item);
 +    ///     i += 1;
 +    /// }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
 +    /// ```
 +    pub EXPLICIT_COUNTER_LOOP,
 +    complexity,
 +    "for-looping with an explicit counter when `_.enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for empty `loop` expressions.
 +    ///
 +    /// **Why is this bad?** These busy loops burn CPU cycles without doing
 +    /// anything. It is _almost always_ a better idea to `panic!` than to have
 +    /// a busy loop.
 +    ///
 +    /// If panicking isn't possible, think of the environment and either:
 +    ///   - block on something
 +    ///   - sleep the thread for some microseconds
 +    ///   - yield or pause the thread
 +    ///
 +    /// For `std` targets, this can be done with
 +    /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
 +    /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
 +    ///
 +    /// For `no_std` targets, doing this is more complicated, especially because
 +    /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
 +    /// probably need to invoke some target-specific intrinsic. Examples include:
 +    ///   - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
 +    ///   - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```no_run
 +    /// loop {}
 +    /// ```
 +    pub EMPTY_LOOP,
-     complexity,
++    suspicious,
 +    "empty `loop {}`, which should block or sleep"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `while let` expressions on iterators.
 +    ///
 +    /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys
 +    /// the intent better.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// while let Some(val) = iter() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub WHILE_LET_ON_ITERATOR,
 +    style,
 +    "using a `while let` loop instead of a for loop on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and
 +    /// ignoring either the keys or values.
 +    ///
 +    /// **Why is this bad?** Readability. There are `keys` and `values` methods that
 +    /// can be used to express that don't need the values or keys.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// for (k, _) in &map {
 +    ///     ..
 +    /// }
 +    /// ```
 +    ///
 +    /// could be replaced by
 +    ///
 +    /// ```ignore
 +    /// for k in map.keys() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    pub FOR_KV_MAP,
 +    style,
 +    "looping on a map using `iter` when `keys` or `values` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for loops that will always `break`, `return` or
 +    /// `continue` an outer loop.
 +    ///
 +    /// **Why is this bad?** This loop never loops, all it does is obfuscating the
 +    /// code.
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// loop {
 +    ///     ..;
 +    ///     break;
 +    /// }
 +    /// ```
 +    pub NEVER_LOOP,
 +    correctness,
 +    "any loop that will always `break` or `return`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for loops which have a range bound that is a mutable variable
 +    ///
 +    /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds
 +    ///
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let mut foo = 42;
 +    /// for i in 0..foo {
 +    ///     foo -= 1;
 +    ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
 +    /// }
 +    /// ```
 +    pub MUT_RANGE_BOUND,
++    suspicious,
 +    "for loop over a range where one of the bounds is a mutable variable"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks whether variables used within while loop condition
 +    /// can be (and are) mutated in the body.
 +    ///
 +    /// **Why is this bad?** If the condition is unchanged, entering the body of the loop
 +    /// will lead to an infinite loop.
 +    ///
 +    /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the
 +    /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
 +    /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let i = 0;
 +    /// while i > 10 {
 +    ///     println!("let me loop forever!");
 +    /// }
 +    /// ```
 +    pub WHILE_IMMUTABLE_CONDITION,
 +    correctness,
 +    "variables used within while expression are not mutated in the body"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks whether a for loop is being used to push a constant
 +    /// value into a Vec.
 +    ///
 +    /// **Why is this bad?** This kind of operation can be expressed more succinctly with
 +    /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
 +    /// have better performance.
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = Vec::new();
 +    /// for _ in 0..20 {
 +    ///    vec.push(item1);
 +    /// }
 +    /// for _ in 0..30 {
 +    ///     vec.push(item2);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = vec![item1; 20];
 +    /// vec.resize(20 + 30, item2);
 +    /// ```
 +    pub SAME_ITEM_PUSH,
 +    style,
 +    "the same item is pushed inside of a for loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks whether a for loop has a single element.
 +    ///
 +    /// **Why is this bad?** There is no reason to have a loop of a
 +    /// single element.
 +    /// **Known problems:** None
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// for item in &[item1] {
 +    ///     println!("{}", item);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item = &item1;
 +    /// println!("{}", item);
 +    /// ```
 +    pub SINGLE_ELEMENT_LOOP,
 +    complexity,
 +    "there is no reason to have a single element loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Check for unnecessary `if let` usage in a for loop
 +    /// where only the `Some` or `Ok` variant of the iterator element is used.
 +    ///
 +    /// **Why is this bad?** It is verbose and can be simplified
 +    /// by first calling the `flatten` method on the `Iterator`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x {
 +    ///     if let Some(n) = n {
 +    ///         println!("{}", n);
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x.into_iter().flatten() {
 +    ///     println!("{}", n);
 +    /// }
 +    /// ```
 +    pub MANUAL_FLATTEN,
 +    complexity,
 +    "for loops over `Option`s or `Result`s with a single expression can be simplified"
 +}
 +
 +declare_lint_pass!(Loops => [
 +    MANUAL_MEMCPY,
 +    MANUAL_FLATTEN,
 +    NEEDLESS_RANGE_LOOP,
 +    EXPLICIT_ITER_LOOP,
 +    EXPLICIT_INTO_ITER_LOOP,
 +    ITER_NEXT_LOOP,
 +    FOR_LOOPS_OVER_FALLIBLES,
 +    WHILE_LET_LOOP,
 +    NEEDLESS_COLLECT,
 +    EXPLICIT_COUNTER_LOOP,
 +    EMPTY_LOOP,
 +    WHILE_LET_ON_ITERATOR,
 +    FOR_KV_MAP,
 +    NEVER_LOOP,
 +    MUT_RANGE_BOUND,
 +    WHILE_IMMUTABLE_CONDITION,
 +    SAME_ITEM_PUSH,
 +    SINGLE_ELEMENT_LOOP,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Loops {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((pat, arg, body, span)) = higher::for_loop(expr) {
 +            // we don't want to check expanded macros
 +            // this check is not at the top of the function
 +            // since higher::for_loop expressions are marked as expansions
 +            if body.span.from_expansion() {
 +                return;
 +            }
 +            check_for_loop(cx, pat, arg, body, expr, span);
 +        }
 +
 +        // we don't want to check expanded macros
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // check for never_loop
 +        never_loop::check(cx, expr);
 +
 +        // check for `loop { if let {} else break }` that could be `while let`
 +        // (also matches an explicit "match" instead of "if let")
 +        // (even if the "match" or "if let" is used for declaration)
 +        if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
 +            // also check for empty `loop {}` statements, skipping those in #[panic_handler]
 +            empty_loop::check(cx, expr, block);
 +            while_let_loop::check(cx, expr, block);
 +        }
 +
 +        while_let_on_iterator::check(cx, expr);
 +
 +        if let Some((cond, body)) = higher::while_loop(expr) {
 +            while_immutable_condition::check(cx, cond, body);
 +        }
 +
 +        needless_collect::check(expr, cx);
 +    }
 +}
 +
 +fn check_for_loop<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +    span: Span,
 +) {
 +    let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
 +    if !is_manual_memcpy_triggered {
 +        needless_range_loop::check(cx, pat, arg, body, expr);
 +        explicit_counter_loop::check(cx, pat, arg, body, expr);
 +    }
 +    check_for_loop_arg(cx, pat, arg, expr);
 +    for_kv_map::check(cx, pat, arg, body, expr);
 +    mut_range_bound::check(cx, arg, body);
 +    single_element_loop::check(cx, pat, arg, body, expr);
 +    same_item_push::check(cx, pat, arg, body, expr);
 +    manual_flatten::check(cx, pat, arg, body, span);
 +}
 +
 +fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
 +    let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
 +
 +    if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
 +        let method_name = &*method.ident.as_str();
 +        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
 +        match method_name {
 +            "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
 +            "into_iter" => {
 +                explicit_iter_loop::check(cx, self_arg, arg, method_name);
 +                explicit_into_iter_loop::check(cx, self_arg, arg);
 +            },
 +            "next" => {
 +                next_loop_linted = iter_next_loop::check(cx, arg, expr);
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    if !next_loop_linted {
 +        for_loops_over_fallibles::check(cx, pat, arg);
 +    }
 +}
index eb82c9c27c3e192306bb34e502f9b41556ba2468,0000000000000000000000000000000000000000..51d7def137e409eea10816378e774726fb29e026
mode 100644,000000..100644
--- /dev/null
@@@ -1,273 -1,0 +1,248 @@@
- use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
 +use super::NEEDLESS_COLLECT;
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_trait_method, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
- use rustc_span::symbol::{sym, Ident};
++use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
-         if let Some(generic_args) = chain_method.args;
-         if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
-         if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
++use rustc_span::sym;
 +use rustc_span::{MultiSpan, Span};
 +
 +const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 +
 +pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    check_needless_collect_direct_usage(expr, cx);
 +    check_needless_collect_indirect_usage(expr, cx);
 +}
 +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
 +        if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
 +        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
-     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
-     }
 +        then {
++            let ty = cx.typeck_results().expr_ty(&args[0]);
 +            let mut applicability = Applicability::MachineApplicable;
 +            let is_empty_sugg = "next().is_none()".to_string();
 +            let method_name = &*method.ident.name.as_str();
 +            let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
 +                        is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
 +                        is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
 +                        is_type_diagnostic_item(cx, ty, sym::BinaryHeap) {
 +                match method_name {
 +                    "len" => "count()".to_string(),
 +                    "is_empty" => is_empty_sugg,
 +                    "contains" => {
 +                        let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability);
 +                        let (arg, pred) = contains_arg
 +                            .strip_prefix('&')
 +                            .map_or(("&x", &*contains_arg), |s| ("x", s));
 +                        format!("any(|{}| x == {})", arg, pred)
 +                    }
 +                    _ => return,
 +                }
 +            }
 +            else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
 +                is_type_diagnostic_item(cx, ty, sym::hashmap_type) {
 +                match method_name {
 +                    "is_empty" => is_empty_sugg,
 +                    _ => return,
 +                }
 +            }
 +            else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                NEEDLESS_COLLECT,
 +                method0_span.with_hi(expr.span.hi()),
 +                NEEDLESS_COLLECT_MSG,
 +                "replace with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
-                 if let StmtKind::Local(
-                     Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
-                     init: Some(init_expr), ty, .. }
-                 ) = stmt.kind;
 +    if let ExprKind::Block(block, _) = expr.kind {
 +        for stmt in block.stmts {
 +            if_chain! {
-                 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 let StmtKind::Local(local) = stmt.kind;
++                if let PatKind::Binding(_, id, ..) = local.pat.kind;
++                if let Some(init_expr) = local.init;
 +                if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
 +                if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
-                 if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
++                let ty = cx.typeck_results().expr_ty(init_expr);
 +                if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
 +                    is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
 +                    is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
 +                    is_type_diagnostic_item(cx, ty, sym::LinkedList);
-                         id: *pat_id,
++                if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
 +                if let [iter_call] = &*iter_calls;
 +                then {
 +                    let mut used_count_visitor = UsedCountVisitor {
 +                        cx,
-     target: Ident,
++                        id,
 +                        count: 0,
 +                    };
 +                    walk_block(&mut used_count_visitor, block);
 +                    if used_count_visitor.count > 1 {
 +                        return;
 +                    }
 +
 +                    // Suggest replacing iter_call with iter_replacement, and removing stmt
 +                    let mut span = MultiSpan::from_span(collect_span);
 +                    span.push_span_label(iter_call.span, "the iterator could be used here instead".into());
 +                    span_lint_hir_and_then(
 +                        cx,
 +                        super::NEEDLESS_COLLECT,
 +                        init_expr.hir_id,
 +                        span,
 +                        NEEDLESS_COLLECT_MSG,
 +                        |diag| {
 +                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
 +                            diag.multipart_suggestion(
 +                                iter_call.get_suggestion_text(),
 +                                vec![
 +                                    (stmt.span, String::new()),
 +                                    (iter_call.span, iter_replacement)
 +                                ],
 +                                Applicability::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,
-         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 }
-                     ),
++    target: HirId,
 +}
 +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        // Check function calls on our collection
-                 return
++        if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
++            if path_to_local_id(recv, self.target) {
++                match &*method_name.ident.name.as_str() {
++                    "into_iter" => self.uses.push(IterFunction {
++                        func: IterFunctionKind::IntoIter,
++                        span: expr.span,
++                    }),
++                    "len" => self.uses.push(IterFunction {
++                        func: IterFunctionKind::Len,
++                        span: expr.span,
++                    }),
++                    "is_empty" => self.uses.push(IterFunction {
++                        func: IterFunctionKind::IsEmpty,
++                        span: expr.span,
++                    }),
++                    "contains" => self.uses.push(IterFunction {
++                        func: IterFunctionKind::Contains(args[0].span),
++                        span: expr.span,
++                    }),
 +                    _ => self.seen_other = true,
 +                }
-         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);
-             }
++                return;
 +            }
 +        }
 +        // Check if the collection is used for anything else
- fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
++        if path_to_local_id(expr, self.target) {
++            self.seen_other = true;
++        } else {
++            walk_expr(self, expr);
 +        }
 +    }
 +
 +    type Map = Map<'tcx>;
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +struct UsedCountVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    id: HirId,
 +    count: usize,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if path_to_local_id(expr, self.id) {
 +            self.count += 1;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +/// Detect the occurrences of calls to `iter` or `into_iter` for the
 +/// given identifier
-         target: identifier,
++fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
 +    let mut visitor = IterFunctionVisitor {
 +        uses: Vec::new(),
++        target: id,
 +        seen_other: false,
 +    };
 +    visitor.visit_block(block);
 +    if visitor.seen_other { None } else { Some(visitor.uses) }
 +}
index 3065bcc3e6c81e2b70baa3d3f69a92fa9c85524d,0000000000000000000000000000000000000000..3810d0dcc051a7da141bcb8cb775df071184d1fe
mode 100644,000000..100644
--- /dev/null
@@@ -1,391 -1,0 +1,391 @@@
-         if method.ident.name == sym!(len);
 +use super::NEEDLESS_RANGE_LOOP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::has_iter_method;
 +use clippy_utils::visitors::LocalUsedVisitor;
 +use clippy_utils::{
 +    contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::middle::region;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{sym, Symbol};
 +use std::iter::{self, Iterator};
 +use std::mem;
 +
 +/// Checks for looping over a range and then indexing a sequence with it.
 +/// The iteratee must be a range literal.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    if let Some(higher::Range {
 +        start: Some(start),
 +        ref end,
 +        limits,
 +    }) = higher::range(arg)
 +    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
 +            let mut visitor = VarVisitor {
 +                cx,
 +                var: canonical_id,
 +                indexed_mut: FxHashSet::default(),
 +                indexed_indirectly: FxHashMap::default(),
 +                indexed_directly: FxHashMap::default(),
 +                referenced: FxHashSet::default(),
 +                nonindex: false,
 +                prefer_mutable: false,
 +            };
 +            walk_expr(&mut visitor, body);
 +
 +            // linting condition: we only indexed one variable, and indexed it directly
 +            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
 +                let (indexed, (indexed_extent, indexed_ty)) = visitor
 +                    .indexed_directly
 +                    .into_iter()
 +                    .next()
 +                    .expect("already checked that we have exactly 1 element");
 +
 +                // ensure that the indexed variable was declared before the loop, see #601
 +                if let Some(indexed_extent) = indexed_extent {
 +                    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +                    let parent_def_id = cx.tcx.hir().local_def_id(parent_id);
 +                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
 +                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
 +                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
 +                        return;
 +                    }
 +                }
 +
 +                // don't lint if the container that is indexed does not have .iter() method
 +                let has_iter = has_iter_method(cx, indexed_ty);
 +                if has_iter.is_none() {
 +                    return;
 +                }
 +
 +                // don't lint if the container that is indexed into is also used without
 +                // indexing
 +                if visitor.referenced.contains(&indexed) {
 +                    return;
 +                }
 +
 +                let starts_at_zero = is_integer_const(cx, start, 0);
 +
 +                let skip = if starts_at_zero {
 +                    String::new()
 +                } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
 +                    return;
 +                } else {
 +                    format!(".skip({})", snippet(cx, start.span, ".."))
 +                };
 +
 +                let mut end_is_start_plus_val = false;
 +
 +                let take = if let Some(end) = *end {
 +                    let mut take_expr = end;
 +
 +                    if let ExprKind::Binary(ref op, left, right) = end.kind {
 +                        if let BinOpKind::Add = op.node {
 +                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
 +                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
 +
 +                            if start_equal_left {
 +                                take_expr = right;
 +                            } else if start_equal_right {
 +                                take_expr = left;
 +                            }
 +
 +                            end_is_start_plus_val = start_equal_left | start_equal_right;
 +                        }
 +                    }
 +
 +                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
 +                        String::new()
 +                    } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
 +                        return;
 +                    } else {
 +                        match limits {
 +                            ast::RangeLimits::Closed => {
 +                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
 +                                format!(".take({})", take_expr + sugg::ONE)
 +                            },
 +                            ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")),
 +                        }
 +                    }
 +                } else {
 +                    String::new()
 +                };
 +
 +                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
 +                    ("mut ", "iter_mut")
 +                } else {
 +                    ("", "iter")
 +                };
 +
 +                let take_is_empty = take.is_empty();
 +                let mut method_1 = take;
 +                let mut method_2 = skip;
 +
 +                if end_is_start_plus_val {
 +                    mem::swap(&mut method_1, &mut method_2);
 +                }
 +
 +                if visitor.nonindex {
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        expr.span,
 +                        &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![
 +                                    (pat.span, format!("({}, <item>)", ident.name)),
 +                                    (
 +                                        arg.span,
 +                                        format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
 +                                    ),
 +                                ],
 +                            );
 +                        },
 +                    );
 +                } else {
 +                    let repl = if starts_at_zero && take_is_empty {
 +                        format!("&{}{}", ref_mut, indexed)
 +                    } else {
 +                        format!("{}.{}(){}{}", indexed, method, method_1, method_2)
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        expr.span,
 +                        &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind;
 +        if len_args.len() == 1;
++        if method.ident.name == sym::len;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
 +        if path.segments.len() == 1;
 +        if path.segments[0].ident.name == var;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn is_end_eq_array_len<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    indexed_ty: Ty<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = end.kind;
 +        if let ast::LitKind::Int(end_int, _) = lit.node;
 +        if let ty::Array(_, arr_len_const) = indexed_ty.kind();
 +        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
 +        then {
 +            return match limits {
 +                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
 +                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
 +            };
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct VarVisitor<'a, 'tcx> {
 +    /// context reference
 +    cx: &'a LateContext<'tcx>,
 +    /// var name to look for as index
 +    var: HirId,
 +    /// indexed variables that are used mutably
 +    indexed_mut: FxHashSet<Symbol>,
 +    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
 +    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
 +    /// subset of `indexed` of vars that are indexed directly: `v[i]`
 +    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
 +    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
 +    /// Any names that are used outside an index operation.
 +    /// Used to detect things like `&mut vec` used together with `vec[i]`
 +    referenced: FxHashSet<Symbol>,
 +    /// has the loop variable been used in expressions other than the index of
 +    /// an index op?
 +    nonindex: bool,
 +    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
 +    /// takes `&mut self`
 +    prefer_mutable: bool,
 +}
 +
 +impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
 +    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +        if_chain! {
 +            // the indexed container is referenced by a name
 +            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
 +            if let QPath::Resolved(None, seqvar) = *seqpath;
 +            if seqvar.segments.len() == 1;
 +            let index_used_directly = path_to_local_id(idx, self.var);
 +            let indexed_indirectly = {
 +                let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
 +                walk_expr(&mut used_visitor, idx);
 +                used_visitor.used
 +            };
 +            if indexed_indirectly || index_used_directly;
 +            then {
 +                if self.prefer_mutable {
 +                    self.indexed_mut.insert(seqvar.segments[0].ident.name);
 +                }
 +                let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
 +                match res {
 +                    Res::Local(hir_id) => {
 +                        let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
 +                        let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
 +                        let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
 +                        if indexed_indirectly {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
 +                        }
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    Res::Def(DefKind::Static | DefKind::Const, ..) => {
 +                        if indexed_indirectly {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
 +                        }
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    _ => (),
 +                }
 +            }
 +        }
 +        true
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // a range index op
 +            if let ExprKind::MethodCall(meth, _, args, _) = expr.kind;
 +            if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
 +                || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
 +            if !self.check(&args[1], &args[0], expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // an index op
 +            if let ExprKind::Index(seqexpr, idx) = expr.kind;
 +            if !self.check(idx, seqexpr, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // directly using a variable
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
 +            if let Res::Local(local_id) = path.res;
 +            then {
 +                if local_id == self.var {
 +                    self.nonindex = true;
 +                } else {
 +                    // not the correct variable, but still a variable
 +                    self.referenced.insert(path.segments[0].ident.name);
 +                }
 +            }
 +        }
 +
 +        let old = self.prefer_mutable;
 +        match expr.kind {
 +            ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
 +                self.prefer_mutable = true;
 +                self.visit_expr(lhs);
 +                self.prefer_mutable = false;
 +                self.visit_expr(rhs);
 +            },
 +            ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
 +                if mutbl == Mutability::Mut {
 +                    self.prefer_mutable = true;
 +                }
 +                self.visit_expr(expr);
 +            },
 +            ExprKind::Call(f, args) => {
 +                self.visit_expr(f);
 +                for expr in args {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::MethodCall(_, _, args, _) => {
 +                let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +                for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) {
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::Closure(_, _, body_id, ..) => {
 +                let body = self.cx.tcx.hir().body(body_id);
 +                self.visit_expr(&body.value);
 +            },
 +            _ => walk_expr(self, expr),
 +        }
 +        self.prefer_mutable = old;
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 63560047578a16aa0c39f8996745dbd02ab6c246,0000000000000000000000000000000000000000..d57588716a5bf16c8f4b28d0b75ceb251718837e
mode 100644,000000..100644
--- /dev/null
@@@ -1,347 -1,0 +1,350 @@@
- use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
 +use super::WHILE_LET_ON_ITERATOR;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
-     if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
-         // The iterator expression will be used on the next iteration unless it is declared within the outer
-         // loop.
++use clippy_utils::{
++    get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
++};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
 +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_span::{symbol::sym, Span, Symbol};
 +
 +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
 +        if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind;
 +        // check for `Some(..)` pattern
 +        if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind;
 +        if let Res::Def(_, pat_did) = pat_path.res;
 +        if match_def_path(cx, pat_did, &paths::OPTION_SOME);
 +        // check for call to `Iterator::next`
 +        if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind;
 +        if method_name.ident.name == sym::next;
 +        if is_trait_method(cx, scrutinee_expr, sym::Iterator);
 +        if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr);
 +        // get the loop containing the match expression
 +        if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
 +        if !uses_iter(cx, &iter_expr, arm.body);
 +        then {
 +            (scrutinee_expr, iter_expr, some_pat, loop_expr)
 +        } else {
 +            return;
 +        }
 +    };
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +    let loop_var = if let Some(some_pat) = some_pat.first() {
 +        if is_refutable(cx, some_pat) {
 +            // Refutable patterns don't work with for loops.
 +            return;
 +        }
 +        snippet_with_applicability(cx, some_pat.span, "..", &mut applicability)
 +    } else {
 +        "_".into()
 +    };
 +
 +    // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
 +    // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
 +    // afterwards a mutable borrow of a field isn't necessary.
 +    let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
 +        "&mut "
 +    } else {
 +        ""
 +    };
 +
 +    let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
 +    span_lint_and_sugg(
 +        cx,
 +        WHILE_LET_ON_ITERATOR,
 +        expr.span.with_hi(scrutinee_expr.span.hi()),
 +        "this loop could be written as a `for` loop",
 +        "try",
 +        format!("for {} in {}{}", loop_var, ref_mut, iterator),
 +        applicability,
 +    );
 +}
 +
 +#[derive(Debug)]
 +struct IterExpr {
 +    /// The span of the whole expression, not just the path and fields stored here.
 +    span: Span,
 +    /// The fields used, in order of child to parent.
 +    fields: Vec<Symbol>,
 +    /// The path being used.
 +    path: Res,
 +}
 +/// Parses any expression to find out which field of which variable is used. Will return `None` if
 +/// the expression might have side effects.
 +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
 +    let span = e.span;
 +    let mut fields = Vec::new();
 +    loop {
 +        match e.kind {
 +            ExprKind::Path(ref path) => {
 +                break Some(IterExpr {
 +                    span,
 +                    fields,
 +                    path: cx.qpath_res(path, e.hir_id),
 +                });
 +            },
 +            ExprKind::Field(base, name) => {
 +                fields.push(name.name);
 +                e = base;
 +            },
 +            // Dereferencing a pointer has no side effects and doesn't affect which field is being used.
 +            ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base,
 +
 +            // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
 +            // already been seen.
 +            ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
 +                fields.clear();
 +                e = base;
 +            },
 +            ExprKind::Unary(UnOp::Deref, base) => {
 +                fields.clear();
 +                e = base;
 +            },
 +
 +            // No effect and doesn't affect which field is being used.
 +            ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base,
 +            _ => break None,
 +        }
 +    }
 +}
 +
 +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool {
 +    loop {
 +        match (&e.kind, fields) {
 +            (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => {
 +                e = base;
 +                fields = tail_fields;
 +            },
 +            (ExprKind::Path(path), []) => {
 +                break cx.qpath_res(path, e.hir_id) == path_res;
 +            },
 +            (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base,
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is the same field as, is a child of, or is the parent of the
 +/// given field. Used to check if the expression can be used while the given field is borrowed
 +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
 +/// `x.z`, and `y` will return false.
 +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool {
 +    match expr.kind {
 +        ExprKind::Field(base, name) => {
 +            if let Some((head_field, tail_fields)) = fields.split_first() {
 +                if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) {
 +                    return true;
 +                }
 +                // Check if the expression is a parent field
 +                let mut fields_iter = tail_fields.iter();
 +                while let Some(field) = fields_iter.next() {
 +                    if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) {
 +                        return true;
 +                    }
 +                }
 +            }
 +
 +            // Check if the expression is a child field.
 +            let mut e = base;
 +            loop {
 +                match e.kind {
 +                    ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true,
 +                    ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +                    ExprKind::Path(ref path) if fields.is_empty() => {
 +                        break cx.qpath_res(path, e.hir_id) == path_res;
 +                    },
 +                    _ => break false,
 +                }
 +            }
 +        },
 +        // If the path matches, this is either an exact match, or the expression is a parent of the field.
 +        ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res,
 +        ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => {
 +            is_expr_same_child_or_parent_field(cx, base, fields, path_res)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Strips off all field and path expressions. This will return true if a field or path has been
 +/// skipped. Used to skip them after failing to check for equality.
 +fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
 +    let mut e = expr;
 +    let e = loop {
 +        match e.kind {
 +            ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +            ExprKind::Path(_) => return (None, true),
 +            _ => break e,
 +        }
 +    };
 +    (Some(e), e.hir_id != expr.hir_id)
 +}
 +
 +/// Checks if the given expression uses the iterator.
 +fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        uses_iter: bool,
 +    }
 +    impl Visitor<'tcx> for V<'_, '_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.uses_iter {
 +                // return
 +            } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                self.uses_iter = true;
 +            } else if let (e, true) = skip_fields_and_path(e) {
 +                if let Some(e) = e {
 +                    self.visit_expr(e);
 +                }
 +            } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
 +                if is_res_used(self.cx, self.iter_expr.path, id) {
 +                    self.uses_iter = true;
 +                }
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        iter_expr,
 +        uses_iter: false,
 +    };
 +    v.visit_expr(container);
 +    v.uses_iter
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
 +    struct AfterLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        used_iter: bool,
 +    }
 +    impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_iter {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_iter = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
 +                    self.used_iter = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if self.loop_id == e.hir_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    struct NestedLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        local_id: HirId,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        found_local: bool,
 +        used_after: bool,
 +    }
 +    impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        fn visit_local(&mut self, l: &'tcx Local<'_>) {
 +            if !self.after_loop {
 +                l.pat.each_binding_or_first(&mut |_, id, _, _| {
 +                    if id == self.local_id {
 +                        self.found_local = true;
 +                    }
 +                });
 +            }
 +            if let Some(e) = l.init {
 +                self.visit_expr(e);
 +            }
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_after {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_after = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
 +                    self.used_after = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if e.hir_id == self.loop_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
++    if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
++        // The iterator expression will be used on the next iteration (for loops), or on the next call (for
++        // closures) unless it is declared within the enclosing expression. TODO: Check for closures
++        // used where an `FnOnce` type is expected.
 +        let local_id = match iter_expr.path {
 +            Res::Local(id) => id,
 +            _ => return true,
 +        };
 +        let mut v = NestedLoopVisitor {
 +            cx,
 +            iter_expr,
 +            local_id,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            found_local: false,
 +            used_after: false,
 +        };
 +        v.visit_expr(e);
 +        v.used_after || !v.found_local
 +    } else {
 +        let mut v = AfterLoopVisitor {
 +            cx,
 +            iter_expr,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            used_iter: false,
 +        };
 +        v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
 +        v.used_iter
 +    }
 +}
index 0b873534f2c8d4be30d6fc29ed6191edebd49f69,0000000000000000000000000000000000000000..97e4a983f32ea3ca4542d17cf63223d2aeca2303
mode 100644,000000..100644
--- /dev/null
@@@ -1,281 -1,0 +1,279 @@@
-     can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
 +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 +use clippy_utils::{
- use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
++    can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
++    peel_hir_expr_refs,
 +};
 +use rustc_ast::util::parser::PREC_POSTFIX;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
- use rustc_span::{
-     symbol::{sym, Ident},
-     SyntaxContext,
- };
++use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
-             let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
-                 match can_pass_as_func(cx, some_binding, some_expr) {
++use rustc_span::{sym, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for usages of `match` which could be implemented using `map`
 +    ///
 +    /// **Why is this bad?** Using the `map` method is clearer and more concise.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// match Some(0) {
 +    ///     Some(x) => Some(x + 1),
 +    ///     None => None,
 +    /// };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// Some(0).map(|x| x + 1);
 +    /// ```
 +    pub MANUAL_MAP,
 +    style,
 +    "reimplementation of `map`"
 +}
 +
 +declare_lint_pass!(ManualMap => [MANUAL_MAP]);
 +
 +impl LateLintPass<'_> for ManualMap {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Match(
 +            scrutinee,
 +            [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
 +            match_kind,
 +        ) = expr.kind
 +        {
 +            if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
 +                return;
 +            }
 +
 +            let (scrutinee_ty, ty_ref_count, ty_mutability) =
 +                peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
 +            if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
 +                && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
 +            {
 +                return;
 +            }
 +
 +            let expr_ctxt = expr.span.ctxt();
 +            let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
 +                try_parse_pattern(cx, arm1.pat, expr_ctxt),
 +                try_parse_pattern(cx, arm2.pat, expr_ctxt),
 +            ) {
 +                (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count }))
 +                    if is_none_expr(cx, arm1.body) =>
 +                {
 +                    (arm2.body, pattern, ref_count, true)
 +                },
 +                (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count }))
 +                    if is_none_expr(cx, arm1.body) =>
 +                {
 +                    (arm2.body, pattern, ref_count, false)
 +                },
 +                (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild))
 +                    if is_none_expr(cx, arm2.body) =>
 +                {
 +                    (arm1.body, pattern, ref_count, true)
 +                },
 +                (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None))
 +                    if is_none_expr(cx, arm2.body) =>
 +                {
 +                    (arm1.body, pattern, ref_count, false)
 +                },
 +                _ => return,
 +            };
 +
 +            // Top level or patterns aren't allowed in closures.
 +            if matches!(some_pat.kind, PatKind::Or(_)) {
 +                return;
 +            }
 +
 +            let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
 +                Some(expr) => expr,
 +                None => return,
 +            };
 +
 +            // These two lints will go back and forth with each other.
 +            if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
 +                && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
 +            {
 +                return;
 +            }
 +
 +            // `map` won't perform any adjustments.
 +            if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
 +                return;
 +            }
 +
 +            if !can_move_expr_to_closure(cx, some_expr) {
 +                return;
 +            }
 +
 +            // Determine which binding mode to use.
 +            let explicit_ref = some_pat.contains_explicit_ref_binding();
 +            let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
 +
 +            let as_ref_str = match binding_ref {
 +                Some(Mutability::Mut) => ".as_mut()",
 +                Some(Mutability::Not) => ".as_ref()",
 +                None => "",
 +            };
 +
 +            let mut app = Applicability::MachineApplicable;
 +
 +            // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
 +            // it's being passed by value.
 +            let scrutinee = peel_hir_expr_refs(scrutinee).0;
 +            let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
 +            let scrutinee_str =
 +                if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
 +                    format!("({})", scrutinee_str)
 +                } else {
 +                    scrutinee_str.into()
 +                };
 +
-                         if match_var(some_expr, some_binding.name)
++            let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
++                match can_pass_as_func(cx, id, some_expr) {
 +                    Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
 +                        snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
 +                    },
 +                    _ => {
- fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
++                        if path_to_local_id(some_expr, id)
 +                            && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
 +                            && binding_ref.is_some()
 +                        {
 +                            return;
 +                        }
 +
 +                        // `ref` and `ref mut` annotations were handled earlier.
 +                        let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
 +                            "mut "
 +                        } else {
 +                            ""
 +                        };
 +                        format!(
 +                            "|{}{}| {}",
 +                            annotation,
 +                            some_binding,
 +                            snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
 +                        )
 +                    },
 +                }
 +            } else if !is_wild_none && explicit_ref.is_none() {
 +                // TODO: handle explicit reference annotations.
 +                format!(
 +                    "|{}| {}",
 +                    snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
 +                    snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
 +                )
 +            } else {
 +                // Refutable bindings and mixed reference annotations can't be handled by `map`.
 +                return;
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_MAP,
 +                expr.span,
 +                "manual implementation of `Option::map`",
 +                "try this",
 +                if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) {
 +                    format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
 +                } else {
 +                    format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
 +                },
 +                app,
 +            );
 +        }
 +    }
 +}
 +
 +// Checks whether the expression could be passed as a function, or whether a closure is needed.
 +// Returns the function to be passed to `map` if it exists.
-             if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
++fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    match expr.kind {
 +        ExprKind::Call(func, [arg])
++            if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
 +        {
 +            Some(func)
 +        },
 +        _ => None,
 +    }
 +}
 +
 +enum OptionPat<'a> {
 +    Wild,
 +    None,
 +    Some {
 +        // The pattern contained in the `Some` tuple.
 +        pattern: &'a Pat<'a>,
 +        // The number of references before the `Some` tuple.
 +        // e.g. `&&Some(_)` has a ref count of 2.
 +        ref_count: usize,
 +    },
 +}
 +
 +// Try to parse into a recognized `Option` pattern.
 +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
 +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
 +    fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
 +        match pat.kind {
 +            PatKind::Wild => Some(OptionPat::Wild),
 +            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
 +            PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
 +            PatKind::TupleStruct(ref qpath, [pattern], _)
 +                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
 +            {
 +                Some(OptionPat::Some { pattern, ref_count })
 +            },
 +            _ => None,
 +        }
 +    }
 +    f(cx, pat, 0, ctxt)
 +}
 +
 +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
 +fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> {
 +    // TODO: Allow more complex expressions.
 +    match expr.kind {
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(ref qpath),
 +                ..
 +            },
 +            [arg],
 +        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) => get_some_expr(cx, expr, ctxt),
 +        _ => None,
 +    }
 +}
 +
 +// Checks for the `None` value.
 +fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    match expr.kind {
 +        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) => is_none_expr(cx, expr),
 +        _ => false,
 +    }
 +}
index cd3e3b97928af8cffc423b9961c21e1ade38d351,0000000000000000000000000000000000000000..f1e3492c4ecc5fdc3f698545376b9a3cb912fdfb
mode 100644,000000..100644
--- /dev/null
@@@ -1,2291 -1,0 +1,2293 @@@
- fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
 +use clippy_utils::consts::{constant, miri_to_const, Constant};
 +use clippy_utils::diagnostics::{
 +    multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
 +};
 +use clippy_utils::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 => (),
 +        }
 +    }
 +}
 +
-     clippy_utils::attrs::is_doc_hidden(attrs)
++fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
 +    let attrs = cx.tcx.get_attrs(variant_def.def_id);
-     let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
++    clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
 +    let ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    let adt_def = match ty.kind() {
 +        ty::Adt(adt_def, _)
 +            if adt_def.is_enum()
 +                && !(is_type_diagnostic_item(cx, ty, sym::option_type)
 +                    || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
 +        {
 +            adt_def
 +        },
 +        _ => return,
 +    };
 +
 +    // First pass - check for violation, but don't do much book-keeping because this is hopefully
 +    // the uncommon case, and the book-keeping is slightly expensive.
 +    let mut wildcard_span = None;
 +    let mut wildcard_ident = None;
 +    let mut has_non_wild = false;
 +    for arm in arms {
 +        match peel_hir_pat_refs(arm.pat).0.kind {
 +            PatKind::Wild => wildcard_span = Some(arm.pat.span),
 +            PatKind::Binding(_, _, ident, None) => {
 +                wildcard_span = Some(arm.pat.span);
 +                wildcard_ident = Some(ident);
 +            },
 +            _ => has_non_wild = true,
 +        }
 +    }
 +    let wildcard_span = match wildcard_span {
 +        Some(x) if has_non_wild => x,
 +        _ => return,
 +    };
 +
 +    // Accumulate the variants which should be put in place of the wildcard because they're not
 +    // already covered.
-         [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg(
++    let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
++    let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
 +
 +    let mut path_prefix = CommonPrefixSearcher::None;
 +    for arm in arms {
 +        // Guards mean that this case probably isn't exhaustively covered. Technically
 +        // this is incorrect, as we should really check whether each variant is exhaustively
 +        // covered by the set of guards that cover it, but that's really hard to do.
 +        recurse_or_patterns(arm.pat, |pat| {
 +            let path = match &peel_hir_pat_refs(pat).0.kind {
 +                PatKind::Path(path) => {
 +                    #[allow(clippy::match_same_arms)]
 +                    let id = match cx.qpath_res(path, pat.hir_id) {
 +                        Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
 +                        Res::Def(_, id) => id,
 +                        _ => return,
 +                    };
 +                    if arm.guard.is_none() {
 +                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                    }
 +                    path
 +                },
 +                PatKind::TupleStruct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
 +                            missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                        }
 +                    }
 +                    path
 +                },
 +                PatKind::Struct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
 +                            missing_variants.retain(|e| e.def_id != id);
 +                        }
 +                    }
 +                    path
 +                },
 +                _ => return,
 +            };
 +            match path {
 +                QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
 +                QPath::TypeRelative(
 +                    hir::Ty {
 +                        kind: TyKind::Path(QPath::Resolved(_, path)),
 +                        ..
 +                    },
 +                    _,
 +                ) => path_prefix.with_prefix(path.segments),
 +                _ => (),
 +            }
 +        });
 +    }
 +
 +    let format_suggestion = |variant: &VariantDef| {
 +        format!(
 +            "{}{}{}{}",
 +            if let Some(ident) = wildcard_ident {
 +                format!("{} @ ", ident.name)
 +            } else {
 +                String::new()
 +            },
 +            if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
 +                let mut s = String::new();
 +                for seg in path_prefix {
 +                    s.push_str(&seg.ident.as_str());
 +                    s.push_str("::");
 +                }
 +                s
 +            } else {
 +                let mut s = cx.tcx.def_path_str(adt_def.did);
 +                s.push_str("::");
 +                s
 +            },
 +            variant.ident.name,
 +            match variant.ctor_kind {
 +                CtorKind::Fn if variant.fields.len() == 1 => "(_)",
 +                CtorKind::Fn => "(..)",
 +                CtorKind::Const => "",
 +                CtorKind::Fictive => "{ .. }",
 +            }
 +        )
 +    };
 +
 +    match missing_variants.as_slice() {
 +        [] => (),
-             let message = if adt_def.is_variant_list_non_exhaustive() {
++        [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
 +            cx,
 +            MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +            wildcard_span,
 +            "wildcard matches only a single variant and will also match any future added variants",
 +            "try this",
 +            format_suggestion(x),
 +            Applicability::MaybeIncorrect,
 +        ),
 +        variants => {
 +            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
-                         diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
++            let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
 +                suggestions.push("_".into());
 +                "wildcard matches known variants and will also match future added variants"
 +            } else {
 +                "wildcard match will also match any future added variants"
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                WILDCARD_ENUM_MATCH_ARM,
 +                wildcard_span,
 +                message,
 +                "try this",
 +                suggestions.join(" | "),
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    };
 +}
 +
 +// If the block contains only a `panic!` macro (as expression or statement)
 +fn is_panic_block(block: &Block<'_>) -> bool {
 +    match (&block.expr, block.stmts.len(), block.stmts.first()) {
 +        (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
 +        (&None, 1, Some(stmt)) => {
 +            is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn check_match_ref_pats(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 => {
 +            if ex.can_have_side_effects() {
 +                let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
 +                let sugg = format!(
 +                    "{};\n{}{}",
 +                    snippet_with_applicability(cx, ex.span, "..", &mut applicability),
 +                    indent,
 +                    snippet_body
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its scrutinee and body",
 +                    "consider using the scrutinee and body instead",
 +                    sugg,
 +                    applicability,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its body itself",
 +                    "consider using the match body instead",
 +                    snippet_body,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Returns true if the `ex` match expression is in a local (`let`) statement
 +fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
 +    let map = &cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
 +        if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
 +        then {
 +            return Some(parent_let_expr);
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets all arms that are unbounded `PatRange`s.
 +fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
 +    arms.iter()
 +        .filter_map(|arm| {
 +            if let Arm { pat, guard: None, .. } = *arm {
 +                if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
 +                    let lhs = match lhs {
 +                        Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
 +                        None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match rhs {
 +                        Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
 +                        None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match range_end {
 +                        RangeEnd::Included => Bound::Included(rhs),
 +                        RangeEnd::Excluded => Bound::Excluded(rhs),
 +                    };
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (lhs, rhs),
 +                    });
 +                }
 +
 +                if let PatKind::Lit(value) = pat.kind {
 +                    let value = constant(cx, cx.typeck_results(), value)?.0;
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (value.clone(), Bound::Included(value)),
 +                    });
 +                }
 +            }
 +            None
 +        })
 +        .collect()
 +}
 +
 +#[derive(Debug, Eq, PartialEq)]
 +pub struct SpannedRange<T> {
 +    pub span: Span,
 +    pub node: (T, Bound<T>),
 +}
 +
 +type TypedRanges = Vec<SpannedRange<u128>>;
 +
 +/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
 +/// and other types than
 +/// `Uint` and `Int` probably don't make sense.
 +fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
 +    ranges
 +        .iter()
 +        .filter_map(|range| match range.node {
 +            (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Included(end)),
 +            }),
 +            (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Excluded(end)),
 +            }),
 +            (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Unbounded),
 +            }),
 +            _ => None,
 +        })
 +        .collect()
 +}
 +
 +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::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
 +        if is_lang_ctor(cx, qpath, OptionSome);
 +        if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
 +        if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
 +        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
 +        if let ExprKind::Path(ref some_path) = e.kind;
 +        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
 +        if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
 +        if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
 +        then {
 +            return Some(rb)
 +        }
 +    }
 +    None
 +}
 +
 +fn has_only_ref_pats(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() {
 +                ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +                ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
 +                ty::Adt(adt, subs) => adt
 +                    .all_fields()
 +                    .map(|f| f.ty(cx.tcx, subs))
 +                    .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +                _ => true,
 +            }
 +        }
 +        // Check for std types which implement drop, but only for memory allocation.
 +        else if is_type_diagnostic_item(cx, ty, sym::vec_type)
 +            || is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +            || is_type_diagnostic_item(cx, ty, sym::Rc)
 +            || is_type_diagnostic_item(cx, ty, sym::Arc)
 +            || is_type_diagnostic_item(cx, ty, sym::cstring_type)
 +            || 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,))
++                            .help("...or consider changing the match arm bodies");
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
 +    let mut result = false;
 +    pat.walk_short(|p| {
 +        result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
 +        !result
 +    });
 +    result
 +}
 +
 +/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
 +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
 +    let mut result = true;
 +    pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
 +    result && ids.is_empty()
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e39a5a1efd1e18f2a76bc64ab5cb76ef84e806a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, LangItem};
++use rustc_lint::LateContext;
++use rustc_span::symbol::sym;
++
++use super::APPEND_INSTEAD_OF_EXTEND;
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
++    let ty = cx.typeck_results().expr_ty(recv).peel_refs();
++    if_chain! {
++        if is_type_diagnostic_item(cx, ty, sym::vec_type);
++        //check source object
++        if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind;
++        if src_method.ident.as_str() == "drain";
++        if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs();
++        if is_type_diagnostic_item(cx, src_ty, sym::vec_type);
++        //check drain range
++        if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs();
++        if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull);
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                APPEND_INSTEAD_OF_EXTEND,
++                expr.span,
++                "use of `extend` instead of `append` for adding the full range of a second vector",
++                "try this",
++                format!(
++                    "{}.append(&mut {})",
++                    snippet_with_applicability(cx, recv.span, "..", &mut applicability),
++                    snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability)
++                ),
++                applicability,
++            );
++        }
++    }
++}
index 403fe8d35468468db91a9c46a13dd2755b5c9fd7,0000000000000000000000000000000000000000..d1b5e945dfdaa83a85b5ce13d776b0d438b88579
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,22 @@@
- use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
- use if_chain::if_chain;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     if is_trait_method(cx, expr, sym::Iterator) {
-         let apply_lint = |message: &str| {
-             span_lint_and_sugg(
-                 cx,
-                 FILTER_MAP_IDENTITY,
-                 filter_map_span.with_hi(expr.span.hi()),
-                 message,
-                 "try",
-                 "flatten()".to_string(),
-                 Applicability::MachineApplicable,
-             );
-         };
-         if_chain! {
-             if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
-             let body = cx.tcx.hir().body(body_id);
-             if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
-             if path_to_local_id(&body.value, binding_id);
-             then {
-                 apply_lint("called `filter_map(|x| x)` on an `Iterator`");
-             }
-         }
-         if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
-             apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
-         }
++use clippy_utils::{is_expr_identity_function, is_trait_method};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::{source_map::Span, sym};
 +
 +use super::FILTER_MAP_IDENTITY;
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
++    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
++        span_lint_and_sugg(
++            cx,
++            FILTER_MAP_IDENTITY,
++            filter_map_span.with_hi(expr.span.hi()),
++            "use of `filter_map` with an identity function",
++            "try",
++            "flatten()".to_string(),
++            Applicability::MachineApplicable,
++        );
 +    }
 +}
index 25f8434cb94428f458f36fd442fcf8260e542828,0000000000000000000000000000000000000000..6f911d79d0bc50880628e5de6f2711802601d58d
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,28 @@@
- use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
- use if_chain::if_chain;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     if is_trait_method(cx, expr, sym::Iterator) {
-         let apply_lint = |message: &str| {
-             span_lint_and_sugg(
-                 cx,
-                 FLAT_MAP_IDENTITY,
-                 flat_map_span.with_hi(expr.span.hi()),
-                 message,
-                 "try",
-                 "flatten()".to_string(),
-                 Applicability::MachineApplicable,
-             );
-         };
-         if_chain! {
-             if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
-             let body = cx.tcx.hir().body(body_id);
-             if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
-             if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
-             if path.segments.len() == 1;
-             if path.segments[0].ident.name == binding_ident.name;
-             then {
-                 apply_lint("called `flat_map(|x| x)` on an `Iterator`");
-             }
-         }
-         if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
-             apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
-         }
++use clippy_utils::{is_expr_identity_function, is_trait_method};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::{source_map::Span, sym};
 +
 +use super::FLAT_MAP_IDENTITY;
 +
 +/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    flat_map_arg: &'tcx hir::Expr<'_>,
 +    flat_map_span: Span,
 +) {
++    if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
++        span_lint_and_sugg(
++            cx,
++            FLAT_MAP_IDENTITY,
++            flat_map_span.with_hi(expr.span.hi()),
++            "use of `flat_map` with an identity function",
++            "try",
++            "flatten()".to_string(),
++            Applicability::MachineApplicable,
++        );
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..538a12566e3031880a9400f3a1f294cf491a0556
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::{is_expr_identity_function, is_trait_method};
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_span::{source_map::Span, sym};
++
++use super::MAP_IDENTITY;
++
++pub(super) fn check(
++    cx: &LateContext<'_>,
++    expr: &hir::Expr<'_>,
++    caller: &hir::Expr<'_>,
++    map_arg: &hir::Expr<'_>,
++    _map_span: Span,
++) {
++    let caller_ty = cx.typeck_results().expr_ty(caller);
++
++    if_chain! {
++        if is_trait_method(cx, expr, sym::Iterator)
++            || is_type_diagnostic_item(cx, caller_ty, sym::result_type)
++            || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
++        if is_expr_identity_function(cx, map_arg);
++        if let Some(sugg_span) = expr.span.trim_start(caller.span);
++        then {
++            span_lint_and_sugg(
++                cx,
++                MAP_IDENTITY,
++                sugg_span,
++                "unnecessary map of the identity function",
++                "remove the call to `map`",
++                String::new(),
++                Applicability::MachineApplicable,
++            )
++        }
++    }
++}
index c8ae972f18ca62a5d4ba0f3e19ca803fa4169f7a,0000000000000000000000000000000000000000..283fcf281df181359cd3948c6e6389919b77cf00
mode 100644,000000..100644
--- /dev/null
@@@ -1,2335 -1,0 +1,2390 @@@
-     complexity,
++mod append_instead_of_extend;
 +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 manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
++mod map_identity;
 +mod map_unwrap_or;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_lazy_eval;
 +mod unwrap_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, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::SymbolStr;
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// **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:** 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 occurrences where one vector gets extended instead of append
++    ///
++    /// **Why is this bad?** Using `append` instead of `extend` is more concise and faster
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let mut a = vec![1, 2, 3];
++    /// let mut b = vec![4, 5, 6];
++    ///
++    /// // Bad
++    /// a.extend(b.drain(..));
++    ///
++    /// // Good
++    /// a.append(&mut b);
++    /// ```
++    pub APPEND_INSTEAD_OF_EXTEND,
++    perf,
++    "using vec.append(&mut vec) to move the full range of a vecor to another"
++}
++
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// **Why is this bad?** `.push_str(s)` is clearer
 +    ///
 +    /// **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,
-     style,
++    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// **Why is this bad?** For most types, this is undefined behavior.
 +    ///
 +    /// **Known problems:** For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// **Why is this bad?** This is a no-op, and likely unintended
 +    ///
 +    /// **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,
-     MANUAL_STR_REPEAT
++    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// **Why is this bad?** It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// **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 instances of `map(f)` where `f` is the identity function.
++    ///
++    /// **Why is this bad?** It can be written more concisely without the call to `map`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x = [1, 2, 3];
++    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let x = [1, 2, 3];
++    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
++    /// ```
++    pub MAP_IDENTITY,
++    complexity,
++    "using iterator.map(|x| x)"
++}
++
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// **Why is this bad?** `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// **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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// **Why is this bad?** These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // Bad
 +    /// let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // use x
 +    /// }
 +    ///
 +    /// // Good
 +    /// let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // use x
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for manual implementations of `str::repeat`
 +    ///
 +    /// **Why is this bad?** These are both harder to read, as well as less performant.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // Bad
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    ///
 +    /// // Good
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
++    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_SPLITN,
-             ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => {
++    MANUAL_STR_REPEAT,
++    APPEND_INSTEAD_OF_EXTEND
 +]);
 +
 +/// 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
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.hir_id()))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        &name,
 +                        self_ty,
 +                        first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        // 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 sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
 +                wrong_self_convention::check(
 +                    cx,
 +                    &item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
 +            if !contains_ty(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) {
-             ("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg),
++            ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                zst_offset::check(cx, expr, recv);
 +            },
 +            ("and_then", [arg]) => {
 +                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                if !biom_option_linted && !biom_result_linted {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                }
 +            },
 +            ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +            ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +            ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
 +            ("collect", []) => match method_call!(recv) {
 +                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
 +                Some(("map", [m_recv, m_arg], _)) => {
 +                    map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
 +                },
 +                Some(("take", [take_self_arg, take_arg], _)) => {
 +                    if meets_msrv(msrv, &msrvs::STR_REPEAT) {
 +                        manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                    }
 +                },
 +                _ => {},
 +            },
 +            ("count", []) => match method_call!(recv) {
 +                Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                    iter_count::check(cx, expr, recv2, name);
 +                },
 +                Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                _ => {},
 +            },
 +            ("expect", [_]) => match method_call!(recv) {
 +                Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                _ => expect_used::check(cx, expr, recv),
 +            },
++            ("extend", [arg]) => {
++                string_extend_chars::check(cx, expr, recv, arg);
++                append_instead_of_extend::check(cx, expr, recv, arg);
++            },
 +            ("filter_map", [arg]) => {
 +                unnecessary_filter_map::check(cx, expr, arg);
 +                filter_map_identity::check(cx, expr, arg, span);
 +            },
 +            ("flat_map", [arg]) => {
 +                flat_map_identity::check(cx, expr, arg, span);
 +                flat_map_option::check(cx, expr, arg, span);
 +            },
 +            ("flatten", []) => {
 +                if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
 +                    map_flatten::check(cx, expr, recv, map_arg);
 +                }
 +            },
 +            ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +            ("for_each", [_]) => {
 +                if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
 +                    inspect_for_each::check(cx, expr, span2);
 +                }
 +            },
 +            ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +            ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +            ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +            ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +            ("map", [m_arg]) => {
 +                if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
 +                        ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
 +                        ("filter", [f_arg]) => {
 +                            filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                        },
 +                        ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
 +                        _ => {},
 +                    }
 +                }
++                map_identity::check(cx, expr, recv, m_arg, span);
 +            },
 +            ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
 +            ("next", []) => {
 +                if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
 +                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
 +                        ("iter", []) => iter_next_slice::check(cx, expr, recv),
 +                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
 +                        ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                        _ => {},
 +                    }
 +                }
 +            },
 +            ("nth", [n_arg]) => match method_call!(recv) {
 +                Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +            },
 +            ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +            ("or_else", [arg]) => {
 +                if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                }
 +            },
 +            ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => {
 +                suspicious_splitn::check(cx, name, expr, recv, count_arg);
 +            },
 +            ("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 800172f4cf3dcdd3ee8a51363444b396c60b4040,0000000000000000000000000000000000000000..073c5570a88776e8c396bcc69284a3f5735cd2e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,183 -1,0 +1,183 @@@
-             if path.ident.as_str() == "len" {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::is_lazyness_candidate;
 +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type};
 +use clippy_utils::{contains_return, get_trait_def_id, last_path_segment, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::{BlockCheckMode, UnsafeSource};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, sym};
 +use std::borrow::Cow;
 +
 +use super::OR_FUN_CALL;
 +
 +/// Checks for the `OR_FUN_CALL` lint.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    method_span: Span,
 +    name: &str,
 +    args: &'tcx [hir::Expr<'_>],
 +) {
 +    /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &hir::Expr<'_>,
 +        or_has_args: bool,
 +        span: Span,
 +    ) -> bool {
 +        if_chain! {
 +            if !or_has_args;
 +            if name == "unwrap_or";
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            let path = last_path_segment(qpath).ident.name;
 +            if matches!(path, kw::Default | sym::new);
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
 +            if implements_trait(cx, arg_ty, default_trait_id, &[]);
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span,
 +                    &format!("use of `{}` followed by a call to `{}`", name, path),
 +                    "try this",
 +                    format!(
 +                        "{}.unwrap_or_default()",
 +                        snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +
 +    /// Checks for `*or(foo())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_general_case<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        name: &str,
 +        method_span: Span,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &'tcx hir::Expr<'_>,
 +        span: Span,
 +        // None if lambda is required
 +        fun_span: Option<Span>,
 +    ) {
 +        // (path, fn_has_argument, methods, suffix)
 +        static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
 +            (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
 +            (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
 +            (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
 +            (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
 +        ];
 +
 +        if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
++            if path.ident.name == sym::len {
 +                let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
 +
 +                match ty.kind() {
 +                    ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
 +                    _ => (),
 +                }
 +
 +                if is_type_diagnostic_item(cx, ty, sym::vec_type) {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        if_chain! {
 +            if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 +
 +            if is_lazyness_candidate(cx, arg);
 +            if !contains_return(arg);
 +
 +            let self_ty = cx.typeck_results().expr_ty(self_expr);
 +
 +            if let Some(&(_, fn_has_arguments, poss, suffix)) =
 +                KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
 +
 +            if poss.contains(&name);
 +
 +            then {
 +                let macro_expanded_snipped;
 +                let sugg: Cow<'_, str> = {
 +                    let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
 +                        (false, Some(fun_span)) => (fun_span, false),
 +                        _ => (arg.span, true),
 +                    };
 +                    let snippet = {
 +                        let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
 +                        if not_macro_argument_snippet == "vec![]" {
 +                            macro_expanded_snipped = snippet(cx, snippet_span, "..");
 +                            match macro_expanded_snipped.strip_prefix("$crate::vec::") {
 +                                Some(stripped) => Cow::from(stripped),
 +                                None => macro_expanded_snipped
 +                            }
 +                        }
 +                        else {
 +                            not_macro_argument_snippet
 +                        }
 +                    };
 +
 +                    if use_lambda {
 +                        let l_arg = if fn_has_arguments { "_" } else { "" };
 +                        format!("|{}| {}", l_arg, snippet).into()
 +                    } else {
 +                        snippet
 +                    }
 +                };
 +                let span_replace_word = method_span.with_hi(span.hi());
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span_replace_word,
 +                    &format!("use of `{}` followed by a function call", name),
 +                    "try this",
 +                    format!("{}_{}({})", name, suffix, sugg),
 +                    Applicability::HasPlaceholders,
 +                );
 +            }
 +        }
 +    }
 +
 +    if args.len() == 2 {
 +        match args[1].kind {
 +            hir::ExprKind::Call(fun, or_args) => {
 +                let or_has_args = !or_args.is_empty();
 +                if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
 +                    let fun_span = if or_has_args { None } else { Some(fun.span) };
 +                    check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
 +                check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
 +            },
 +            hir::ExprKind::Block(block, _) => {
 +                if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
 +                    if let Some(block_expr) = block.expr {
 +                        if let hir::ExprKind::MethodCall(..) = block_expr.kind {
 +                            check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
 +                        }
 +                    }
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..59565350f72966260ad0913d0c208e8b498acb4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
++
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_hir::{def::Res, def_id::DefId, Crate, Item, ItemKind, UseKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Symbol;
++
++use crate::utils::conf::Rename;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for imports that do not rename the item as specified
++    /// in the `enforce-import-renames` config option.
++    ///
++    /// **Why is this bad?** Consistency is important, if a project has defined import
++    /// renames they should be followed. More practically, some item names are too
++    /// vague outside of their defining scope this can enforce a more meaningful naming.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// An example clippy.toml configuration:
++    /// ```toml
++    /// # clippy.toml
++    /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
++    /// ```
++    ///
++    /// ```rust,ignore
++    /// use serde_json::Value;
++    /// ```
++    /// Use instead:
++    /// ```rust,ignore
++    /// use serde_json::Value as JsonValue;
++    /// ```
++    pub MISSING_ENFORCED_IMPORT_RENAMES,
++    restriction,
++    "enforce import renames"
++}
++
++pub struct ImportRename {
++    conf_renames: Vec<Rename>,
++    renames: FxHashMap<DefId, Symbol>,
++}
++
++impl ImportRename {
++    pub fn new(conf_renames: Vec<Rename>) -> Self {
++        Self {
++            conf_renames,
++            renames: FxHashMap::default(),
++        }
++    }
++}
++
++impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]);
++
++impl LateLintPass<'_> for ImportRename {
++    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
++        for Rename { path, rename } in &self.conf_renames {
++            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) {
++                self.renames.insert(id, Symbol::intern(rename));
++            }
++        }
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        if_chain! {
++            if let ItemKind::Use(path, UseKind::Single) = &item.kind;
++            if let Res::Def(_, id) = path.res;
++            if let Some(name) = self.renames.get(&id);
++            // Remove semicolon since it is not present for nested imports
++            let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
++            if let Some(snip) = snippet_opt(cx, span_without_semi);
++            if let Some(import) = match snip.split_once(" as ") {
++                None => Some(snip.as_str()),
++                Some((import, rename)) => {
++                    if rename.trim() == &*name.as_str() {
++                        None
++                    } else {
++                        Some(import.trim())
++                    }
++                },
++            };
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    MISSING_ENFORCED_IMPORT_RENAMES,
++                    span_without_semi,
++                    "this import should be renamed",
++                    "try",
++                    format!(
++                        "{} as {}",
++                        import,
++                        name,
++                    ),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
index 1786d5805d78b342dfa1b5c693d76b8db1aeae62,0000000000000000000000000000000000000000..6c87136e5e14e0a235e697e4bd071d7f1cefcff3
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,129 @@@
-     correctness,
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{match_def_path, paths, trait_ref_of_method};
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::TypeFoldable;
 +use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for sets/maps with mutable key types.
 +    ///
 +    /// **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and
 +    /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
 +    /// so having types with interior mutability is a bad idea.
 +    ///
 +    /// **Known problems:** It's correct to use a struct, that contains interior mutability
 +    /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
 +    /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
 +    /// The `bytes` crate is a great example of this.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// use std::cmp::{PartialEq, Eq};
 +    /// use std::collections::HashSet;
 +    /// use std::hash::{Hash, Hasher};
 +    /// use std::sync::atomic::AtomicUsize;
 +    ///# #[allow(unused)]
 +    ///
 +    /// struct Bad(AtomicUsize);
 +    /// impl PartialEq for Bad {
 +    ///     fn eq(&self, rhs: &Self) -> bool {
 +    ///          ..
 +    /// ; unimplemented!();
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Eq for Bad {}
 +    ///
 +    /// impl Hash for Bad {
 +    ///     fn hash<H: Hasher>(&self, h: &mut H) {
 +    ///         ..
 +    /// ; unimplemented!();
 +    ///     }
 +    /// }
 +    ///
 +    /// fn main() {
 +    ///     let _: HashSet<Bad> = HashSet::new();
 +    /// }
 +    /// ```
 +    pub MUTABLE_KEY_TYPE,
++    suspicious,
 +    "Check for mutable `Map`/`Set` key type"
 +}
 +
 +declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
 +        if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
 +            check_sig(cx, item.hir_id(), sig.decl);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
 +        if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
 +            if trait_ref_of_method(cx, item.hir_id()).is_none() {
 +                check_sig(cx, item.hir_id(), sig.decl);
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            check_sig(cx, item.hir_id(), sig.decl);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
 +        if let hir::PatKind::Wild = local.pat.kind {
 +            return;
 +        }
 +        check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat));
 +    }
 +}
 +
 +fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
 +    let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
 +    let fn_sig = cx.tcx.fn_sig(fn_def_id);
 +    for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
 +        check_ty(cx, hir_ty.span, ty);
 +    }
 +    check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
 +}
 +
 +// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
 +// generics (because the compiler cannot ensure immutability for unknown types).
 +fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
 +    let ty = ty.peel_refs();
 +    if let Adt(def, substs) = ty.kind() {
 +        if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
 +            .iter()
 +            .any(|path| match_def_path(cx, def.did, &**path))
 +            && is_mutable_type(cx, substs.type_at(0), span)
 +        {
 +            span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
 +        }
 +    }
 +}
 +
 +fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
 +    match *ty.kind() {
 +        RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
 +            mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
 +        },
 +        Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
 +        Array(inner_ty, size) => {
 +            size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
 +        },
 +        Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
 +        Adt(..) => {
 +            cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
 +                && !ty.has_escaping_bound_vars()
 +                && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
 +        },
 +        _ => false,
 +    }
 +}
index b2206a822088e01e9d6798a451585e533a0b1b6f,0000000000000000000000000000000000000000..910b05360925d53dd6a6fd39aa14dbc8066d5cd0
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,179 @@@
-                         _ => reduce_expression(cx, e),
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::has_drop;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::ops::Deref;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for statements which have no effect.
 +    ///
 +    /// **Why is this bad?** Similar to dead code, these statements are actually
 +    /// executed. However, as they have no effect, all they do is make the code less
 +    /// readable.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// 0;
 +    /// ```
 +    pub NO_EFFECT,
 +    complexity,
 +    "statements with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for expression statements that can be reduced to a
 +    /// sub-expression.
 +    ///
 +    /// **Why is this bad?** Expressions by themselves often have no side-effects.
 +    /// Having such expressions reduces readability.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// compute_array()[0];
 +    /// ```
 +    pub UNNECESSARY_OPERATION,
 +    complexity,
 +    "outer expressions with no effect"
 +}
 +
 +fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if expr.span.from_expansion() {
 +        return false;
 +    }
 +    match expr.kind {
 +        ExprKind::Lit(..) | ExprKind::Closure(..) => true,
 +        ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
 +        ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
 +        ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => has_no_effect(cx, inner),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            !has_drop(cx, cx.typeck_results().expr_ty(expr))
 +                && fields.iter().all(|field| has_no_effect(cx, field.expr))
 +                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                match res {
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => {
 +                        !has_drop(cx, cx.typeck_results().expr_ty(expr))
 +                            && args.iter().all(|arg| has_no_effect(cx, arg))
 +                    },
 +                    _ => false,
 +                }
 +            } else {
 +                false
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NoEffect {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let StmtKind::Semi(expr) = stmt.kind {
 +            if has_no_effect(cx, expr) {
 +                span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
 +            } else if let Some(reduced) = reduce_expression(cx, expr) {
 +                let mut snippet = String::new();
 +                for e in reduced {
 +                    if e.span.from_expansion() {
 +                        return;
 +                    }
 +                    if let Some(snip) = snippet_opt(cx, e.span) {
 +                        snippet.push_str(&snip);
 +                        snippet.push(';');
 +                    } else {
 +                        return;
 +                    }
 +                }
 +                span_lint_hir_and_then(
 +                    cx,
 +                    UNNECESSARY_OPERATION,
 +                    expr.hir_id,
 +                    stmt.span,
 +                    "statement can be reduced",
 +                    |diag| {
 +                        diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable);
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
 +    if expr.span.from_expansion() {
 +        return None;
 +    }
 +    match expr.kind {
 +        ExprKind::Index(a, b) => Some(vec![a, b]),
 +        ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
 +            Some(vec![a, b])
 +        },
 +        ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
 +                None
 +            } else {
 +                Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
 +            }
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                match res {
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                        if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
 +                    {
 +                        Some(args.iter().collect())
 +                    },
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            if block.stmts.is_empty() {
 +                block.expr.as_ref().and_then(|e| {
 +                    match block.rules {
 +                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
 +                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
 +                        // in case of compiler-inserted signaling blocks
++                        BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
 +                    }
 +                })
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1adad5be6ddc591f2cf11944e4b98f8c1d5554fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,276 @@@
++use std::{
++    fmt,
++    hash::{Hash, Hasher},
++};
++
++use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++use serde::{de, Deserialize};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks that common macros are used with consistent bracing.
++    ///
++    /// **Why is this bad?** This is mostly a consistency lint although using () or []
++    /// doesn't give you a semicolon in item position, which can be unexpected.
++    ///
++    /// **Known problems:**
++    /// None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// vec!{1, 2, 3};
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// vec![1, 2, 3];
++    /// ```
++    pub NONSTANDARD_MACRO_BRACES,
++    style,
++    "check consistent use of braces in macro"
++}
++
++const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
++
++/// The (name, (open brace, close brace), source snippet)
++type MacroInfo<'a> = (&'a str, &'a (String, String), String);
++
++#[derive(Clone, Debug, Default)]
++pub struct MacroBraces {
++    macro_braces: FxHashMap<String, (String, String)>,
++    done: FxHashSet<Span>,
++}
++
++impl MacroBraces {
++    pub fn new(conf: &FxHashSet<MacroMatcher>) -> Self {
++        let macro_braces = macro_braces(conf.clone());
++        Self {
++            macro_braces,
++            done: FxHashSet::default(),
++        }
++    }
++}
++
++impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]);
++
++impl EarlyLintPass for MacroBraces {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
++        if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) {
++            let span = item.span.ctxt().outer_expn_data().call_site;
++            emit_help(cx, snip, braces, name, span);
++            self.done.insert(span);
++        }
++    }
++
++    fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
++        if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) {
++            let span = stmt.span.ctxt().outer_expn_data().call_site;
++            emit_help(cx, snip, braces, name, span);
++            self.done.insert(span);
++        }
++    }
++
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
++        if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) {
++            let span = expr.span.ctxt().outer_expn_data().call_site;
++            emit_help(cx, snip, braces, name, span);
++            self.done.insert(span);
++        }
++    }
++
++    fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
++        if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) {
++            let span = ty.span.ctxt().outer_expn_data().call_site;
++            emit_help(cx, snip, braces, name, span);
++            self.done.insert(span);
++        }
++    }
++}
++
++fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option<MacroInfo<'a>> {
++    if_chain! {
++        if in_macro(span);
++        if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces);
++        if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
++        let c = snip.replace(" ", ""); // make formatting consistent
++        if !c.starts_with(&format!("{}!{}", name, braces.0));
++        if !this.done.contains(&span.ctxt().outer_expn_data().call_site);
++        then {
++            Some((name, braces, snip))
++        } else {
++            None
++        }
++    }
++}
++
++fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
++    let with_space = &format!("! {}", braces.0);
++    let without_space = &format!("!{}", braces.0);
++    let mut help = snip;
++    for b in BRACES.iter().filter(|b| b.0 != braces.0) {
++        help = help.replace(b.0, &braces.0).replace(b.1, &braces.1);
++        // Only `{` traditionally has space before the brace
++        if braces.0 != "{" && help.contains(with_space) {
++            help = help.replace(with_space, without_space);
++        } else if braces.0 == "{" && help.contains(without_space) {
++            help = help.replace(without_space, with_space);
++        }
++    }
++    span_lint_and_help(
++        cx,
++        NONSTANDARD_MACRO_BRACES,
++        span,
++        &format!("use of irregular braces for `{}!` macro", name),
++        Some(span),
++        &format!("consider writing `{}`", help),
++    );
++}
++
++fn find_matching_macro(
++    span: Span,
++    braces: &FxHashMap<String, (String, String)>,
++) -> Option<(&String, &(String, String))> {
++    braces
++        .iter()
++        .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
++}
++
++fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
++    let mut braces = vec![
++        macro_matcher!(
++            name: "print",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "println",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "eprint",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "eprintln",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "write",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "writeln",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "format",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "format_args",
++            braces: ("(", ")"),
++        ),
++        macro_matcher!(
++            name: "vec",
++            braces: ("[", "]"),
++        ),
++    ]
++    .into_iter()
++    .collect::<FxHashMap<_, _>>();
++    // We want users items to override any existing items
++    for it in conf {
++        braces.insert(it.name, it.braces);
++    }
++    braces
++}
++
++macro_rules! macro_matcher {
++    (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => {
++        ($name.to_owned(), ($open.to_owned(), $close.to_owned()))
++    };
++}
++pub(crate) use macro_matcher;
++
++#[derive(Clone, Debug)]
++pub struct MacroMatcher {
++    name: String,
++    braces: (String, String),
++}
++
++impl Hash for MacroMatcher {
++    fn hash<H: Hasher>(&self, state: &mut H) {
++        self.name.hash(state);
++    }
++}
++
++impl PartialEq for MacroMatcher {
++    fn eq(&self, other: &Self) -> bool {
++        self.name == other.name
++    }
++}
++impl Eq for MacroMatcher {}
++
++impl<'de> Deserialize<'de> for MacroMatcher {
++    fn deserialize<D>(deser: D) -> Result<Self, D::Error>
++    where
++        D: de::Deserializer<'de>,
++    {
++        #[derive(Deserialize)]
++        #[serde(field_identifier, rename_all = "lowercase")]
++        enum Field {
++            Name,
++            Brace,
++        }
++        struct MacVisitor;
++        impl<'de> de::Visitor<'de> for MacVisitor {
++            type Value = MacroMatcher;
++
++            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
++                formatter.write_str("struct MacroMatcher")
++            }
++
++            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
++            where
++                V: de::MapAccess<'de>,
++            {
++                let mut name = None;
++                let mut brace: Option<&str> = None;
++                while let Some(key) = map.next_key()? {
++                    match key {
++                        Field::Name => {
++                            if name.is_some() {
++                                return Err(de::Error::duplicate_field("name"));
++                            }
++                            name = Some(map.next_value()?);
++                        },
++                        Field::Brace => {
++                            if brace.is_some() {
++                                return Err(de::Error::duplicate_field("brace"));
++                            }
++                            brace = Some(map.next_value()?);
++                        },
++                    }
++                }
++                let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
++                let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
++                Ok(MacroMatcher {
++                    name,
++                    braces: BRACES
++                        .iter()
++                        .find(|b| b.0 == brace)
++                        .map(|(o, c)| ((*o).to_owned(), (*c).to_owned()))
++                        .ok_or_else(|| {
++                            de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace))
++                        })?,
++                })
++            }
++        }
++
++        const FIELDS: &[&str] = &["name", "brace"];
++        deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
++    }
++}
index ae5f0627fd65a41d5d271f895d5bd1622b48a5be,0000000000000000000000000000000000000000..b41c478c266157d5295155ffdc4103aed7847452
mode 100644,000000..100644
--- /dev/null
@@@ -1,535 -1,0 +1,535 @@@
-         if len_path.ident.name == sym!(len) && len_args.len() == 1;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
 +use clippy_utils::{higher, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::RangeLimits;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::ty;
 +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 rustc_span::symbol::Ident;
 +use std::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for zipping a collection with the range of
 +    /// `0.._.len()`.
 +    ///
 +    /// **Why is this bad?** The code is better expressed with `.enumerate()`.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// x.iter().zip(0..x.len());
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// x.iter().enumerate();
 +    /// ```
 +    pub RANGE_ZIP_WITH_LEN,
 +    complexity,
 +    "zipping iterator with a range when `enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for exclusive ranges where 1 is added to the
 +    /// upper bound, e.g., `x..(y+1)`.
 +    ///
 +    /// **Why is this bad?** The code is more readable with an inclusive range
 +    /// like `x..=y`.
 +    ///
 +    /// **Known problems:** Will add unnecessary pair of parentheses when the
 +    /// expression is not wrapped in a pair but starts with a opening parenthesis
 +    /// and ends with a closing one.
 +    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 +    ///
 +    /// Also in many cases, inclusive ranges are still slower to run than
 +    /// exclusive ranges, because they essentially add an extra branch that
 +    /// LLVM may fail to hoist out of the loop.
 +    ///
 +    /// This will cause a warning that cannot be fixed if the consumer of the
 +    /// range only accepts a specific range type, instead of the generic
 +    /// `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// for x..(y+1) { .. }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust,ignore
 +    /// for x..=y { .. }
 +    /// ```
 +    pub RANGE_PLUS_ONE,
 +    pedantic,
 +    "`x..(y+1)` reads better as `x..=y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for inclusive ranges where 1 is subtracted from
 +    /// the upper bound, e.g., `x..=(y-1)`.
 +    ///
 +    /// **Why is this bad?** The code is more readable with an exclusive range
 +    /// like `x..y`.
 +    ///
 +    /// **Known problems:** This will cause a warning that cannot be fixed if
 +    /// the consumer of the range only accepts a specific range type, instead of
 +    /// the generic `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// for x..=(y-1) { .. }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust,ignore
 +    /// for x..y { .. }
 +    /// ```
 +    pub RANGE_MINUS_ONE,
 +    pedantic,
 +    "`x..=(y-1)` reads better as `x..y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for range expressions `x..y` where both `x` and `y`
 +    /// are constant and `x` is greater or equal to `y`.
 +    ///
 +    /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.
 +    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust,no_run
 +    /// fn main() {
 +    ///     (10..=0).for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[3..1];
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     (0..=10).rev().for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[1..3];
 +    /// }
 +    /// ```
 +    pub REVERSED_EMPTY_RANGES,
 +    correctness,
 +    "reversing the limits of range expressions, resulting in empty ranges"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could
 +    /// be more readably expressed as `(3..8).contains(x)`.
 +    ///
 +    /// **Why is this bad?** `contains` expresses the intent better and has less
 +    /// failure modes (such as fencepost errors or using `||` instead of `&&`).
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // given
 +    /// let x = 6;
 +    ///
 +    /// assert!(x >= 3 && x < 8);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    ///# let x = 6;
 +    /// assert!((3..8).contains(&x));
 +    /// ```
 +    pub MANUAL_RANGE_CONTAINS,
 +    style,
 +    "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 +}
 +
 +pub struct Ranges {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Ranges {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Ranges => [
 +    RANGE_ZIP_WITH_LEN,
 +    RANGE_PLUS_ONE,
 +    RANGE_MINUS_ONE,
 +    REVERSED_EMPTY_RANGES,
 +    MANUAL_RANGE_CONTAINS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ranges {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::MethodCall(path, _, args, _) => {
 +                check_range_zip_with_len(cx, path, args, expr.span);
 +            },
 +            ExprKind::Binary(ref op, l, r) => {
 +                if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) {
 +                    check_possible_range_contains(cx, op.node, l, r, expr);
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        check_exclusive_range_plus_one(cx, expr);
 +        check_inclusive_range_minus_one(cx, expr);
 +        check_reversed_empty_range(cx, expr);
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
 +    if in_constant(cx, expr.hir_id) {
 +        return;
 +    }
 +
 +    let span = expr.span;
 +    let combine_and = match op {
 +        BinOpKind::And | BinOpKind::BitAnd => true,
 +        BinOpKind::Or | BinOpKind::BitOr => false,
 +        _ => return,
 +    };
 +    // value, name, order (higher/lower), inclusiveness
 +    if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) =
 +        (check_range_bounds(cx, l), check_range_bounds(cx, r))
 +    {
 +        // we only lint comparisons on the same name and with different
 +        // direction
 +        if lname != rname || lord == rord {
 +            return;
 +        }
 +        let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
 +        if combine_and && ord == Some(rord) {
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less {
 +                (lval_span, rval_span, linc, rinc)
 +            } else {
 +                (rval_span, lval_span, rinc, linc)
 +            };
 +            // we only lint inclusive lower bounds
 +            if !l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("RangeInclusive", "..=")
 +            } else {
 +                ("Range", "..")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `{}::contains` implementation", range_type),
 +                "use",
 +                format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        } else if !combine_and && ord == Some(lord) {
 +            // `!_.contains(_)`
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less {
 +                (lval_span, rval_span, linc, rinc)
 +            } else {
 +                (rval_span, lval_span, rinc, linc)
 +            };
 +            if l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("Range", "..")
 +            } else {
 +                ("RangeInclusive", "..=")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `!{}::contains` implementation", range_type),
 +                "use",
 +                format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> {
 +    if let ExprKind::Binary(ref op, l, r) = ex.kind {
 +        let (inclusive, ordering) = match op.node {
 +            BinOpKind::Gt => (false, Ordering::Greater),
 +            BinOpKind::Ge => (true, Ordering::Greater),
 +            BinOpKind::Lt => (false, Ordering::Less),
 +            BinOpKind::Le => (true, Ordering::Less),
 +            _ => return None,
 +        };
 +        if let Some(id) = match_ident(l) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
 +                return Some((c, id, l.span, r.span, ordering, inclusive));
 +            }
 +        } else if let Some(id) = match_ident(r) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
 +                return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +fn match_ident(e: &Expr<'_>) -> Option<Ident> {
 +    if let ExprKind::Path(ref qpath) = e.kind {
 +        if let Some(seg) = single_segment_path(qpath) {
 +            if seg.args.is_none() {
 +                return Some(seg.ident);
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
 +    if_chain! {
 +        if path.ident.as_str() == "zip";
 +        if let [iter, zip_arg] = args;
 +        // `.iter()` call
 +        if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind;
 +        if iter_path.ident.name == sym::iter;
 +        // range expression in `.zip()` call: `0..x.len()`
 +        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
 +        if is_integer_const(cx, start, 0);
 +        // `.len()` call
 +        if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind;
++        if len_path.ident.name == sym::len && len_args.len() == 1;
 +        // `.iter()` and `.len()` called on same `Path`
 +        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
 +        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
 +        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
 +        then {
 +            span_lint(cx,
 +                RANGE_ZIP_WITH_LEN,
 +                span,
 +                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
 +                    snippet(cx, iter_args[0].span, "_"))
 +            );
 +        }
 +    }
 +}
 +
 +// exclusive range plus one: `x..(y+1)`
 +fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range {
 +            start,
 +            end: Some(end),
 +            limits: RangeLimits::HalfOpen
 +        }) = higher::range(expr);
 +        if let Some(y) = y_plus_one(cx, end);
 +        then {
 +            let span = if expr.span.from_expansion() {
 +                expr.span
 +                    .ctxt()
 +                    .outer_expn_data()
 +                    .call_site
 +            } else {
 +                expr.span
 +            };
 +            span_lint_and_then(
 +                cx,
 +                RANGE_PLUS_ONE,
 +                span,
 +                "an inclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
 +                    let end = Sugg::hir(cx, y, "y");
 +                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
 +                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("({}..={})", start, end),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        } else {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("{}..={}", start, end),
 +                                Applicability::MachineApplicable, // snippet
 +                            );
 +                        }
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +// inclusive range minus one: `x..=(y-1)`
 +fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr);
 +        if let Some(y) = y_minus_one(cx, end);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                RANGE_MINUS_ONE,
 +                expr.span,
 +                "an exclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
 +                    let end = Sugg::hir(cx, y, "y");
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "use",
 +                        format!("{}..{}", start, end),
 +                        Applicability::MachineApplicable, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        matches!(
 +            get_parent_expr(cx, expr),
 +            Some(Expr {
 +                kind: ExprKind::Index(..),
 +                ..
 +            })
 +        )
 +    }
 +
 +    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        let mut cur_expr = expr;
 +        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
 +            match higher::for_loop(parent_expr) {
 +                Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true,
 +                _ => cur_expr = parent_expr,
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
 +        match limits {
 +            RangeLimits::HalfOpen => ordering != Ordering::Less,
 +            RangeLimits::Closed => ordering == Ordering::Greater,
 +        }
 +    }
 +
 +    if_chain! {
 +        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr);
 +        let ty = cx.typeck_results().expr_ty(start);
 +        if let ty::Int(_) | ty::Uint(_) = ty.kind();
 +        if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
 +        if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
 +        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
 +        if is_empty_range(limits, ordering);
 +        then {
 +            if inside_indexing_expr(cx, expr) {
 +                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
 +                if ordering != Ordering::Equal {
 +                    span_lint(
 +                        cx,
 +                        REVERSED_EMPTY_RANGES,
 +                        expr.span,
 +                        "this range is reversed and using it to index a slice will panic at run-time",
 +                    );
 +                }
 +            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
 +            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
 +                span_lint_and_then(
 +                    cx,
 +                    REVERSED_EMPTY_RANGES,
 +                    expr.span,
 +                    "this range is empty so it will yield no values",
 +                    |diag| {
 +                        if ordering != Ordering::Equal {
 +                            let start_snippet = snippet(cx, start.span, "_");
 +                            let end_snippet = snippet(cx, end.span, "_");
 +                            let dots = match limits {
 +                                RangeLimits::HalfOpen => "..",
 +                                RangeLimits::Closed => "..="
 +                            };
 +
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using the following if you are attempting to iterate over this \
 +                                 range in reverse",
 +                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) => {
 +            if is_integer_const(cx, lhs, 1) {
 +                Some(rhs)
 +            } else if is_integer_const(cx, rhs, 1) {
 +                Some(lhs)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
 +        _ => None,
 +    }
 +}
index 7dafce60d5eda1cc2c09bbf31913a4dc277772d7,0000000000000000000000000000000000000000..119ec21520f13c932acb14b62285a84da3230c13
mode 100644,000000..100644
--- /dev/null
@@@ -1,672 -1,0 +1,672 @@@
-             // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
 +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{def_id, Body, FnDecl, HirId};
 +use rustc_index::bit_set::{BitSet, HybridBitSet};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::{
 +    self, traversal,
 +    visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
 +};
 +use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
 +use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{BytePos, Span};
 +use rustc_span::sym;
 +use std::convert::TryFrom;
 +use std::ops::ControlFlow;
 +
 +macro_rules! unwrap_or_continue {
 +    ($x:expr) => {
 +        match $x {
 +            Some(x) => x,
 +            None => continue,
 +        }
 +    };
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned
 +    /// value that is going to be dropped without further use.
 +    ///
 +    /// **Why is this bad?** It is not always possible for the compiler to eliminate useless
 +    /// allocations and deallocations generated by redundant `clone()`s.
 +    ///
 +    /// **Known problems:**
 +    ///
 +    /// False-negatives: analysis performed by this lint is conservative and limited.
 +    ///
 +    /// **Example:**
 +    /// ```rust
 +    /// # use std::path::Path;
 +    /// # #[derive(Clone)]
 +    /// # struct Foo;
 +    /// # impl Foo {
 +    /// #     fn new() -> Self { Foo {} }
 +    /// # }
 +    /// # fn call(x: Foo) {}
 +    /// {
 +    ///     let x = Foo::new();
 +    ///     call(x.clone());
 +    ///     call(x.clone()); // this can just pass `x`
 +    /// }
 +    ///
 +    /// ["lorem", "ipsum"].join(" ").to_string();
 +    ///
 +    /// Path::new("/a/b").join("c").to_path_buf();
 +    /// ```
 +    pub REDUNDANT_CLONE,
 +    perf,
 +    "`clone()` of an owned value that is going to be dropped immediately"
 +}
 +
 +declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClone {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
 +
 +        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
 +        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 +
 +        let maybe_storage_live_result = MaybeStorageLive
 +            .into_engine(cx.tcx, mir)
 +            .pass_name("redundant_clone")
 +            .iterate_to_fixpoint()
 +            .into_results_cursor(mir);
 +        let mut possible_borrower = {
 +            let mut vis = PossibleBorrowerVisitor::new(cx, mir);
 +            vis.visit_body(mir);
 +            vis.into_map(cx, maybe_storage_live_result)
 +        };
 +
 +        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
 +            let terminator = bbdata.terminator();
 +
 +            if terminator.source_info.span.from_expansion() {
 +                continue;
 +            }
 +
 +            // Give up on loops
 +            if terminator.successors().any(|s| *s == bb) {
 +                continue;
 +            }
 +
 +            let (fn_def_id, arg, arg_ty, clone_ret) =
 +                unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 +
 +            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
 +                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
 +                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
 +                    && is_type_diagnostic_item(cx, arg_ty, sym::string_type));
 +
 +            let from_deref = !from_borrow
 +                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
 +                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
 +
 +            if !from_borrow && !from_deref {
 +                continue;
 +            }
 +
 +            if let ty::Adt(def, _) = arg_ty.kind() {
 +                if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) {
 +                    continue;
 +                }
 +            }
 +
++            // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
 +            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
 +
 +            let loc = mir::Location {
 +                block: bb,
 +                statement_index: bbdata.statements.len(),
 +            };
 +
 +            // `Local` to be cloned, and a local of `clone` call's destination
 +            let (local, ret_local) = if from_borrow {
 +                // `res = clone(arg)` can be turned into `res = move arg;`
 +                // if `arg` is the only borrow of `cloned` at this point.
 +
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
 +                    continue;
 +                }
 +
 +                (cloned, clone_ret)
 +            } else {
 +                // `arg` is a reference as it is `.deref()`ed in the previous block.
 +                // Look into the predecessor block and find out the source of deref.
 +
 +                let ps = &mir.predecessors()[bb];
 +                if ps.len() != 1 {
 +                    continue;
 +                }
 +                let pred_terminator = mir[ps[0]].terminator();
 +
 +                // receiver of the `deref()` call
 +                let (pred_arg, deref_clone_ret) = if_chain! {
 +                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
 +                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
 +                    if res == cloned;
 +                    if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id);
 +                    if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
 +                        || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString);
 +                    then {
 +                        (pred_arg, res)
 +                    } else {
 +                        continue;
 +                    }
 +                };
 +
 +                let (local, cannot_move_out) =
 +                    unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
 +                let loc = mir::Location {
 +                    block: bb,
 +                    statement_index: mir.basic_blocks()[bb].statements.len(),
 +                };
 +
 +                // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
 +                // at the last statement:
 +                //
 +                // ```
 +                // pred_arg = &local;
 +                // cloned = deref(pred_arg);
 +                // arg = &cloned;
 +                // StorageDead(pred_arg);
 +                // res = to_path_buf(cloned);
 +                // ```
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
 +                    continue;
 +                }
 +
 +                (local, deref_clone_ret)
 +            };
 +
 +            let clone_usage = if local == ret_local {
 +                CloneUsage {
 +                    cloned_used: false,
 +                    cloned_consume_or_mutate_loc: None,
 +                    clone_consumed_or_mutated: true,
 +                }
 +            } else {
 +                let clone_usage = visit_clone_usage(local, ret_local, mir, bb);
 +                if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated {
 +                    // cloned value is used, and the clone is modified or moved
 +                    continue;
 +                } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
 +                    // cloned value is mutated, and the clone is alive.
 +                    if possible_borrower.is_alive_at(ret_local, loc) {
 +                        continue;
 +                    }
 +                }
 +                clone_usage
 +            };
 +
 +            let span = terminator.source_info.span;
 +            let scope = terminator.source_info.scope;
 +            let node = mir.source_scopes[scope]
 +                .local_data
 +                .as_ref()
 +                .assert_crate_local()
 +                .lint_root;
 +
 +            if_chain! {
 +                if let Some(snip) = snippet_opt(cx, span);
 +                if let Some(dot) = snip.rfind('.');
 +                then {
 +                    let sugg_span = span.with_lo(
 +                        span.lo() + BytePos(u32::try_from(dot).unwrap())
 +                    );
 +                    let mut app = Applicability::MaybeIncorrect;
 +
 +                    let call_snip = &snip[dot + 1..];
 +                    // Machine applicable when `call_snip` looks like `foobar()`
 +                    if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
 +                        if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
 +                            app = Applicability::MachineApplicable;
 +                        }
 +                    }
 +
 +                    span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
 +                        diag.span_suggestion(
 +                            sugg_span,
 +                            "remove this",
 +                            String::new(),
 +                            app,
 +                        );
 +                        if clone_usage.cloned_used {
 +                            diag.span_note(
 +                                span,
 +                                "cloned value is neither consumed nor mutated",
 +                            );
 +                        } else {
 +                            diag.span_note(
 +                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
 +                                "this value is dropped without further use",
 +                            );
 +                        }
 +                    });
 +                } else {
 +                    span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
 +fn is_call_with_ref_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &'tcx mir::Body<'tcx>,
 +    kind: &'tcx mir::TerminatorKind<'tcx>,
 +) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
 +    if_chain! {
 +        if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
 +        if args.len() == 1;
 +        if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
 +        if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind();
 +        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
 +        if !is_copy(cx, inner_ty);
 +        then {
 +            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +type CannotMoveOut = bool;
 +
 +/// Finds the first `to = (&)from`, and returns
 +/// ``Some((from, whether `from` cannot be moved out))``.
 +fn find_stmt_assigns_to<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    to_local: mir::Local,
 +    by_ref: bool,
 +    bb: mir::BasicBlock,
 +) -> Option<(mir::Local, CannotMoveOut)> {
 +    let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| {
 +        if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
 +            return if *local == to_local { Some(v) } else { None };
 +        }
 +
 +        None
 +    })?;
 +
 +    match (by_ref, &*rvalue) {
 +        (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
 +            Some(base_local_and_movability(cx, mir, *place))
 +        },
 +        (false, mir::Rvalue::Ref(_, _, place)) => {
 +            if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
 +                Some(base_local_and_movability(cx, mir, *place))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
 +/// if it is already a `Local`.
 +///
 +/// Also reports whether given `place` cannot be moved out.
 +fn base_local_and_movability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    place: mir::Place<'tcx>,
 +) -> (mir::Local, CannotMoveOut) {
 +    use rustc_middle::mir::PlaceRef;
 +
 +    // Dereference. You cannot move things out from a borrowed value.
 +    let mut deref = false;
 +    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
 +    let mut field = false;
 +    // If projection is a slice index then clone can be removed only if the
 +    // underlying type implements Copy
 +    let mut slice = false;
 +
 +    let PlaceRef { local, mut projection } = place.as_ref();
 +    while let [base @ .., elem] = projection {
 +        projection = base;
 +        deref |= matches!(elem, mir::ProjectionElem::Deref);
 +        field |= matches!(elem, mir::ProjectionElem::Field(..))
 +            && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +        slice |= matches!(elem, mir::ProjectionElem::Index(..))
 +            && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +    }
 +
 +    (local, deref || field || slice)
 +}
 +
 +#[derive(Default)]
 +struct CloneUsage {
 +    /// Whether the cloned value is used after the clone.
 +    cloned_used: bool,
 +    /// The first location where the cloned value is consumed or mutated, if any.
 +    cloned_consume_or_mutate_loc: Option<mir::Location>,
 +    /// Whether the clone value is mutated.
 +    clone_consumed_or_mutated: bool,
 +}
 +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
 +    struct V {
 +        cloned: mir::Local,
 +        clone: mir::Local,
 +        result: CloneUsage,
 +    }
 +    impl<'tcx> mir::visit::Visitor<'tcx> for V {
 +        fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
 +            let statements = &data.statements;
 +            for (statement_index, statement) in statements.iter().enumerate() {
 +                self.visit_statement(statement, mir::Location { block, statement_index });
 +            }
 +
 +            self.visit_terminator(
 +                data.terminator(),
 +                mir::Location {
 +                    block,
 +                    statement_index: statements.len(),
 +                },
 +            );
 +        }
 +
 +        fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
 +            let local = place.local;
 +
 +            if local == self.cloned
 +                && !matches!(
 +                    ctx,
 +                    PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
 +                )
 +            {
 +                self.result.cloned_used = true;
 +                self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
 +                    matches!(
 +                        ctx,
 +                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                            | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
 +                    )
 +                    .then(|| loc)
 +                });
 +            } else if local == self.clone {
 +                match ctx {
 +                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                    | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
 +                        self.result.clone_consumed_or_mutated = true;
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +
 +    let init = CloneUsage {
 +        cloned_used: false,
 +        cloned_consume_or_mutate_loc: None,
 +        // Consider non-temporary clones consumed.
 +        // TODO: Actually check for mutation of non-temporaries.
 +        clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
 +    };
 +    traversal::ReversePostorder::new(mir, bb)
 +        .skip(1)
 +        .fold(init, |usage, (tbb, tdata)| {
 +            // Short-circuit
 +            if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
 +                // Give up on loops
 +                tdata.terminator().successors().any(|s| *s == bb)
 +            {
 +                return CloneUsage {
 +                    cloned_used: true,
 +                    clone_consumed_or_mutated: true,
 +                    ..usage
 +                };
 +            }
 +
 +            let mut v = V {
 +                cloned,
 +                clone,
 +                result: usage,
 +            };
 +            v.visit_basic_block_data(tbb, tdata);
 +            v.result
 +        })
 +}
 +
 +/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
 +#[derive(Copy, Clone)]
 +struct MaybeStorageLive;
 +
 +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
 +    type Domain = BitSet<mir::Local>;
 +    const NAME: &'static str = "maybe_storage_live";
 +
 +    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
 +        // bottom = dead
 +        BitSet::new_empty(body.local_decls.len())
 +    }
 +
 +    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
 +        for arg in body.args_iter() {
 +            state.insert(arg);
 +        }
 +    }
 +}
 +
 +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
 +    type Idx = mir::Local;
 +
 +    fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
 +        match stmt.kind {
 +            mir::StatementKind::StorageLive(l) => trans.gen(l),
 +            mir::StatementKind::StorageDead(l) => trans.kill(l),
 +            _ => (),
 +        }
 +    }
 +
 +    fn terminator_effect(
 +        &self,
 +        _trans: &mut impl GenKill<Self::Idx>,
 +        _terminator: &mir::Terminator<'tcx>,
 +        _loc: mir::Location,
 +    ) {
 +    }
 +
 +    fn call_return_effect(
 +        &self,
 +        _in_out: &mut impl GenKill<Self::Idx>,
 +        _block: mir::BasicBlock,
 +        _func: &mir::Operand<'tcx>,
 +        _args: &[mir::Operand<'tcx>],
 +        _return_place: mir::Place<'tcx>,
 +    ) {
 +        // Nothing to do when a call returns successfully
 +    }
 +}
 +
 +/// Collects the possible borrowers of each local.
 +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
 +/// possible borrowers of `a`.
 +struct PossibleBorrowerVisitor<'a, 'tcx> {
 +    possible_borrower: TransitiveRelation<mir::Local>,
 +    body: &'a mir::Body<'tcx>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, body: &'a mir::Body<'tcx>) -> Self {
 +        Self {
 +            possible_borrower: TransitiveRelation::default(),
 +            cx,
 +            body,
 +        }
 +    }
 +
 +    fn into_map(
 +        self,
 +        cx: &LateContext<'tcx>,
 +        maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
 +    ) -> PossibleBorrowerMap<'a, 'tcx> {
 +        let mut map = FxHashMap::default();
 +        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
 +            if is_copy(cx, self.body.local_decls[row].ty) {
 +                continue;
 +            }
 +
 +            let borrowers = self.possible_borrower.reachable_from(&row);
 +            if !borrowers.is_empty() {
 +                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
 +                for &c in borrowers {
 +                    if c != mir::Local::from_usize(0) {
 +                        bs.insert(c);
 +                    }
 +                }
 +
 +                if !bs.is_empty() {
 +                    map.insert(row, bs);
 +                }
 +            }
 +        }
 +
 +        let bs = BitSet::new_empty(self.body.local_decls.len());
 +        PossibleBorrowerMap {
 +            map,
 +            maybe_live,
 +            bitset: (bs.clone(), bs),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
 +        let lhs = place.local;
 +        match rvalue {
 +            mir::Rvalue::Ref(_, _, borrowed) => {
 +                self.possible_borrower.add(borrowed.local, lhs);
 +            },
 +            other => {
 +                if ContainsRegion
 +                    .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
 +                    .is_continue()
 +                {
 +                    return;
 +                }
 +                rvalue_locals(other, |rhs| {
 +                    if lhs != rhs {
 +                        self.possible_borrower.add(rhs, lhs);
 +                    }
 +                });
 +            },
 +        }
 +    }
 +
 +    fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
 +        if let mir::TerminatorKind::Call {
 +            args,
 +            destination: Some((mir::Place { local: dest, .. }, _)),
 +            ..
 +        } = &terminator.kind
 +        {
 +            // If the call returns something with lifetimes,
 +            // let's conservatively assume the returned value contains lifetime of all the arguments.
 +            // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
 +            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() {
 +                return;
 +            }
 +
 +            for op in args {
 +                match op {
 +                    mir::Operand::Copy(p) | mir::Operand::Move(p) => {
 +                        self.possible_borrower.add(p.local, *dest);
 +                    },
 +                    mir::Operand::Constant(..) => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct ContainsRegion;
 +
 +impl TypeVisitor<'_> for ContainsRegion {
 +    type BreakTy = ();
 +
 +    fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
 +        ControlFlow::BREAK
 +    }
 +}
 +
 +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 +    use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
 +
 +    let mut visit_op = |op: &mir::Operand<'_>| match op {
 +        mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
 +        mir::Operand::Constant(..) => (),
 +    };
 +
 +    match rvalue {
 +        Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
 +        Aggregate(_, ops) => ops.iter().for_each(visit_op),
 +        BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            visit_op(lhs);
 +            visit_op(rhs);
 +        }
 +        _ => (),
 +    }
 +}
 +
 +/// Result of `PossibleBorrowerVisitor`.
 +struct PossibleBorrowerMap<'a, 'tcx> {
 +    /// Mapping `Local -> its possible borrowers`
 +    map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +    maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
 +    // Caches to avoid allocation of `BitSet` on every query
 +    bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
 +}
 +
 +impl PossibleBorrowerMap<'_, '_> {
 +    /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
 +    fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +
 +        self.bitset.0.clear();
 +        let maybe_live = &mut self.maybe_live;
 +        if let Some(bitset) = self.map.get(&borrowed) {
 +            for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
 +                self.bitset.0.insert(b);
 +            }
 +        } else {
 +            return false;
 +        }
 +
 +        self.bitset.1.clear();
 +        for b in borrowers {
 +            self.bitset.1.insert(*b);
 +        }
 +
 +        self.bitset.0 == self.bitset.1
 +    }
 +
 +    fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +        self.maybe_live.contains(local)
 +    }
 +}
index 16e4d73851fb4e89b67cb68ff184880254d4cee1,0000000000000000000000000000000000000000..da3e30af35ca4ffd7e2a8de07ef16ce6e028e04c
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,70 @@@
++use crate::rustc_lint::LintContext;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_macro_callsite;
 +use clippy_utils::{in_macro, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Looks for blocks of expressions and fires if the last expression returns
 +    /// `()` but is not followed by a semicolon.
 +    ///
 +    /// **Why is this bad?** The semicolon might be optional but when extending the block with new
 +    /// code, it doesn't require a change in previous last line.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// fn main() {
 +    ///     println!("Hello world")
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     println!("Hello world");
 +    /// }
 +    /// ```
 +    pub SEMICOLON_IF_NOTHING_RETURNED,
 +    pedantic,
 +    "add a semicolon if nothing is returned"
 +}
 +
 +declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]);
 +
 +impl LateLintPass<'_> for SemicolonIfNothingReturned {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
 +        if_chain! {
 +            if !in_macro(block.span);
 +            if let Some(expr) = block.expr;
 +            let t_expr = cx.typeck_results().expr_ty(expr);
 +            if t_expr.is_unit();
 +            if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
 +            if !snippet.ends_with('}');
++            if cx.sess().source_map().is_multiline(block.span);
 +            then {
 +                // filter out the desugared `for` loop
 +                if let ExprKind::DropTemps(..) = &expr.kind {
 +                    return;
 +                }
 +
 +                let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, "..");
 +                let suggestion = format!("{0};", sugg);
 +                span_lint_and_sugg(
 +                    cx,
 +                    SEMICOLON_IF_NOTHING_RETURNED,
 +                    expr.span.source_callsite(),
 +                    "consider adding a `;` to the last statement for consistent formatting",
 +                    "add a `;` here",
 +                    suggestion,
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
index 512abde11a63433a827f797f95002b6489ac6a20,0000000000000000000000000000000000000000..2203ab57b10820c626e99c0c0d1411b1d70004ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,208 @@@
-     correctness,
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g.
 +    /// subtracting elements in an Add impl.
 +    ///
 +    /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// impl Add for Foo {
 +    ///     type Output = Foo;
 +    ///
 +    ///     fn add(self, other: Foo) -> Foo {
 +    ///         Foo(self.0 - other.0)
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_ARITHMETIC_IMPL,
-     correctness,
++    suspicious,
 +    "suspicious use of operators in impl of arithmetic trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Lints for suspicious operations in impls of OpAssign, e.g.
 +    /// subtracting elements in an AddAssign impl.
 +    ///
 +    /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    /// ```ignore
 +    /// impl AddAssign for Foo {
 +    ///     fn add_assign(&mut self, other: Foo) {
 +    ///         *self = *self - other;
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_OP_ASSIGN_IMPL,
++    suspicious,
 +    "suspicious use of operators in impl of OpAssign trait"
 +}
 +
 +declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind {
 +            match binop.node {
 +                hir::BinOpKind::Eq
 +                | hir::BinOpKind::Lt
 +                | hir::BinOpKind::Le
 +                | hir::BinOpKind::Ne
 +                | hir::BinOpKind::Ge
 +                | hir::BinOpKind::Gt => return,
 +                _ => {},
 +            }
 +
 +            // Check for more than one binary operation in the implemented function
 +            // Linting when multiple operations are involved can result in false positives
 +            let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
 +            if_chain! {
 +                if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
 +                if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
 +                then {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut visitor = BinaryExprVisitor { nb_binops: 0 };
 +                    walk_expr(&mut visitor, &body.value);
 +                    if visitor.nb_binops > 1 {
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if let Some(impl_trait) = check_binop(
 +                cx,
 +                expr,
 +                binop.node,
 +                &[
 +                    "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr",
 +                ],
 +                &[
 +                    hir::BinOpKind::Add,
 +                    hir::BinOpKind::Sub,
 +                    hir::BinOpKind::Mul,
 +                    hir::BinOpKind::Div,
 +                    hir::BinOpKind::Rem,
 +                    hir::BinOpKind::BitAnd,
 +                    hir::BinOpKind::BitOr,
 +                    hir::BinOpKind::BitXor,
 +                    hir::BinOpKind::Shl,
 +                    hir::BinOpKind::Shr,
 +                ],
 +            ) {
 +                span_lint(
 +                    cx,
 +                    SUSPICIOUS_ARITHMETIC_IMPL,
 +                    binop.span,
 +                    &format!("suspicious use of binary operator in `{}` impl", impl_trait),
 +                );
 +            }
 +
 +            if let Some(impl_trait) = check_binop(
 +                cx,
 +                expr,
 +                binop.node,
 +                &[
 +                    "AddAssign",
 +                    "SubAssign",
 +                    "MulAssign",
 +                    "DivAssign",
 +                    "BitAndAssign",
 +                    "BitOrAssign",
 +                    "BitXorAssign",
 +                    "RemAssign",
 +                    "ShlAssign",
 +                    "ShrAssign",
 +                ],
 +                &[
 +                    hir::BinOpKind::Add,
 +                    hir::BinOpKind::Sub,
 +                    hir::BinOpKind::Mul,
 +                    hir::BinOpKind::Div,
 +                    hir::BinOpKind::BitAnd,
 +                    hir::BinOpKind::BitOr,
 +                    hir::BinOpKind::BitXor,
 +                    hir::BinOpKind::Rem,
 +                    hir::BinOpKind::Shl,
 +                    hir::BinOpKind::Shr,
 +                ],
 +            ) {
 +                span_lint(
 +                    cx,
 +                    SUSPICIOUS_OP_ASSIGN_IMPL,
 +                    binop.span,
 +                    &format!("suspicious use of binary operator in `{}` impl", impl_trait),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_binop(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    binop: hir::BinOpKind,
 +    traits: &[&'static str],
 +    expected_ops: &[hir::BinOpKind],
 +) -> Option<&'static str> {
 +    let mut trait_ids = vec![];
 +    let [krate, module] = paths::OPS_MODULE;
 +
 +    for &t in traits {
 +        let path = [krate, module, t];
 +        if let Some(trait_id) = get_trait_def_id(cx, &path) {
 +            trait_ids.push(trait_id);
 +        } else {
 +            return None;
 +        }
 +    }
 +
 +    // Get the actually implemented trait
 +    let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
 +
 +    if_chain! {
 +        if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
 +        if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.res.def_id());
 +        if binop != expected_ops[idx];
 +        then{
 +            return Some(traits[idx])
 +        }
 +    }
 +
 +    None
 +}
 +
 +struct BinaryExprVisitor {
 +    nb_binops: u32,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        match expr.kind {
 +            hir::ExprKind::Binary(..)
 +            | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _)
 +            | hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index 1b3c457b01adb64650f9965d352eabedee6d54d9,0000000000000000000000000000000000000000..07a4e294049791f7256e41810c31ed22088a3426
mode 100644,000000..100644
--- /dev/null
@@@ -1,429 -1,0 +1,430 @@@
- use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path, eq_maybe_qself};
 +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 +
-                 TupleStruct(qself2, path2, ps2) if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
++use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{meets_msrv, msrvs, over};
 +use rustc_ast::mut_visit::*;
 +use rustc_ast::ptr::P;
 +use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 +use rustc_ast_pretty::pprust;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::DUMMY_SP;
 +
 +use std::cell::Cell;
 +use std::mem;
 +
 +declare_clippy_lint! {
 +    /// **What it does:**
 +    ///
 +    /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
 +    /// suggests replacing the pattern with a nested one, `Some(0 | 2)`.
 +    ///
 +    /// Another way to think of this is that it rewrites patterns in
 +    /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
 +    ///
 +    /// **Why is this bad?**
 +    ///
 +    /// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// fn main() {
 +    ///     if let Some(0) | Some(2) = Some(0) {}
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #![feature(or_patterns)]
 +    ///
 +    /// fn main() {
 +    ///     if let Some(0 | 2) = Some(0) {}
 +    /// }
 +    /// ```
 +    pub UNNESTED_OR_PATTERNS,
 +    pedantic,
 +    "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 +}
 +
 +#[derive(Clone, Copy)]
 +pub struct UnnestedOrPatterns {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl UnnestedOrPatterns {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
 +
 +impl EarlyLintPass for UnnestedOrPatterns {
 +    fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &a.pat);
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            if let ast::ExprKind::Let(pat, _) = &e.kind {
 +                lint_unnested_or_patterns(cx, pat);
 +            }
 +        }
 +    }
 +
 +    fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &p.pat);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &l.pat);
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
 +    if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
 +        // This is a leaf pattern, so cloning is unprofitable.
 +        return;
 +    }
 +
 +    let mut pat = P(pat.clone());
 +
 +    // Nix all the paren patterns everywhere so that they aren't in our way.
 +    remove_all_parens(&mut pat);
 +
 +    // Transform all unnested or-patterns into nested ones, and if there were none, quit.
 +    if !unnest_or_patterns(&mut pat) {
 +        return;
 +    }
 +
 +    span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| {
 +        insert_necessary_parens(&mut pat);
 +        db.span_suggestion_verbose(
 +            pat.span,
 +            "nest the patterns",
 +            pprust::pat_to_string(&pat),
 +            Applicability::MachineApplicable,
 +        );
 +    });
 +}
 +
 +/// Remove all `(p)` patterns in `pat`.
 +fn remove_all_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            noop_visit_pat(pat, self);
 +            let inner = match &mut pat.kind {
 +                Paren(i) => mem::replace(&mut i.kind, Wild),
 +                _ => return,
 +            };
 +            pat.kind = inner;
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Insert parens where necessary according to Rust's precedence rules for patterns.
 +fn insert_necessary_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            use ast::{BindingMode::*, Mutability::*};
 +            noop_visit_pat(pat, self);
 +            let target = match &mut pat.kind {
 +                // `i @ a | b`, `box a | b`, and `& mut? a | b`.
 +                Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
 +                Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)`
 +                _ => return,
 +            };
 +            target.kind = Paren(P(take_pat(target)));
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
 +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
 +fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
 +    struct Visitor {
 +        changed: bool,
 +    }
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, p: &mut P<Pat>) {
 +            // This is a bottom up transformation, so recurse first.
 +            noop_visit_pat(p, self);
 +
 +            // Don't have an or-pattern? Just quit early on.
 +            let alternatives = match &mut p.kind {
 +                Or(ps) => ps,
 +                _ => return,
 +            };
 +
 +            // Collapse or-patterns directly nested in or-patterns.
 +            let mut idx = 0;
 +            let mut this_level_changed = false;
 +            while idx < alternatives.len() {
 +                let inner = if let Or(ps) = &mut alternatives[idx].kind {
 +                    mem::take(ps)
 +                } else {
 +                    idx += 1;
 +                    continue;
 +                };
 +                this_level_changed = true;
 +                alternatives.splice(idx..=idx, inner);
 +            }
 +
 +            // Focus on `p_n` and then try to transform all `p_i` where `i > n`.
 +            let mut focus_idx = 0;
 +            while focus_idx < alternatives.len() {
 +                this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx);
 +                focus_idx += 1;
 +            }
 +            self.changed |= this_level_changed;
 +
 +            // Deal with `Some(Some(0)) | Some(Some(1))`.
 +            if this_level_changed {
 +                noop_visit_pat(p, self);
 +            }
 +        }
 +    }
 +
 +    let mut visitor = Visitor { changed: false };
 +    visitor.visit_pat(pat);
 +    visitor.changed
 +}
 +
 +/// Match `$scrutinee` against `$pat` and extract `$then` from it.
 +/// Panics if there is no match.
 +macro_rules! always_pat {
 +    ($scrutinee:expr, $pat:pat => $then:expr) => {
 +        match $scrutinee {
 +            $pat => $then,
 +            _ => unreachable!(),
 +        }
 +    };
 +}
 +
 +/// Focus on `focus_idx` in `alternatives`,
 +/// attempting to extend it with elements of the same constructor `C`
 +/// in `alternatives[focus_idx + 1..]`.
 +fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) -> bool {
 +    // Extract the kind; we'll need to make some changes in it.
 +    let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild);
 +    // We'll focus on `alternatives[focus_idx]`,
 +    // so we're draining from `alternatives[focus_idx + 1..]`.
 +    let start = focus_idx + 1;
 +
 +    // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
 +    let changed = match &mut focus_kind {
 +        // These pattern forms are "leafs" and do not have sub-patterns.
 +        // Therefore they are not some form of constructor `C`,
 +        // with which a pattern `C(p_0)` may be formed,
 +        // which we would want to join with other `C(p_j)`s.
 +        Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
 +        // Dealt with elsewhere.
 +        | Or(_) | Paren(_) => false,
 +        // Transform `box x | ... | box y` into `box (x | y)`.
 +        //
 +        // The cases below until `Slice(...)` deal with *singleton* products.
 +        // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`.
 +        Box(target) => extend_with_matching(
 +            target, start, alternatives,
 +            |k| matches!(k, Box(_)),
 +            |k| always_pat!(k, Box(p) => p),
 +        ),
 +        // Transform `&m x | ... | &m y` into `&m (x | y)`.
 +        Ref(target, m1) => extend_with_matching(
 +            target, start, alternatives,
 +            |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
 +            |k| always_pat!(k, Ref(p, _) => p),
 +        ),
 +        // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
 +        Ident(b1, i1, Some(target)) => extend_with_matching(
 +            target, start, alternatives,
 +            // Binding names must match.
 +            |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)),
 +            |k| always_pat!(k, Ident(_, _, Some(p)) => p),
 +        ),
 +        // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`.
 +        Slice(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Slice(ps) => ps),
 +        ),
 +        // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`.
 +        Tuple(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Tuple(ps) => ps),
 +        ),
 +        // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`.
 +        TupleStruct(qself1, path1, ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(
 +                k,
++                TupleStruct(qself2, path2, ps2)
++                    if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
 +            ),
 +            |k| always_pat!(k, TupleStruct(_, _, ps) => ps),
 +        ),
 +        // Transform a record pattern `S { fp_0, ..., fp_n }`.
 +        Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives),
 +    };
 +
 +    alternatives[focus_idx].kind = focus_kind;
 +    changed
 +}
 +
 +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`.
 +/// In particular, for a record pattern, the order in which the field patterns is irrelevant.
 +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
 +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
 +fn extend_with_struct_pat(
 +    qself1: &Option<ast::QSelf>,
 +    path1: &ast::Path,
 +    fps1: &mut Vec<ast::PatField>,
 +    rest1: bool,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +) -> bool {
 +    (0..fps1.len()).any(|idx| {
 +        let pos_in_2 = Cell::new(None); // The element `k`.
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| {
 +                matches!(k, Struct(qself2, path2, fps2, rest2)
 +                if rest1 == *rest2 // If one struct pattern has `..` so must the other.
 +                && eq_maybe_qself(qself1, qself2)
 +                && eq_path(path1, path2)
 +                && fps1.len() == fps2.len()
 +                && fps1.iter().enumerate().all(|(idx_1, fp1)| {
 +                    if idx_1 == idx {
 +                        // In the case of `k`, we merely require identical field names
 +                        // so that we will transform into `ident_k: p1_k | p2_k`.
 +                        let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
 +                        pos_in_2.set(pos);
 +                        pos.is_some()
 +                    } else {
 +                        fps2.iter().any(|fp2| eq_field_pat(fp1, fp2))
 +                    }
 +                }))
 +            },
 +            // Extract `p2_k`.
 +            |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat),
 +        );
 +        extend_with_tail_or(&mut fps1[idx].pat, tail_or)
 +    })
 +}
 +
 +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`.
 +/// Here, the idea is that we fixate on some `p_k` in `C`,
 +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`),
 +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
 +/// where `~` denotes semantic equality.
 +fn extend_with_matching_product(
 +    targets: &mut Vec<P<Pat>>,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
 +    extract: impl Fn(PatKind) -> Vec<P<Pat>>,
 +) -> bool {
 +    (0..targets.len()).any(|idx| {
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| predicate(k, targets, idx),
 +            |k| extract(k).swap_remove(idx),
 +        );
 +        extend_with_tail_or(&mut targets[idx], tail_or)
 +    })
 +}
 +
 +/// Extract the pattern from the given one and replace it with `Wild`.
 +/// This is meant for temporarily swapping out the pattern for manipulation.
 +fn take_pat(from: &mut Pat) -> Pat {
 +    let dummy = Pat {
 +        id: DUMMY_NODE_ID,
 +        kind: Wild,
 +        span: DUMMY_SP,
 +        tokens: None,
 +    };
 +    mem::replace(from, dummy)
 +}
 +
 +/// Extend `target` as an or-pattern with the alternatives
 +/// in `tail_or` if there are any and return if there were.
 +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec<P<Pat>>) -> bool {
 +    fn extend(target: &mut Pat, mut tail_or: Vec<P<Pat>>) {
 +        match target {
 +            // On an existing or-pattern in the target, append to it.
 +            Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
 +            // Otherwise convert the target to an or-pattern.
 +            target => {
 +                let mut init_or = vec![P(take_pat(target))];
 +                init_or.append(&mut tail_or);
 +                target.kind = Or(init_or);
 +            },
 +        }
 +    }
 +
 +    let changed = !tail_or.is_empty();
 +    if changed {
 +        // Extend the target.
 +        extend(target, tail_or);
 +    }
 +    changed
 +}
 +
 +// Extract all inner patterns in `alternatives` matching our `predicate`.
 +// Only elements beginning with `start` are considered for extraction.
 +fn drain_matching(
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> Vec<P<Pat>> {
 +    let mut tail_or = vec![];
 +    let mut idx = 0;
 +    for pat in alternatives.drain_filter(|p| {
 +        // Check if we should extract, but only if `idx >= start`.
 +        idx += 1;
 +        idx > start && predicate(&p.kind)
 +    }) {
 +        tail_or.push(extract(pat.into_inner().kind));
 +    }
 +    tail_or
 +}
 +
 +fn extend_with_matching(
 +    target: &mut Pat,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> bool {
 +    extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract))
 +}
 +
 +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
 +fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
 +    ps1.len() == ps2.len()
 +        && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
 +        && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
 +        && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
 +}
index 254b104bdefb10ef088732d2fbf20c3f4ecefeb8,0000000000000000000000000000000000000000..906ac10f4610b92f0c548f2445064dde896b6a87
mode 100644,000000..100644
--- /dev/null
@@@ -1,470 -1,0 +1,307 @@@
- use clippy_utils::source::snippet_opt;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     def::{self, DefKind},
 +use clippy_utils::ty::same_type_and_consts;
 +use clippy_utils::{in_macro, meets_msrv, msrvs};
 +use if_chain::if_chain;
++use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    self as hir,
-     Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
-     QPath, TyKind,
++    def::{CtorOf, DefKind, Res},
 +    def_id::LocalDefId,
 +    intravisit::{walk_ty, NestedVisitorMap, Visitor},
- use rustc_middle::ty::{AssocKind, Ty};
++    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
- use rustc_span::{BytePos, Span};
++use rustc_middle::ty::AssocKind;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
-         hir_id: HirId,
-         impl_trait_ref_def_id: Option<LocalDefId>,
-         types_to_skip: Vec<HirId>,
-         types_to_lint: Vec<HirId>,
++use rustc_span::Span;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for unnecessary repetition of structure name when a
 +    /// replacement with `Self` is applicable.
 +    ///
 +    /// **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct
 +    /// name
 +    /// feels inconsistent.
 +    ///
 +    /// **Known problems:**
 +    /// - Unaddressed false negative in fn bodies of trait implementations
 +    /// - False positive with assotiated types in traits (#4140)
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// struct Foo {}
 +    /// impl Foo {
 +    ///     fn new() -> Foo {
 +    ///         Foo {}
 +    ///     }
 +    /// }
 +    /// ```
 +    /// could be
 +    /// ```rust
 +    /// struct Foo {}
 +    /// impl Foo {
 +    ///     fn new() -> Self {
 +    ///         Self {}
 +    ///     }
 +    /// }
 +    /// ```
 +    pub USE_SELF,
 +    nursery,
 +    "unnecessary structure name repetition whereas `Self` is applicable"
 +}
 +
 +#[derive(Default)]
 +pub struct UseSelf {
 +    msrv: Option<RustcVersion>,
 +    stack: Vec<StackItem>,
 +}
 +
 +impl UseSelf {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Self::default()
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +enum StackItem {
 +    Check {
-     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        impl_id: LocalDefId,
++        in_body: u32,
++        types_to_skip: FxHashSet<HirId>,
 +    },
 +    NoCheck,
 +}
 +
 +impl_lint_pass!(UseSelf => [USE_SELF]);
 +
 +const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 +
 +impl<'tcx> LateLintPass<'tcx> for UseSelf {
-         //
-         // NB: If you push something on the stack in this method, remember to also pop it in the
-         // `check_item_post` method.
-         match &item.kind {
-             ItemKind::Impl(Impl {
-                 self_ty: hir_self_ty,
-                 of_trait,
-                 ..
-             }) => {
-                 let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind {
-                     let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
-                     parameters.as_ref().map_or(true, |params| {
-                         !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
-                     })
-                 } else {
-                     false
-                 };
-                 let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
-                 if should_check {
-                     self.stack.push(StackItem::Check {
-                         hir_id: hir_self_ty.hir_id,
-                         impl_trait_ref_def_id,
-                         types_to_lint: Vec::new(),
-                         types_to_skip: Vec::new(),
-                     });
-                 } else {
-                     self.stack.push(StackItem::NoCheck);
++    fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
++        if !is_item_interesting(item) {
++            // This does two things:
++            //  1) Reduce needless churn on `self.stack`
++            //  2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
++            //     in order to lint `foo() -> impl <..>`
++            return;
++        }
 +        // We push the self types of `impl`s on a stack here. Only the top type on the stack is
 +        // relevant for linting, since this is the self type of the `impl` we're currently in. To
 +        // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
 +        // we're in an `impl` or nested item, that we don't want to lint
-             },
-             ItemKind::Static(..)
-             | ItemKind::Const(..)
-             | ItemKind::Fn(..)
-             | ItemKind::Enum(..)
-             | ItemKind::Struct(..)
-             | ItemKind::Union(..)
-             | ItemKind::Trait(..) => {
-                 self.stack.push(StackItem::NoCheck);
-             },
-             _ => (),
-         }
++        let stack_item = if_chain! {
++            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
++            if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
++            let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
++            if parameters.as_ref().map_or(true, |params| {
++                !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
++            });
++            then {
++                StackItem::Check {
++                    impl_id: item.def_id,
++                    in_body: 0,
++                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
 +                }
-         use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
-         match item.kind {
-             Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
-                 self.stack.pop();
-             },
-             _ => (),
++            } else {
++                StackItem::NoCheck
++            }
++        };
++        self.stack.push(stack_item);
 +    }
 +
 +    fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
-                 impl_trait_ref_def_id: Some(def_id),
++        if is_item_interesting(item) {
++            self.stack.pop();
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
 +        // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
 +        // declaration. The collection of those types is all this method implementation does.
 +        if_chain! {
 +            if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
 +            if let Some(&mut StackItem::Check {
-             if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
++                impl_id,
 +                ref mut types_to_skip,
 +                ..
 +            }) = self.stack.last_mut();
-     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
++            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
 +            then {
 +                // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
 +                // `Self`.
 +                let self_ty = impl_trait_ref.self_ty();
 +
 +                // `trait_method_sig` is the signature of the function, how it is declared in the
 +                // trait, not in the impl of the trait.
 +                let trait_method = cx
 +                    .tcx
 +                    .associated_items(impl_trait_ref.def_id)
 +                    .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
 +                    .expect("impl method matches a trait method");
 +                let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
 +                let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
 +
 +                // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
 +                // implementation of the trait.
 +                let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
 +                    Some(&**ty)
 +                } else {
 +                    None
 +                };
 +                let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
 +
 +                // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
 +                //
 +                // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
 +                // trait declaration. This is used to check if `Self` was used in the trait
 +                // declaration.
 +                //
 +                // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
 +                // to `Self`), we want to skip linting that type and all subtypes of it. This
 +                // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
 +                // for u8`, when the trait always uses `Vec<u8>`.
 +                //
 +                // See also https://github.com/rust-lang/rust-clippy/issues/2894.
 +                for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
 +                    if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
 +                        let mut visitor = SkipTyCollector::default();
 +                        visitor.visit_ty(impl_hir_ty);
 +                        types_to_skip.extend(visitor.types_to_skip);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
-         //
-         // This method implementation determines which types should get linted in a `Body` and
-         // which shouldn't, with a visitor. We could directly lint in the visitor, but then we
-         // could only allow this lint on item scope. And we would have to check if those types are
-         // already dealt with in `check_ty` anyway.
-         if let Some(StackItem::Check {
-             hir_id,
-             types_to_lint,
-             types_to_skip,
-             ..
-         }) = self.stack.last_mut()
-         {
-             let self_ty = ty_from_hir_id(cx, *hir_id);
-             let mut visitor = LintTyCollector {
-                 cx,
-                 self_ty,
-                 types_to_lint: vec![],
-                 types_to_skip: vec![],
-             };
-             visitor.visit_expr(&body.value);
-             types_to_lint.extend(visitor.types_to_lint);
-             types_to_skip.extend(visitor.types_to_skip);
++    fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 +        // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
 +        // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
 +        // However the `node_type()` method can *only* be called in bodies.
-     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
-         if in_macro(hir_ty.span)
-             || in_impl(cx, hir_ty)
-             || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
-         {
-             return;
++        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
++            *in_body = in_body.saturating_add(1);
 +        }
 +    }
 +
-         let lint_dependend_on_expr_kind = if let Some(StackItem::Check {
-             hir_id,
-             types_to_lint,
-             types_to_skip,
-             ..
-         }) = self.stack.last()
-         {
-             if types_to_skip.contains(&hir_ty.hir_id) {
-                 false
-             } else if types_to_lint.contains(&hir_ty.hir_id) {
-                 true
++    fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
++        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
++            *in_body = in_body.saturating_sub(1);
 +        }
++    }
 +
-                 let self_ty = ty_from_hir_id(cx, *hir_id);
-                 should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
-             }
-         } else {
-             false
-         };
-         if lint_dependend_on_expr_kind {
-             // FIXME: this span manipulation should not be necessary
-             // @flip1995 found an ast lowering issue in
-             // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162
++    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
++        if_chain! {
++            if !in_macro(hir_ty.span);
++            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
++            if let Some(&StackItem::Check {
++                impl_id,
++                in_body,
++                ref types_to_skip,
++            }) = self.stack.last();
++            if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
++            if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
++            if !types_to_skip.contains(&hir_ty.hir_id);
++            let ty = if in_body > 0 {
++                cx.typeck_results().node_type(hir_ty.hir_id)
 +            } else {
-             if !hir.opt_span(id).map_or(false, in_macro) {
-                 match hir.find(id) {
-                     Some(Node::Expr(Expr {
-                         kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
-                         ..
-                     })) => span_lint_until_last_segment(cx, hir_ty.span, segment),
-                     _ => span_lint(cx, hir_ty.span),
-                 }
++                hir_ty_to_ty(cx.tcx, hir_ty)
++            };
++            if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
 +            let hir = cx.tcx.hir();
 +            let id = hir.get_parent_node(hir_ty.hir_id);
-         fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool {
-             let def_id = expr.hir_id.owner;
-             if cx.tcx.has_typeck_results(def_id) {
-                 cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty)
-             } else {
-                 false
-             }
-         }
-         if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
-             return;
++            if !hir.opt_span(id).map_or(false, in_macro);
++            then {
++                span_lint(cx, hir_ty.span);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-         if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() {
-             let self_ty = ty_from_hir_id(cx, *hir_id);
-             match &expr.kind {
-                 ExprKind::Struct(QPath::Resolved(_, path), ..) => {
-                     if expr_ty_matches(cx, expr, self_ty) {
-                         match path.res {
-                             def::Res::SelfTy(..) => (),
-                             def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
-                             _ => {
-                                 span_lint(cx, path.span);
-                             },
-                         }
-                     }
-                 },
-                 // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
-                 ExprKind::Call(fun, _) => {
-                     if let Expr {
-                         kind: ExprKind::Path(ref qpath),
-                         ..
-                     } = fun
-                     {
-                         if expr_ty_matches(cx, expr, self_ty) {
-                             let res = cx.qpath_res(qpath, fun.hir_id);
-                             if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
-                                 match ctor_of {
-                                     def::CtorOf::Variant => {
-                                         span_lint_on_qpath_resolved(cx, qpath, true);
-                                     },
-                                     def::CtorOf::Struct => {
-                                         span_lint_on_qpath_resolved(cx, qpath, false);
-                                     },
-                                 }
-                             }
++        if_chain! {
++            if !in_macro(expr.span);
++            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
++            if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
++            if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
++            then {} else { return; }
 +        }
-                 },
-                 // unit enum variants (`Enum::A`)
-                 ExprKind::Path(qpath) => {
-                     if expr_ty_matches(cx, expr, self_ty) {
-                         span_lint_on_qpath_resolved(cx, qpath, true);
-                     }
-                 },
-                 _ => (),
-             }
++        match expr.kind {
++            ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
++                Res::SelfTy(..) => (),
++                Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
++                _ => span_lint(cx, path.span),
++            },
++            // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
++            ExprKind::Call(fun, _) => {
++                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
++                    if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
++                        match ctor_of {
++                            CtorOf::Variant => lint_path_to_variant(cx, path),
++                            CtorOf::Struct => span_lint(cx, path.span),
 +                        }
 +                    }
- struct LintTyCollector<'a, 'tcx> {
-     cx: &'a LateContext<'tcx>,
-     self_ty: Ty<'tcx>,
-     types_to_lint: Vec<HirId>,
-     types_to_skip: Vec<HirId>,
- }
- impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
-     type Map = Map<'tcx>;
-     fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
-         if_chain! {
-             if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
-             if should_lint_ty(hir_ty, ty, self.self_ty);
-             then {
-                 self.types_to_lint.push(hir_ty.hir_id);
-             } else {
-                 self.types_to_skip.push(hir_ty.hir_id);
-             }
-         }
-         walk_ty(self, hir_ty);
-     }
-     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-         NestedVisitorMap::None
-     }
- }
++                }
++            },
++            // unit enum variants (`Enum::A`)
++            ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
++            _ => (),
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[derive(Default)]
 +struct SkipTyCollector {
 +    types_to_skip: Vec<HirId>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for SkipTyCollector {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
 +        self.types_to_skip.push(hir_ty.hir_id);
 +
 +        walk_ty(self, hir_ty);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
- #[allow(clippy::cast_possible_truncation)]
- fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
-     let sp = span.with_hi(segment.ident.span.lo());
-     // remove the trailing ::
-     let span_without_last_segment = match snippet_opt(cx, sp) {
-         Some(snippet) => match snippet.rfind("::") {
-             Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
-             None => sp,
-         },
-         None => sp,
-     };
-     span_lint(cx, span_without_last_segment);
- }
- fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
-     if path.segments.len() > 1 {
-         span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
-     }
- }
- fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) {
-     if let QPath::Resolved(_, path) = qpath {
-         if until_last_segment {
-             span_lint_on_path_until_last_segment(cx, path);
-         } else {
-             span_lint(cx, path.span);
-         }
-     }
- }
- fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
-     if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
-         hir_ty_to_ty(cx.tcx, hir_ty)
-     } else {
-         unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
-     }
- }
- fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
-     let map = cx.tcx.hir();
-     let parent = map.get_parent_node(hir_ty.hir_id);
-     if_chain! {
-         if let Some(Node::Item(item)) = map.find(parent);
-         if let ItemKind::Impl { .. } = item.kind;
-         then {
-             true
-         } else {
-             false
-         }
 +fn span_lint(cx: &LateContext<'_>, span: Span) {
 +    span_lint_and_sugg(
 +        cx,
 +        USE_SELF,
 +        span,
 +        "unnecessary structure name repetition",
 +        "use the applicable keyword",
 +        "Self".to_owned(),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
- fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
-     if_chain! {
-         if same_type_and_consts(ty, self_ty);
-         if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
-         then {
-             !matches!(path.res, def::Res::SelfTy(..))
-         } else {
-             false
-         }
-     }
++fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
++    if let [.., self_seg, _variant] = path.segments {
++        let span = path
++            .span
++            .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
++        span_lint(cx, span);
 +    }
 +}
 +
++fn is_item_interesting(item: &Item<'_>) -> bool {
++    use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
++    matches!(
++        item.kind,
++        Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
++    )
 +}
index 2be99fb761b18be18a119e8cfc48a8e1a4ca2831,0000000000000000000000000000000000000000..c97f7e1626e76291df3ecea23dfd579116950dc0
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,188 @@@
-                     if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into";
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 +use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
 +use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls
 +    /// which uselessly convert to the same type.
 +    ///
 +    /// **Why is this bad?** Redundant code.
 +    ///
 +    /// **Known problems:** None.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust
 +    /// // Bad
 +    /// // format!() returns a `String`
 +    /// let s: String = format!("hello").into();
 +    ///
 +    /// // Good
 +    /// let s: String = format!("hello");
 +    /// ```
 +    pub USELESS_CONVERSION,
 +    complexity,
 +    "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
 +}
 +
 +#[derive(Default)]
 +pub struct UselessConversion {
 +    try_desugar_arm: Vec<HirId>,
 +}
 +
 +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
 +
 +#[allow(clippy::too_many_lines)]
 +impl<'tcx> LateLintPass<'tcx> for UselessConversion {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +
 +        if Some(&e.hir_id) == self.try_desugar_arm.last() {
 +            return;
 +        }
 +
 +        match e.kind {
 +            ExprKind::Match(_, arms, MatchSource::TryDesugar) => {
 +                let e = match arms[0].body.kind {
 +                    ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e,
 +                    _ => return,
 +                };
 +                if let ExprKind::Call(_, args) = e.kind {
 +                    self.try_desugar_arm.push(args[0].hir_id);
 +                }
 +            },
 +
 +            ExprKind::MethodCall(name, .., args, _) => {
 +                if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" {
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(&args[0]);
 +                    if same_type_and_consts(a, b) {
 +                        let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
 +                        span_lint_and_sugg(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{}`", b),
 +                            "consider removing `.into()`",
 +                            sugg,
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                }
 +                if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter {
 +                    if let Some(parent_expr) = get_parent_expr(cx, e) {
 +                        if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind {
 +                            if parent_name.ident.name != sym::into_iter {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(&args[0]);
 +                    if same_type_and_consts(a, b) {
 +                        let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
 +                        span_lint_and_sugg(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{}`", b),
 +                            "consider removing `.into_iter()`",
 +                            sugg,
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    }
 +                }
 +                if_chain! {
++                    if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && name.ident.name == sym::try_into;
 +                    let a = cx.typeck_results().expr_ty(e);
 +                    let b = cx.typeck_results().expr_ty(&args[0]);
 +                    if is_type_diagnostic_item(cx, a, sym::result_type);
 +                    if let ty::Adt(_, substs) = a.kind();
 +                    if let Some(a_type) = substs.types().next();
 +                    if same_type_and_consts(a_type, b);
 +
 +                    then {
 +                        span_lint_and_help(
 +                            cx,
 +                            USELESS_CONVERSION,
 +                            e.span,
 +                            &format!("useless conversion to the same type: `{}`", b),
 +                            None,
 +                            "consider removing `.try_into()`",
 +                        );
 +                    }
 +                }
 +            },
 +
 +            ExprKind::Call(path, args) => {
 +                if_chain! {
 +                    if args.len() == 1;
 +                    if let ExprKind::Path(ref qpath) = path.kind;
 +                    if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
 +                    then {
 +                        let a = cx.typeck_results().expr_ty(e);
 +                        let b = cx.typeck_results().expr_ty(&args[0]);
 +                        if_chain! {
 +                            if match_def_path(cx, def_id, &paths::TRY_FROM);
 +                            if is_type_diagnostic_item(cx, a, sym::result_type);
 +                            if let ty::Adt(_, substs) = a.kind();
 +                            if let Some(a_type) = substs.types().next();
 +                            if same_type_and_consts(a_type, b);
 +
 +                            then {
 +                                let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
 +                                span_lint_and_help(
 +                                    cx,
 +                                    USELESS_CONVERSION,
 +                                    e.span,
 +                                    &format!("useless conversion to the same type: `{}`", b),
 +                                    None,
 +                                    &hint,
 +                                );
 +                            }
 +                        }
 +
 +                        if_chain! {
 +                            if match_def_path(cx, def_id, &paths::FROM_FROM);
 +                            if same_type_and_consts(a, b);
 +
 +                            then {
 +                                let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par();
 +                                let sugg_msg =
 +                                    format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
 +                                span_lint_and_sugg(
 +                                    cx,
 +                                    USELESS_CONVERSION,
 +                                    e.span,
 +                                    &format!("useless conversion to the same type: `{}`", b),
 +                                    &sugg_msg,
 +                                    sugg.to_string(),
 +                                    Applicability::MachineApplicable, // snippet
 +                                );
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_expr_post(&mut self, _: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if Some(&e.hir_id) == self.try_desugar_arm.last() {
 +            self.try_desugar_arm.pop();
 +        }
 +    }
 +}
index 0e33ae740d946cd00e6c2903d56094a53e9bb441,0000000000000000000000000000000000000000..44d3d4563428d4f4de8a937a1e85263d70a4b558
mode 100644,000000..100644
--- /dev/null
@@@ -1,239 -1,0 +1,257 @@@
-         "iOS", "macOS",
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::{env, fmt, fs, io};
 +
++/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
++#[derive(Clone, Debug, Deserialize)]
++pub struct Rename {
++    pub path: String,
++    pub rename: String,
++}
++
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<String>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![error.to_string()],
 +        }
 +    }
 +}
 +
++/// Note that the configuration parsing currently doesn't support documentation that will
++/// that spans over several lines. This will be possible with the new implementation
++/// See (rust-clippy#7172)
 +macro_rules! define_Conf {
 +    ($(
 +        #[doc = $doc:literal]
 +        $(#[conf_deprecated($dep:literal)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $(#[doc = $doc] pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(e.to_string()),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
 +                                    None => $name = Some(value),
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors })
 +            }
 +        }
 +
 +        #[cfg(feature = "metadata-collector-lint")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                $doc,
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +// N.B., this macro is parsed by util/lintlib.py
 +define_Conf! {
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
 +    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
 +    (cyclomatic_complexity_threshold: Option<u64> = None),
 +    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
 +    (doc_valid_idents: Vec<String> = [
 +        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +        "DirectX",
 +        "ECMAScript",
 +        "GPLv2", "GPLv3",
 +        "GitHub", "GitLab",
 +        "IPv4", "IPv6",
 +        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +        "NaN", "NaNs",
 +        "OAuth", "GraphQL",
 +        "OCaml",
 +        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +        "WebGL",
 +        "TensorFlow",
 +        "TrueType",
-     /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
++        "iOS", "macOS", "FreeBSD",
 +        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +        "MinGW",
 +        "CamelCase",
 +    ].iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
-     /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
++    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
++    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<String> = Vec::new()),
++    /// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
++    (disallowed_types: Vec<String> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
++    /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. <br> A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name.
++    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
++    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename.
++    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
++    /// Lint: RESTRICTED_SCRIPTS. The list of unicode scripts allowed to be used in the scope.
++    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => return Ok(Some(config_file)),
 +                }
 +            }
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
index 46af03663b86be540a341d0a00c3111a3b6d2efc,0000000000000000000000000000000000000000..e877af09e28900d94f5491a828b93bffabed4388
mode 100644,000000..100644
--- /dev/null
@@@ -1,845 -1,0 +1,863 @@@
- const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefor the entire lint is guarded by a feature flag called
 +//! `metadata-collector-lint`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
 +use if_chain::if_chain;
++use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +
 +use crate::utils::internal_lints::is_lint_ref_type;
 +use clippy_utils::{
 +    diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
 +    ty::walk_ptrs_ty_depth,
 +};
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/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. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
-     cx.tcx
-         .hir()
-         .attrs(item.hir_id())
-         .iter()
-         .filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string()))
-         .reduce(|mut acc, sym| {
-             acc.push_str(&sym);
-             acc.push('\n');
-             acc
-         })
++const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
++    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +
 +/// This template will be used to format the configuration section in the lint documentation.
 +/// The `configurations` parameter will be replaced with one or multiple formatted
 +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
 +macro_rules! CONFIGURATION_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +**Configuration**
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +    };
 +}
 +/// This template will be used to format an individual `ClippyConfiguration` instance in the
 +/// lint documentation.
 +///
 +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
 +/// `default`
 +macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 +    () => {
 +        "* {name}: {ty}: {doc} (defaults to `{default}`)\n"
 +    };
 +}
 +
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +
 +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)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        lints
 +            .iter_mut()
 +            .for_each(|x| x.applicability = 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,
 +    level: &'static str,
 +    docs: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl std::fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let 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()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let documentation = doc_comment.split_off(split_pos);
 +
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// **What it does:** Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        docs.push_str(&configuration_section);
 +                    }
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let mut emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info.drain(..) {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
++    let attrs = cx.tcx.hir().attrs(item.hir_id());
++    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
++    let mut docs = String::from(&*lines.next()?.as_str());
++    let mut in_code_block = false;
++    for line in lines {
++        docs.push('\n');
++        let line = line.as_str();
++        let line = &*line;
++        if let Some(info) = line.trim_start().strip_prefix("```") {
++            in_code_block = !in_code_block;
++            if in_code_block {
++                let lang = info
++                    .trim()
++                    .split(',')
++                    // remove rustdoc directives
++                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
++                    // if no language is present, fill in "rust"
++                    .unwrap_or("rust");
++                docs.push_str("```");
++                docs.push_str(lang);
++                continue;
++            }
++        }
++        docs.push_str(line);
++    }
++    Some(docs)
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &'hir Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in &cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 +}
 +
 +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .drain(..)
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type Map = Map<'hir>;
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static, _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type Map = Map<'hir>;
 +
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        // 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 51c1117d20641f92c2161cc1f87b7b7a6a8934fb,0000000000000000000000000000000000000000..520586b3a1f426e093a18dc1a4dafd10739c7e3f
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,210 @@@
- use clippy_utils::in_macro;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-         if is_test_module_or_function(item) {
 +use clippy_utils::source::{snippet, snippet_with_applicability};
++use clippy_utils::{in_macro, is_test_module_or_function};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    def::{DefKind, Res},
 +    Item, ItemKind, PathSegment, UseKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::kw;
 +use rustc_span::{sym, BytePos};
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for `use Enum::*`.
 +    ///
 +    /// **Why is this bad?** It is usually better style to use the prefixed name of
 +    /// an enumeration variant, rather than importing variants.
 +    ///
 +    /// **Known problems:** Old-style enumerations that prefix the variants are
 +    /// still around.
 +    ///
 +    /// **Example:**
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// use std::cmp::Ordering::*;
 +    /// foo(Less);
 +    ///
 +    /// // Good
 +    /// use std::cmp::Ordering;
 +    /// foo(Ordering::Less)
 +    /// ```
 +    pub ENUM_GLOB_USE,
 +    pedantic,
 +    "use items that import all variants of an enum"
 +}
 +
 +declare_clippy_lint! {
 +    /// **What it does:** Checks for wildcard imports `use _::*`.
 +    ///
 +    /// **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if
 +    /// you try to import something through a wildcard, that already has been imported by name from
 +    /// a different source:
 +    ///
 +    /// ```rust,ignore
 +    /// use crate1::foo; // Imports a function named foo
 +    /// use crate2::*; // Has a function named foo
 +    ///
 +    /// foo(); // Calls crate1::foo
 +    /// ```
 +    ///
 +    /// This can lead to confusing error messages at best and to unexpected behavior at worst.
 +    ///
 +    /// **Exceptions:**
 +    ///
 +    /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
 +    /// provide modules named "prelude" specifically designed for wildcard import.
 +    ///
 +    /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
 +    ///
 +    /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
 +    ///
 +    /// **Known problems:** If macros are imported through the wildcard, this macro is not included
 +    /// by the suggestion and has to be added by hand.
 +    ///
 +    /// Applying the suggestion when explicit imports of the things imported with a glob import
 +    /// exist, may result in `unused_imports` warnings.
 +    ///
 +    /// **Example:**
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// use crate1::*;
 +    ///
 +    /// foo();
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// // Good
 +    /// use crate1::foo;
 +    ///
 +    /// foo();
 +    /// ```
 +    pub WILDCARD_IMPORTS,
 +    pedantic,
 +    "lint `use _::*` statements"
 +}
 +
 +#[derive(Default)]
 +pub struct WildcardImports {
 +    warn_on_all: bool,
 +    test_modules_deep: u32,
 +}
 +
 +impl WildcardImports {
 +    pub fn new(warn_on_all: bool) -> Self {
 +        Self {
 +            warn_on_all,
 +            test_modules_deep: 0,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
 +
 +impl LateLintPass<'_> for WildcardImports {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
-     fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
-         if is_test_module_or_function(item) {
++        if is_test_module_or_function(cx.tcx, item) {
 +            self.test_modules_deep = self.test_modules_deep.saturating_add(1);
 +        }
 +        if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
 +            return;
 +        }
 +        if_chain! {
 +            if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
 +            if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
 +            let used_imports = cx.tcx.names_imported_by_glob_use(item.def_id);
 +            if !used_imports.is_empty(); // Already handled by `unused_imports`
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
 +                let (span, braced_glob) = if import_source_snippet.is_empty() {
 +                    // This is a `_::{_, *}` import
 +                    // In this case `use_path.span` is empty and ends directly in front of the `*`,
 +                    // so we need to extend it by one byte.
 +                    (
 +                        use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
 +                        true,
 +                    )
 +                } else {
 +                    // In this case, the `use_path.span` ends right before the `::*`, so we need to
 +                    // extend it up to the `*`. Since it is hard to find the `*` in weird
 +                    // formattings like `use _ ::  *;`, we extend it up to, but not including the
 +                    // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
 +                    // can just use the end of the item span
 +                    let mut span = use_path.span.with_hi(item.span.hi());
 +                    if snippet(cx, span, "").ends_with(';') {
 +                        span = use_path.span.with_hi(item.span.hi() - BytePos(1));
 +                    }
 +                    (
 +                        span, false,
 +                    )
 +                };
 +
 +                let imports_string = if used_imports.len() == 1 {
 +                    used_imports.iter().next().unwrap().to_string()
 +                } else {
 +                    let mut imports = used_imports
 +                        .iter()
 +                        .map(ToString::to_string)
 +                        .collect::<Vec<_>>();
 +                    imports.sort();
 +                    if braced_glob {
 +                        imports.join(", ")
 +                    } else {
 +                        format!("{{{}}}", imports.join(", "))
 +                    }
 +                };
 +
 +                let sugg = if braced_glob {
 +                    imports_string
 +                } else {
 +                    format!("{}::{}", import_source_snippet, imports_string)
 +                };
 +
 +                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
 +                    (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
 +                } else {
 +                    (WILDCARD_IMPORTS, "usage of wildcard import")
 +                };
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    lint,
 +                    span,
 +                    message,
 +                    "try",
 +                    sugg,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
- fn is_test_module_or_function(item: &Item<'_>) -> bool {
-     matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
- }
++    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
++        if is_test_module_or_function(cx.tcx, item) {
 +            self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
 +        }
 +    }
 +}
 +
 +impl WildcardImports {
 +    fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
 +        in_macro(item.span)
 +            || is_prelude_import(segments)
 +            || (is_super_only_import(segments) && self.test_modules_deep > 0)
 +    }
 +}
 +
 +// Allow "...prelude::..::*" imports.
 +// Many crates have a prelude, and it is imported as a glob by design.
 +fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
 +    segments.iter().any(|ps| ps.ident.name == sym::prelude)
 +}
 +
 +// Allow "super::*" imports in tests.
 +fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
 +    segments.len() == 1 && segments[0].ident.name == kw::Super
 +}
index 93ed3b1840068416459a731f682cde21b01472a0,0000000000000000000000000000000000000000..6ede9011208305f5905f25435744c1bf8e735ce2
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,23 @@@
- version = "0.1.54"
 +[package]
 +name = "clippy_utils"
++version = "0.1.55"
 +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]
 +deny-warnings = []
 +internal-lints = []
 +metadata-collector-lint = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 90c034bd02a9e8493b93718f178de160cd60857e,0000000000000000000000000000000000000000..30c2260d15cacdeb05d30416ff5d0c6276c2ecb2
mode 100644,000000..100644
--- /dev/null
@@@ -1,614 -1,0 +1,619 @@@
-         (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
 +//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
 +//!
 +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
 +
 +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 +
 +use crate::{both, over};
 +use if_chain::if_chain;
 +use rustc_ast::ptr::P;
 +use rustc_ast::{self as ast, *};
 +use rustc_span::symbol::Ident;
 +use std::mem;
 +
 +pub mod ident_iter;
 +pub use ident_iter::IdentIter;
 +
 +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
 +    use BinOpKind::*;
 +    matches!(
 +        kind,
 +        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
 +    )
 +}
 +
 +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
 +pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
 +}
 +
 +pub fn eq_id(l: Ident, r: Ident) -> bool {
 +    l.name == r.name
 +}
 +
 +pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
 +    use PatKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_pat(l, r),
 +        (_, Paren(r)) => eq_pat(l, r),
 +        (Wild, Wild) | (Rest, Rest) => true,
 +        (Lit(l), Lit(r)) => eq_expr(l, r),
 +        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
 +        (Range(lf, lt, le), Range(rf, rt, re)) => {
 +            eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
 +        },
 +        (Box(l), Box(r))
 +        | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
 +        | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
 +        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
-             lr == rr && eq_maybe_qself(lqself, rqself) &&eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
++        (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
++            eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
++        },
 +        (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
-         _ => false
++            lr == rr
++                && eq_maybe_qself(lqself, rqself)
++                && eq_path(lp, rp)
++                && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
 +        },
 +        (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
 +    match (l, r) {
 +        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
 +        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
 +            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_pat(&l.pat, &r.pat)
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +}
 +
 +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
 +    l.position == r.position && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
 +    match (l, r) {
 +        (Some(l), Some(r)) => eq_qself(l, r),
 +        (None, None) => true,
++        _ => false,
 +    }
 +}
 +
 +pub fn eq_path(l: &Path, r: &Path) -> bool {
 +    over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
 +}
 +
 +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
 +    eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
 +}
 +
 +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
 +    match (l, r) {
 +        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
 +            over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
 +        },
 +        (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
 +            over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
 +    match (l, r) {
 +        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
 +        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
 +    match (l, r) {
 +        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
 +        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
 +        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
 +    both(l, r, |l, r| eq_expr(l, r))
 +}
 +
 +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
 +    match (l, r) {
 +        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
 +        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
 +    use ExprKind::*;
 +    if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
 +        return false;
 +    }
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_expr(l, r),
 +        (_, Paren(r)) => eq_expr(l, r),
 +        (Err, Err) => true,
 +        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
 +        (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
 +        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
 +        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
 +        (Lit(l), Lit(r)) => l.kind == r.kind,
 +        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
 +        (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
 +        (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
 +        (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
 +        (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
 +            eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
 +        },
 +        (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
 +        (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
 +        (TryBlock(l), TryBlock(r)) => eq_block(l, r),
 +        (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
 +        (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
 +        (Continue(ll), Continue(rl)) => eq_label(ll, rl),
 +        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
 +        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
 +        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
 +        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
 +        (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
 +            lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
 +        },
 +        (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
 +        (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
 +        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (Struct(lse), Struct(rse)) => {
 +            eq_maybe_qself(&lse.qself, &rse.qself)
 +                && eq_path(&lse.path, &rse.path)
 +                && eq_struct_rest(&lse.rest, &rse.rest)
 +                && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_expr(&l.expr, &r.expr)
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +}
 +
 +pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_pat(&l.pat, &r.pat)
 +        && eq_expr(&l.body, &r.body)
 +        && eq_expr_opt(&l.guard, &r.guard)
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +}
 +
 +pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
 +    both(l, r, |l, r| eq_id(l.ident, r.ident))
 +}
 +
 +pub fn eq_block(l: &Block, r: &Block) -> bool {
 +    l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
 +}
 +
 +pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
 +    use StmtKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Local(l), Local(r)) => {
 +            eq_pat(&l.pat, &r.pat)
 +                && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
 +                && eq_expr_opt(&l.init, &r.init)
 +                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        },
 +        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
 +        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
 +        (Empty, Empty) => true,
 +        (MacCall(l), MacCall(r)) => {
 +            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
 +    eq_id(l.ident, r.ident)
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        && eq_vis(&l.vis, &r.vis)
 +        && eq_kind(&l.kind, &r.kind)
 +}
 +
 +pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
 +    use ItemKind::*;
 +    match (l, r) {
 +        (ExternCrate(l), ExternCrate(r)) => l == r,
 +        (Use(l), Use(r)) => eq_use_tree(l, r),
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (Mod(lu, lmk), Mod(ru, rmk)) => {
 +            lu == ru
 +                && match (lmk, rmk) {
 +                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
 +                        linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
 +                    },
 +                    (ModKind::Unloaded, ModKind::Unloaded) => true,
 +                    _ => false,
 +                }
 +        },
 +        (ForeignMod(l), ForeignMod(r)) => {
 +            both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
 +                && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
 +        },
 +        (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, |l, r| eq_generic_bound(l, r))
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (Enum(le, lg), Enum(re, rg)) => {
 +            over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
 +        },
 +        (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
 +            eq_variant_data(lv, rv) && eq_generics(lg, rg)
 +        },
 +        (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => {
 +            la == ra
 +                && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, |l, r| eq_generic_bound(l, r))
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
 +        (
 +            Impl(box ImplKind {
 +                unsafety: lu,
 +                polarity: lp,
 +                defaultness: ld,
 +                constness: lc,
 +                generics: lg,
 +                of_trait: lot,
 +                self_ty: lst,
 +                items: li,
 +            }),
 +            Impl(box ImplKind {
 +                unsafety: ru,
 +                polarity: rp,
 +                defaultness: rd,
 +                constness: rc,
 +                generics: rg,
 +                of_trait: rot,
 +                self_ty: rst,
 +                items: ri,
 +            }),
 +        ) => {
 +            matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
 +                && eq_defaultness(*ld, *rd)
 +                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
 +                && eq_generics(lg, rg)
 +                && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
 +                && eq_ty(lst, rst)
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
 +    use ForeignItemKind::*;
 +    match (l, r) {
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, |l, r| eq_generic_bound(l, r))
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
 +    use AssocItemKind::*;
 +    match (l, r) {
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, |l, r| eq_generic_bound(l, r))
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        && eq_vis(&l.vis, &r.vis)
 +        && eq_id(l.ident, r.ident)
 +        && eq_variant_data(&l.data, &r.data)
 +        && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
 +}
 +
 +pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
 +    use VariantData::*;
 +    match (l, r) {
 +        (Unit(_), Unit(_)) => true,
 +        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        && eq_vis(&l.vis, &r.vis)
 +        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
 +        && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
 +    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
 +}
 +
 +pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
 +    matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
 +        && l.asyncness.is_async() == r.asyncness.is_async()
 +        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
 +        && eq_ext(&l.ext, &r.ext)
 +}
 +
 +pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
 +    over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
 +        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
 +            eq_where_predicate(l, r)
 +        })
 +}
 +
 +pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
 +    use WherePredicate::*;
 +    match (l, r) {
 +        (BoundPredicate(l), BoundPredicate(r)) => {
 +            over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +                eq_generic_param(l, r)
 +            }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
 +                && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
 +        },
 +        (RegionPredicate(l), RegionPredicate(r)) => {
 +            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
 +        },
 +        (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
 +    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
 +}
 +
 +pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
 +    eq_expr(&l.value, &r.value)
 +}
 +
 +pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
 +    use UseTreeKind::*;
 +    match (l, r) {
 +        (Glob, Glob) => true,
 +        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
 +        (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
 +    matches!(
 +        (l, r),
 +        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
 +    )
 +}
 +
 +pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
 +    use VisibilityKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
 +        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
 +    eq_fn_ret_ty(&l.output, &r.output)
 +        && over(&l.inputs, &r.inputs, |l, r| {
 +            l.is_placeholder == r.is_placeholder
 +                && eq_pat(&l.pat, &r.pat)
 +                && eq_ty(&l.ty, &r.ty)
 +                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +        })
 +}
 +
 +pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
 +    match (l, r) {
 +        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
 +        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
 +    use TyKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_ty(l, r),
 +        (_, Paren(r)) => eq_ty(l, r),
 +        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
 +        (Slice(l), Slice(r)) => eq_ty(l, r),
 +        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
 +        (Rptr(ll, l), Rptr(rl, r)) => {
 +            both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
 +        },
 +        (BareFn(l), BareFn(r)) => {
 +            l.unsafety == r.unsafety
 +                && eq_ext(&l.ext, &r.ext)
 +                && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
 +                && eq_fn_decl(&l.decl, &r.decl)
 +        },
 +        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
 +        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
 +        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
 +        (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
 +    use Extern::*;
 +    match (l, r) {
 +        (None, None) | (Implicit, Implicit) => true,
 +        (Explicit(l), Explicit(r)) => eq_str_lit(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
 +    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
 +}
 +
 +pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
 +    eq_path(&l.trait_ref.path, &r.trait_ref.path)
 +        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +            eq_generic_param(l, r)
 +        })
 +}
 +
 +pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
 +    use GenericParamKind::*;
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
 +        && match (&l.kind, &r.kind) {
 +            (Lifetime, Lifetime) => true,
 +            (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
 +            (
 +                Const {
 +                    ty: lt,
 +                    kw_span: _,
 +                    default: ld,
 +                },
 +                Const {
 +                    ty: rt,
 +                    kw_span: _,
 +                    default: rd,
 +                },
 +            ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
 +            _ => false,
 +        }
 +        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
 +}
 +
 +pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
 +    use GenericBound::*;
 +    match (l, r) {
 +        (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
 +        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
 +    use AssocTyConstraintKind::*;
 +    eq_id(l.ident, r.ident)
 +        && match (&l.kind, &r.kind) {
 +            (Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
 +            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
 +    eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
 +}
 +
 +pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
 +    use AttrKind::*;
 +    l.style == r.style
 +        && match (&l.kind, &r.kind) {
 +            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
 +            (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
 +    use MacArgs::*;
 +    match (l, r) {
 +        (Empty, Empty) => true,
 +        (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
 +        (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
 +        _ => false,
 +    }
 +}
 +
 +/// Extract args from an assert-like macro.
 +///
 +/// Currently working with:
 +/// - `assert_eq!` and `assert_ne!`
 +/// - `debug_assert_eq!` and `debug_assert_ne!`
 +///
 +/// For example:
 +///
 +/// `debug_assert_eq!(a, b)` will return Some([a, b])
 +pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
 +    if_chain! {
 +        if let ExprKind::If(_, ref block, _) = expr.kind;
 +        if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
 +        then {
 +            expr = e;
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::Block(ref block, _) = expr.kind;
 +        if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
 +        if let ExprKind::Match(ref match_expr, _) = expr.kind;
 +        if let ExprKind::Tup(ref tup) = match_expr.kind;
 +        if let [a, b, ..] = tup.as_slice();
 +        if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
 +        then {
 +            return Some([&*a, &*b]);
 +        }
 +    }
 +    None
 +}
index 0318c483959f2182a47f996dba7f88f6d584fc04,0000000000000000000000000000000000000000..c19b558cd8c6e044902c2713e8f169fd2113920d
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,164 @@@
 +use rustc_ast::{ast, attr};
 +use rustc_errors::Applicability;
 +use rustc_session::Session;
 +use rustc_span::sym;
 +use std::str::FromStr;
 +
 +/// Deprecation status of attributes known by Clippy.
 +#[allow(dead_code)]
 +pub enum DeprecationStatus {
 +    /// Attribute is deprecated
 +    Deprecated,
 +    /// Attribute is deprecated and was replaced by the named attribute
 +    Replaced(&'static str),
 +    None,
 +}
 +
 +pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
 +    ("author", DeprecationStatus::None),
 +    ("cognitive_complexity", DeprecationStatus::None),
 +    (
 +        "cyclomatic_complexity",
 +        DeprecationStatus::Replaced("cognitive_complexity"),
 +    ),
 +    ("dump", DeprecationStatus::None),
 +    ("msrv", DeprecationStatus::None),
 +];
 +
 +pub struct LimitStack {
 +    stack: Vec<u64>,
 +}
 +
 +impl Drop for LimitStack {
 +    fn drop(&mut self) {
 +        assert_eq!(self.stack.len(), 1);
 +    }
 +}
 +
 +impl LimitStack {
 +    #[must_use]
 +    pub fn new(limit: u64) -> Self {
 +        Self { stack: vec![limit] }
 +    }
 +    pub fn limit(&self) -> u64 {
 +        *self.stack.last().expect("there should always be a value in the stack")
 +    }
 +    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| stack.push(val));
 +    }
 +    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
 +    }
 +}
 +
 +pub fn get_attr<'a>(
 +    sess: &'a Session,
 +    attrs: &'a [ast::Attribute],
 +    name: &'static str,
 +) -> impl Iterator<Item = &'a ast::Attribute> {
 +    attrs.iter().filter(move |attr| {
 +        let attr = if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr
 +        } else {
 +            return false;
 +        };
 +        let attr_segments = &attr.path.segments;
 +        if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
 +            BUILTIN_ATTRIBUTES
 +                .iter()
 +                .find_map(|&(builtin_name, ref deprecation_status)| {
 +                    if attr_segments[1].ident.name.as_str() == builtin_name {
 +                        Some(deprecation_status)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .map_or_else(
 +                    || {
 +                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
 +                        false
 +                    },
 +                    |deprecation_status| {
 +                        let mut diag =
 +                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
 +                        match *deprecation_status {
 +                            DeprecationStatus::Deprecated => {
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::Replaced(new_name) => {
 +                                diag.span_suggestion(
 +                                    attr_segments[1].ident.span,
 +                                    "consider using",
 +                                    new_name.to_string(),
 +                                    Applicability::MachineApplicable,
 +                                );
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::None => {
 +                                diag.cancel();
 +                                attr_segments[1].ident.name.as_str() == name
 +                            },
 +                        }
 +                    },
 +                )
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
 +    for attr in get_attr(sess, attrs, name) {
 +        if let Some(ref value) = attr.value_str() {
 +            if let Ok(value) = FromStr::from_str(&value.as_str()) {
 +                f(value);
 +            } else {
 +                sess.span_err(attr.span, "not a number");
 +            }
 +        } else {
 +            sess.span_err(attr.span, "bad clippy attribute");
 +        }
 +    }
 +}
 +
 +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
 +    let mut unique_attr = None;
 +    for attr in get_attr(sess, attrs, name) {
 +        match attr.style {
 +            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
 +            ast::AttrStyle::Inner => {
 +                sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name))
 +                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
 +                    .emit();
 +            },
 +            ast::AttrStyle::Outer => {
 +                sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
 +            },
 +        }
 +    }
 +    unique_attr
 +}
 +
 +/// Return true if the attributes contain any of `proc_macro`,
 +/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
 +pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
 +    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
 +}
 +
 +/// Return true if the attributes contain `#[doc(hidden)]`
 +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
 +    attrs
 +        .iter()
 +        .filter(|attr| attr.has_name(sym::doc))
 +        .filter_map(ast::Attribute::meta_item_list)
 +        .any(|l| attr::list_contains_name(&l, sym::hidden))
 +}
++
++/// Return true if the attributes contain `#[unstable]`
++pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
++    attrs.iter().any(|attr| attr.has_name(sym::unstable))
++}
index 0d7fdeeb920f2c6897f172cf0f239fbf7d6c207b,0000000000000000000000000000000000000000..15c27d1a996d74e97abfc58d4f9752405b8f727a
mode 100644,000000..100644
--- /dev/null
@@@ -1,575 -1,0 +1,581 @@@
- use crate::{clip, sext, unsext};
 +#![allow(clippy::float_cmp)]
 +
-             ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
++use crate::{clip, is_direct_expn_of, sext, unsext};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitFloatType, LitKind};
 +use rustc_data_structures::sync::Lrc;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::Scalar;
 +use rustc_middle::ty::subst::{Subst, SubstsRef};
 +use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
 +use rustc_middle::{bug, span_bug};
 +use rustc_span::symbol::Symbol;
 +use std::cmp::Ordering::{self, Equal};
 +use std::convert::TryInto;
 +use std::hash::{Hash, Hasher};
 +use std::iter;
 +
 +/// A `LitKind`-like enum to fold constant `Expr`s into.
 +#[derive(Debug, Clone)]
 +pub enum Constant {
 +    /// A `String` (e.g., "abc").
 +    Str(String),
 +    /// A binary string (e.g., `b"abc"`).
 +    Binary(Lrc<[u8]>),
 +    /// A single `char` (e.g., `'a'`).
 +    Char(char),
 +    /// An integer's bit representation.
 +    Int(u128),
 +    /// An `f32`.
 +    F32(f32),
 +    /// An `f64`.
 +    F64(f64),
 +    /// `true` or `false`.
 +    Bool(bool),
 +    /// An array of constants.
 +    Vec(Vec<Constant>),
 +    /// Also an array, but with only one constant, repeated N times.
 +    Repeat(Box<Constant>, u64),
 +    /// A tuple of constants.
 +    Tuple(Vec<Constant>),
 +    /// A raw pointer.
 +    RawPtr(u128),
 +    /// A reference
 +    Ref(Box<Constant>),
 +    /// A literal with syntax error.
 +    Err(Symbol),
 +}
 +
 +impl PartialEq for Constant {
 +    fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
 +            (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
 +            (&Self::Char(l), &Self::Char(r)) => l == r,
 +            (&Self::Int(l), &Self::Int(r)) => l == r,
 +            (&Self::F64(l), &Self::F64(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                l.to_bits() == r.to_bits()
 +            },
 +            (&Self::F32(l), &Self::F32(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                f64::from(l).to_bits() == f64::from(r).to_bits()
 +            },
 +            (&Self::Bool(l), &Self::Bool(r)) => l == r,
 +            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
 +            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
 +            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
 +            // TODO: are there inter-type equalities?
 +            _ => false,
 +        }
 +    }
 +}
 +
 +impl Hash for Constant {
 +    fn hash<H>(&self, state: &mut H)
 +    where
 +        H: Hasher,
 +    {
 +        std::mem::discriminant(self).hash(state);
 +        match *self {
 +            Self::Str(ref s) => {
 +                s.hash(state);
 +            },
 +            Self::Binary(ref b) => {
 +                b.hash(state);
 +            },
 +            Self::Char(c) => {
 +                c.hash(state);
 +            },
 +            Self::Int(i) => {
 +                i.hash(state);
 +            },
 +            Self::F32(f) => {
 +                f64::from(f).to_bits().hash(state);
 +            },
 +            Self::F64(f) => {
 +                f.to_bits().hash(state);
 +            },
 +            Self::Bool(b) => {
 +                b.hash(state);
 +            },
 +            Self::Vec(ref v) | Self::Tuple(ref v) => {
 +                v.hash(state);
 +            },
 +            Self::Repeat(ref c, l) => {
 +                c.hash(state);
 +                l.hash(state);
 +            },
 +            Self::RawPtr(u) => {
 +                u.hash(state);
 +            },
 +            Self::Ref(ref r) => {
 +                r.hash(state);
 +            },
 +            Self::Err(ref s) => {
 +                s.hash(state);
 +            },
 +        }
 +    }
 +}
 +
 +impl Constant {
 +    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
 +        match (left, right) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
 +            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
 +            (&Self::Int(l), &Self::Int(r)) => {
 +                if let ty::Int(int_ty) = *cmp_type.kind() {
 +                    Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
 +                } else {
 +                    Some(l.cmp(&r))
 +                }
 +            },
 +            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
 +            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
 +            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
 +            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r)
 +                .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
 +                .find(|r| r.map_or(true, |o| o != Ordering::Equal))
 +                .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
 +            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
 +                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
 +                    Some(Equal) => Some(ls.cmp(rs)),
 +                    x => x,
 +                }
 +            },
 +            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
 +            // TODO: are there any useful inter-type orderings?
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Parses a `LitKind` to a `Constant`.
 +pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
 +    match *lit {
 +        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
 +        LitKind::Byte(b) => Constant::Int(u128::from(b)),
 +        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
 +        LitKind::Char(c) => Constant::Char(c),
 +        LitKind::Int(n, _) => Constant::Int(n),
 +        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
 +            ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
 +            ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
 +        },
 +        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
 +            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
 +            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
 +            _ => bug!(),
 +        },
 +        LitKind::Bool(b) => Constant::Bool(b),
 +        LitKind::Err(s) => Constant::Err(s),
 +    }
 +}
 +
 +pub fn constant<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<(Constant, bool)> {
 +    let mut cx = ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    };
 +    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
 +}
 +
 +pub fn constant_simple<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<Constant> {
 +    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
 +}
 +
 +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
 +pub fn constant_context<'a, 'tcx>(
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +) -> ConstEvalLateContext<'a, 'tcx> {
 +    ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    }
 +}
 +
 +pub struct ConstEvalLateContext<'a, 'tcx> {
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    needed_resolution: bool,
 +    substs: SubstsRef<'tcx>,
 +}
 +
 +impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 +    /// Simple constant folding: Insert an expression, get a constant or none.
 +    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
 +        match e.kind {
 +            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
 +            ExprKind::Block(block, _) => self.block(block),
++            ExprKind::Lit(ref lit) => {
++                if is_direct_expn_of(e.span, "cfg").is_some() {
++                    None
++                } else {
++                    Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
++                }
++            },
 +            ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
 +            ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
 +            ExprKind::Repeat(value, _) => {
 +                let n = match self.typeck_results.expr_ty(e).kind() {
 +                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
 +                    _ => span_bug!(e.span, "typeck error"),
 +                };
 +                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
 +            },
 +            ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
 +                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
 +            }),
 +            ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
 +            ExprKind::Binary(op, left, right) => self.binop(op, left, right),
 +            ExprKind::Call(callee, args) => {
 +                // We only handle a few const functions for now.
 +                if_chain! {
 +                    if args.is_empty();
 +                    if let ExprKind::Path(qpath) = &callee.kind;
 +                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
 +                    if let Some(def_id) = res.opt_def_id();
 +                    let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
 +                    let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
 +                    if let ["core", "num", int_impl, "max_value"] = *def_path;
 +                    then {
 +                       let value = match int_impl {
 +                           "<impl i8>" => i8::MAX as u128,
 +                           "<impl i16>" => i16::MAX as u128,
 +                           "<impl i32>" => i32::MAX as u128,
 +                           "<impl i64>" => i64::MAX as u128,
 +                           "<impl i128>" => i128::MAX as u128,
 +                           _ => return None,
 +                       };
 +                       Some(Constant::Int(value))
 +                    }
 +                    else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Index(arr, index) => self.index(arr, index),
 +            ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
 +            // TODO: add other expressions.
 +            _ => None,
 +        }
 +    }
 +
 +    #[allow(clippy::cast_possible_wrap)]
 +    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Bool, Int};
 +        match *o {
 +            Bool(b) => Some(Bool(!b)),
 +            Int(value) => {
 +                let value = !value;
 +                match *ty.kind() {
 +                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
 +                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
 +                    _ => None,
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Int, F32, F64};
 +        match *o {
 +            Int(value) => {
 +                let ity = match *ty.kind() {
 +                    ty::Int(ity) => ity,
 +                    _ => return None,
 +                };
 +                // sign extend
 +                let value = sext(self.lcx.tcx, value, ity);
 +                let value = value.checked_neg()?;
 +                // clear unused bits
 +                Some(Int(unsext(self.lcx.tcx, value, ity)))
 +            },
 +            F32(f) => Some(F32(-f)),
 +            F64(f) => Some(F64(-f)),
 +            _ => None,
 +        }
 +    }
 +
 +    /// Create `Some(Vec![..])` of all constants, unless there is any
 +    /// non-constant part.
 +    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
 +        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
 +    }
 +
 +    /// Lookup a possibly constant expression from a `ExprKind::Path`.
 +    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
 +        let res = self.typeck_results.qpath_res(qpath, id);
 +        match res {
 +            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
 +                let substs = self.typeck_results.node_substs(id);
 +                let substs = if self.substs.is_empty() {
 +                    substs
 +                } else {
 +                    substs.subst(self.lcx.tcx, self.substs)
 +                };
 +
 +                let result = self
 +                    .lcx
 +                    .tcx
 +                    .const_eval_resolve(
 +                        self.param_env,
 +                        ty::Unevaluated {
 +                            def: ty::WithOptConstParam::unknown(def_id),
 +                            substs,
 +                            promoted: None,
 +                        },
 +                        None,
 +                    )
 +                    .ok()
 +                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
 +                let result = miri_to_const(result);
 +                if result.is_some() {
 +                    self.needed_resolution = true;
 +                }
 +                result
 +            },
 +            // FIXME: cover all usable cases.
 +            _ => None,
 +        }
 +    }
 +
 +    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
 +        let lhs = self.expr(lhs);
 +        let index = self.expr(index);
 +
 +        match (lhs, index) {
 +            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
 +                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                _ => None,
 +            },
 +            (Some(Constant::Vec(vec)), _) => {
 +                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
 +                    match vec.get(0) {
 +                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                        _ => None,
 +                    }
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    /// A block can only yield a constant if it only has one constant expression.
 +    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
 +        if block.stmts.is_empty() {
 +            block.expr.as_ref().and_then(|b| self.expr(b))
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
 +        if let Some(Constant::Bool(b)) = self.expr(cond) {
 +            if b {
 +                self.expr(&*then)
 +            } else {
 +                otherwise.as_ref().and_then(|expr| self.expr(expr))
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
 +        let l = self.expr(left)?;
 +        let r = self.expr(right);
 +        match (l, r) {
 +            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
 +                ty::Int(ity) => {
 +                    let l = sext(self.lcx.tcx, l, ity);
 +                    let r = sext(self.lcx.tcx, r, ity);
 +                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
 +                    match op.node {
 +                        BinOpKind::Add => l.checked_add(r).map(zext),
 +                        BinOpKind::Sub => l.checked_sub(r).map(zext),
 +                        BinOpKind::Mul => l.checked_mul(r).map(zext),
 +                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
 +                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
 +                        BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
 +                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
 +                        BinOpKind::BitXor => Some(zext(l ^ r)),
 +                        BinOpKind::BitOr => Some(zext(l | r)),
 +                        BinOpKind::BitAnd => Some(zext(l & r)),
 +                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                        _ => None,
 +                    }
 +                },
 +                ty::Uint(_) => match op.node {
 +                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
 +                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
 +                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
 +                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
 +                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
 +                    BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
 +                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
 +                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
 +                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
 +                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
 +                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                    _ => None,
 +                },
 +                _ => None,
 +            },
 +            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F32(l + r)),
 +                BinOpKind::Sub => Some(Constant::F32(l - r)),
 +                BinOpKind::Mul => Some(Constant::F32(l * r)),
 +                BinOpKind::Div => Some(Constant::F32(l / r)),
 +                BinOpKind::Rem => Some(Constant::F32(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F64(l + r)),
 +                BinOpKind::Sub => Some(Constant::F64(l - r)),
 +                BinOpKind::Mul => Some(Constant::F64(l * r)),
 +                BinOpKind::Div => Some(Constant::F64(l / r)),
 +                BinOpKind::Rem => Some(Constant::F64(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (l, r) => match (op.node, l, r) {
 +                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
 +                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
 +                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
 +                    Some(r)
 +                },
 +                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
 +                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
 +                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
 +                _ => None,
 +            },
 +        }
 +    }
 +}
 +
 +pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
 +    use rustc_middle::mir::interpret::ConstValue;
 +    match result.val {
 +        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
 +            match result.ty.kind() {
 +                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
 +                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
 +                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
 +                    int.try_into().expect("invalid f32 bit representation"),
 +                ))),
 +                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
 +                    int.try_into().expect("invalid f64 bit representation"),
 +                ))),
 +                ty::RawPtr(type_and_mut) => {
 +                    if let ty::Uint(_) = type_and_mut.ty.kind() {
 +                        return Some(Constant::RawPtr(int.assert_bits(int.size())));
 +                    }
 +                    None
 +                },
 +                // FIXME: implement other conversions.
 +                _ => None,
 +            }
 +        },
 +        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
 +            ty::Ref(_, tam, _) => match tam.kind() {
 +                ty::Str => String::from_utf8(
 +                    data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
 +                        .to_owned(),
 +                )
 +                .ok()
 +                .map(Constant::Str),
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
 +            ty::Array(sub_type, len) => match sub_type.kind() {
 +                ty::Float(FloatTy::F32) => match miri_to_const(len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
 +                        .to_owned()
 +                        .chunks(4)
 +                        .map(|chunk| {
 +                            Some(Constant::F32(f32::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                ty::Float(FloatTy::F64) => match miri_to_const(len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
 +                        .to_owned()
 +                        .chunks(8)
 +                        .map(|chunk| {
 +                            Some(Constant::F64(f64::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                // FIXME: implement other array type conversions.
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        // FIXME: implement other conversions.
 +        _ => None,
 +    }
 +}
index 769836aaf18ed6c54c3773c5c4e052e8528f96c5,0000000000000000000000000000000000000000..ef4854afc83cedeabf594430b3a2ad0d24ac5990
mode 100644,000000..100644
--- /dev/null
@@@ -1,1685 -1,0 +1,1741 @@@
-     PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 +#![feature(box_patterns)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_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::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +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,
 +    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
- /// 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, path)) = expr.kind {
-         if let [p] = path.segments {
-             return p.ident.name == var;
-         }
-     }
-     false
- }
++    PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 +};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::exports::Export;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::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))
 +}
 +
- /// 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(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
-         _ => None,
-     }
- }
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
 +    match path {
 +        QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
 +        QPath::TypeRelative(_, s) => s.args,
 +        QPath::LangItem(..) => None,
 +    }
 +}
 +
 +pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    get_qpath_generics(path)
 +        .map_or([].as_ref(), |a| a.args)
 +        .iter()
 +        .filter_map(|a| {
 +            if let hir::GenericArg::Type(ty) = a {
 +                Some(ty)
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.get(0),
 +        QPath::TypeRelative(_, seg) => Some(seg),
 +        QPath::LangItem(..) => None,
 +    }
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
 +pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
 +    if let ExprKind::Path(p) = &expr.kind {
 +        cx.qpath_res(p, expr.hir_id)
 +    } else {
 +        Res::Err
 +    }
 +}
 +
 +/// Resolves the path to a `DefId` and checks if it matches the given path.
 +pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
 +    cx.qpath_res(path, hir_id)
 +        .opt_def_id()
 +        .map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +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, 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(path, _, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push(args); // build up `matched` backwards
 +                current = &args[0]; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
 +    Some(def_id) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +    match cx.tcx.hir().find(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
- /// Gets the loop enclosing the given expression, if any.
- pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +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(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
-                 e @ Expr {
-                     kind: ExprKind::Loop(..),
++/// Gets the loop or closure enclosing the given expression, if any.
++pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    let map = tcx.hir();
 +    for (_, node) in map.parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(
++                e
++                @
++                Expr {
++                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
 +                    ..
 +                },
 +            ) => return Some(e),
 +            Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    let map = tcx.hir();
 +    match map.parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let map = tcx.hir();
 +    let mut iter = map.parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((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 {
 +                kind: MacroKind::Bang,
 +                name: mac_name,
 +                proc_macro: _,
 +            } = 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 {
 +            kind: MacroKind::Bang,
 +            name: mac_name,
 +            proc_macro: _,
 +        } = data.kind
 +        {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: 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(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats.iter().map(|pat| &**pat))
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
 +        },
 +        PatKind::Slice(head, ref middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => {
 +                    are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).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(block, ..) = expr.kind {
 +        match (block.stmts.is_empty(), block.expr.as_ref()) {
 +            (true, Some(e)) => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind;
 +        if let Res::SelfTy(..) = path.res;
 +        then {
 +            return true
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
 +            if is_lang_ctor(cx, path, ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_lang_ctor(cx, path, ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if let MatchSource::TryDesugar = *source {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
 +                (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context
 +///
 +/// Useful for skipping long running code when it's unnecessary
 +pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[allow(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[allow(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if is_automatically_derived(map.attrs(enclosing_node)) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.get_parent_item(enclosing_node);
 +    }
 +    false
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args)
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +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(cond, then_expr, ref else_expr) = expr.kind {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then_expr.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = *else_expr {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[Expr {
 +            kind: ExprKind::Closure(_, _, body, _, _),
 +            ..
 +        }],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// Finds the `#[must_use]` attribute, if any
 +pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
 +    attrs.iter().find(|a| a.has_name(sym::must_use))
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
 +}
 +
++/// Checks if an expression represents the identity function
++/// Only examines closures and `std::convert::identity`
++pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
++    /// * `|x| x`
++    /// * `|x| return x`
++    /// * `|x| { return x }`
++    /// * `|x| { return x; }`
++    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
++        let id = if_chain! {
++            if let [param] = func.params;
++            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
++            then {
++                id
++            } else {
++                return false;
++            }
++        };
++
++        let mut expr = &func.value;
++        loop {
++            match expr.kind {
++                #[rustfmt::skip]
++                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
++                | ExprKind::Ret(Some(e)) => expr = e,
++                #[rustfmt::skip]
++                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
++                    if_chain! {
++                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
++                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
++                        then {
++                            expr = ret_val;
++                        } else {
++                            return false;
++                        }
++                    }
++                },
++                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
++            }
++        }
++    }
++
++    match expr.kind {
++        ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
++        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
++        _ => false,
++    }
++}
++
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
 +    let map = tcx.hir();
 +    let mut child_id = expr.hir_id;
 +    let mut iter = map.parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some(Node::Expr(expr)),
 +            },
 +            Some((_, node)) => break Some(node),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some(Node::Stmt(Stmt {
 +            kind: StmtKind::Expr(_)
 +                | StmtKind::Semi(_)
 +                | StmtKind::Local(Local {
 +                    pat: Pat {
 +                        kind: PatKind::Wild,
 +                        ..
 +                    },
 +                    ..
 +                }),
 +            ..
 +        }))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
 +            attr.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust,ignore
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { 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,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(BorrowKind::Ref, _, e) 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)
 +}
 +
++/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
++/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
++pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
++    loop {
++        match expr.kind {
++            ExprKind::AddrOf(_, _, e) => expr = e,
++            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
++            _ => break,
++        }
++    }
++    expr
++}
++
 +#[macro_export]
 +macro_rules! unwrap_cargo_metadata {
 +    ($cx: ident, $lint: ident, $deps: expr) => {{
 +        let mut command = cargo_metadata::MetadataCommand::new();
 +        if !$deps {
 +            command.no_deps();
 +        }
 +
 +        match command.exec() {
 +            Ok(metadata) => metadata,
 +            Err(err) => {
 +                span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
 +                return;
 +            },
 +        }
 +    }};
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
 +        if let Res::Def(_, def_id) = path.res;
 +        then {
 +            cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
 +        } else {
 +            false
 +        }
 +    }
 +}
++
++/// Checks whether item either has `test` attribute applied, or
++/// is a module with `test` in its name.
++pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
++    if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
++        if tcx.has_attr(def_id.to_def_id(), sym::test) {
++            return true;
++        }
++    }
++
++    matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
++}
index 791688cd194a997a75461edc103998525e434df4,0000000000000000000000000000000000000000..8adb691595213fd80cd036f1aa50fa20cadd97a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,83 -1,0 +1,79 @@@
- use crate::{get_pat_name, match_var};
 +use crate::source::snippet;
- use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
++use crate::{path_to_local_id, strip_pat_refs};
 +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
- use rustc_span::{Span, Symbol};
++use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
-         get_binding_name(&body.params[idx]).map_or_else(
-             || Some(vec![]),
-             |name| extract_clone_suggestions(cx, name, replacements, body),
-         )
++use rustc_span::Span;
 +use std::borrow::Cow;
 +
 +pub fn get_spans(
 +    cx: &LateContext<'_>,
 +    opt_body_id: Option<BodyId>,
 +    idx: usize,
 +    replacements: &[(&'static str, &'static str)],
 +) -> Option<Vec<(Span, Cow<'static, str>)>> {
 +    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
-     name: Symbol,
++        if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
++            extract_clone_suggestions(cx, binding_id, replacements, body)
++        } else {
++            Some(vec![])
++        }
 +    } else {
 +        Some(vec![])
 +    }
 +}
 +
 +fn extract_clone_suggestions<'tcx>(
 +    cx: &LateContext<'tcx>,
-         name,
++    id: HirId,
 +    replace: &[(&'static str, &'static str)],
 +    body: &'tcx Body<'_>,
 +) -> Option<Vec<(Span, Cow<'static, str>)>> {
 +    let mut visitor = PtrCloneVisitor {
 +        cx,
-     name: Symbol,
++        id,
 +        replace,
 +        spans: vec![],
 +        abort: false,
 +    };
 +    visitor.visit_body(body);
 +    if visitor.abort { None } else { Some(visitor.spans) }
 +}
 +
 +struct PtrCloneVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
-         if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
-             if args.len() == 1 && match_var(&args[0], self.name) {
++    id: HirId,
 +    replace: &'a [(&'static str, &'static str)],
 +    spans: Vec<(Span, Cow<'static, str>)>,
 +    abort: bool,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.abort {
 +            return;
 +        }
-                         self.spans
-                             .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
++        if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
++            if path_to_local_id(recv, self.id) {
 +                if seg.ident.name.as_str() == "capacity" {
 +                    self.abort = true;
 +                    return;
 +                }
 +                for &(fn_name, suffix) in self.replace {
 +                    if seg.ident.name.as_str() == fn_name {
- fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
-     get_pat_name(arg.pat)
- }
++                        self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
 +                        return;
 +                    }
 +                }
 +            }
 +        }
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
index ed3a2fff83f168e7825b1c4e2fce3dee7cf6b820,0000000000000000000000000000000000000000..e98354358af62a10c91ea9992426928b35131323
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,131 @@@
- cargo dev ide_setup
 +# Basics for hacking on Clippy
 +
 +This document explains the basics for hacking on Clippy. Besides others, this
 +includes how to build and test Clippy. For a more in depth description on
 +the codebase take a look at [Adding Lints] or [Common Tools].
 +
 +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
 +
 +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
 +  - [Get the Code](#get-the-code)
 +  - [Building and Testing](#building-and-testing)
 +  - [`cargo dev`](#cargo-dev)
 +  - [lintcheck](#lintcheck)
 +  - [PR](#pr)
 +  - [Common Abbreviations](#common-abbreviations)
 +
 +## Get the Code
 +
 +First, make sure you have checked out the latest version of Clippy. If this is
 +your first time working on Clippy, create a fork of the repository and clone it
 +afterwards with the following command:
 +
 +```bash
 +git clone git@github.com:<your-username>/rust-clippy
 +```
 +
 +If you've already cloned Clippy in the past, update it to the latest version:
 +
 +```bash
 +# If the upstream remote has not been added yet
 +git remote add upstream https://github.com/rust-lang/rust-clippy
 +# upstream has to be the remote of the rust-lang/rust-clippy repo
 +git fetch upstream
 +# make sure that you are on the master branch
 +git checkout master
 +# rebase your master branch on the upstream master
 +git rebase upstream/master
 +# push to the master branch of your fork
 +git push
 +```
 +
 +## Building and Testing
 +
 +You can build and test Clippy like every other Rust project:
 +
 +```bash
 +cargo build  # builds Clippy
 +cargo test   # tests Clippy
 +```
 +
 +Since Clippy's test suite is pretty big, there are some commands that only run a
 +subset of Clippy's tests:
 +
 +```bash
 +# only run UI tests
 +cargo uitest
 +# only run UI tests starting with `test_`
 +TESTNAME="test_" cargo uitest
 +# only run dogfood tests
 +cargo test --test dogfood
 +```
 +
 +If the output of a [UI test] differs from the expected output, you can update the
 +reference file with:
 +
 +```bash
 +cargo dev bless
 +```
 +
 +For example, this is necessary, if you fix a typo in an error message of a lint
 +or if you modify a test file to add a test case.
 +
 +_Note:_ This command may update more files than you intended. In that case only
 +commit the files you wanted to update.
 +
 +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
 +
 +## `cargo dev`
 +
 +Clippy has some dev tools to make working on Clippy more convenient. These tools
 +can be accessed through the `cargo dev` command. Available tools are listed
 +below. To get more information about these commands, just call them with
 +`--help`.
 +
 +```bash
 +# formats the whole Clippy codebase and all tests
 +cargo dev fmt
 +# register or update lint names/groups/...
 +cargo dev update_lints
 +# create a new lint and register it
 +cargo dev new_lint
++# automatically formatting all code before each commit
++cargo dev setup git-hook
 +# (experimental) Setup Clippy to work with IntelliJ-Rust
++cargo dev setup intellij
 +```
 +
 +## lintcheck
 +`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results.  
 +You can `git diff` the updated log against its previous version and
 +see what impact your lint made on a small set of crates.  
 +If you add a new lint, please audit the resulting warnings and make sure
 +there are no false positives and that the suggestions are valid.
 +
 +Refer to the tools [README] for more details.
 +
 +[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md
 +## PR
 +
 +We follow a rustc no merge-commit policy.
 +See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
 +
 +## Common Abbreviations
 +
 +| Abbreviation | Meaning                                |
 +| ------------ | -------------------------------------- |
 +| UB           | Undefined Behavior                     |
 +| FP           | False Positive                         |
 +| FN           | False Negative                         |
 +| ICE          | Internal Compiler Error                |
 +| AST          | Abstract Syntax Tree                   |
 +| MIR          | Mid-Level Intermediate Representation  |
 +| HIR          | High-Level Intermediate Representation |
 +| TCX          | Type context                           |
 +
 +This is a concise list of abbreviations that can come up during Clippy development. An extensive
 +general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if
 +an abbreviation or meaning is unclear to you.
 +
 +[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html
index abac1227b4ff389cee02b348ddf01ad9a5346b78,0000000000000000000000000000000000000000..0a85f65001101c4fa5a172ac462b73d781e5e125
mode 100644,000000..100644
--- /dev/null
@@@ -1,203 -1,0 +1,203 @@@
-   - [Checking if a type defines a method](#checking-if-a-type-defines-a-method)
 +# Common tools for writing lints
 +
 +You may need following tooltips to catch up with common operations.
 +
 +- [Common tools for writing lints](#common-tools-for-writing-lints)
 +  - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
 +  - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
 +  - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
++  - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
 +  - [Dealing with macros](#dealing-with-macros)
 +
 +Useful Rustc dev guide links:
 +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
 +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 +
 +# Retrieving the type of an expression
 +
 +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
 +
 +- which type does this expression correspond to (using its [`TyKind`][TyKind])?
 +- is it a sized type?
 +- is it a primitive type?
 +- does it implement a trait?
 +
 +This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
 +that gives you access to the underlying structure [`TyS`][TyS].
 +
 +Example of use:
 +```rust
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // Get type of `expr`
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        // Match its kind to enter its type
 +        match ty.kind {
 +            ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
 +            _ => ()
 +        }
 +    }
 +}
 +```
 +
 +Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method
 +to retrieve a type from a pattern.
 +
 +Two noticeable items here:
 +- `cx` is the lint context [`LateContext`][LateContext]. The two most useful
 +  data structures in this context are `tcx` and the `TypeckResults` returned by
 +  `LateContext::typeck_results`, allowing us to jump to type definitions and
 +  other compilation stages such as HIR.
 +- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is
 +  created by type checking step, it includes useful information such as types
 +  of expressions, ways to resolve methods and so on.
 +
 +# Checking if an expr is calling a specific method
 +
 +Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 +
 +```rust
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            // Check our expr is calling a method
 +            if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
 +            // Check the name of this method is `some_method`
 +            if path.ident.name == sym!(some_method);
 +            then {
 +                // ...
 +            }
 +        }
 +    }
 +}
 +```
 +
 +# Checking if a type implements a specific trait
 +
 +There are two ways to do this, depending if the target trait is part of lang items.
 +
 +```rust
 +use clippy_utils::{implements_trait, match_trait_method, paths};
 +
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // 1. Using expression and Clippy's convenient method
 +        // we use `match_trait_method` function from Clippy's toolbox
 +        if match_trait_method(cx, expr, &paths::INTO) {
 +            // `expr` implements `Into` trait
 +        }
 +
 +        // 2. Using type context `TyCtxt`
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        if cx.tcx.lang_items()
 +            // we are looking for the `DefId` of `Drop` trait in lang items
 +            .drop_trait()
 +            // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
 +                // `expr` implements `Drop` trait
 +            }
 +    }
 +}
 +```
 +
 +> Prefer using lang items, if the target trait is available there.
 +
 +A list of defined paths for Clippy can be found in [paths.rs][paths]
 +
 +We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
 +
 +# Checking if a type defines a specific method
 +
 +To check if our type defines a method called `some_method`:
 +
 +```rust
 +use clippy_utils::{is_type_diagnostic_item, return_ty};
 +
 +impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        if_chain! {
 +            // Check if item is a method/function
 +            if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
 +            // Check the method is named `some_method`
 +            if impl_item.ident.name == sym!(some_method);
 +            // We can also check it has a parameter `self`
 +            if signature.decl.implicit_self.has_implicit_self();
 +            // We can go further and even check if its return type is `String`
 +            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
 +            then {
 +                // ...
 +            }
 +        }
 +    }
 +}
 +```
 +
 +# Dealing with macros
 +
 +There are several helpers in [`clippy_utils`][utils] to deal with macros:
 +
 +- `in_macro()`: detect if the given span is expanded by a macro
 +
 +You may want to use this for example to not start linting in any macro.
 +
 +```rust
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            "bar" => println!("whatever"),
 +            _ => ()
 +        }
 +    };
 +}
 +
 +foo!("bar");
 +
 +// if we lint the `match` of `foo` call and test its span
 +assert_eq!(in_macro(match_span), true);
 +```
 +
 +- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
 +
 +You may want to use it for example to not start linting in macros from other crates
 +
 +```rust
 +#[macro_use]
 +extern crate a_crate_with_macros;
 +
 +// `foo` is defined in `a_crate_with_macros`
 +foo!("bar");
 +
 +// if we lint the `match` of `foo` call and test its span
 +assert_eq!(in_external_macro(cx.sess(), match_span), true);
 +```
 +
 +- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
 +
 +```rust
 +macro_rules! m {
 +    ($a:expr, $b:expr) => {
 +        if $a.is_some() {
 +            $b;
 +        }
 +    }
 +}
 +
 +let x: Option<u32> = Some(42);
 +m!(x, x.unwrap());
 +
 +// These spans are not from the same context
 +// x.is_some() is from inside the macro
 +// x.unwrap() is from outside the macro
 +assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
 +```
 +
 +[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
 +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
 +[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
 +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
 +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
 +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
 +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
 +[paths]: ../clippy_utils/src/paths.rs
 +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
index e0af9bf0625721a82c59fc63bb000bcd34a253f5,0000000000000000000000000000000000000000..afe3033c288cffd8d95b06ea2e833989455cb81a
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,124 @@@
 +# Release a new Clippy Version
 +
 +_NOTE: This document is probably only relevant to you, if you're a member of the
 +Clippy team._
 +
 +Clippy is released together with stable Rust releases. The dates for these
 +releases can be found at the [Rust Forge]. This document explains the necessary
 +steps to create a Clippy release.
 +
 +1. [Remerge the `beta` branch](#remerge-the-beta-branch)
 +2. [Update the `beta` branch](#update-the-beta-branch)
 +3. [Find the Clippy commit](#find-the-clippy-commit)
 +4. [Tag the stable commit](#tag-the-stable-commit)
 +5. [Update `CHANGELOG.md`](#update-changelogmd)
 +
 +_NOTE: This document is for stable Rust releases, not for point releases. For
 +point releases, step 1. and 2. should be enough._
 +
 +[Rust Forge]: https://forge.rust-lang.org/
 +
 +
 +## Remerge the `beta` branch
 +
 +This step is only necessary, if since the last release something was backported
 +to the beta Rust release. The remerge is then necessary, to make sure that the
 +Clippy commit, that was used by the now stable Rust release, persists in the
 +tree of the Clippy repository.
 +
 +To find out if this step is necessary run
 +
 +```bash
 +# Assumes that the local master branch is up-to-date
 +$ git fetch upstream
 +$ git branch master --contains upstream/beta
 +```
 +
 +If this command outputs `master`, this step is **not** necessary.
 +
 +```bash
 +# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy
 +$ git checkout -b backport_remerge
 +$ git merge upstream/beta
 +$ git diff  # This diff has to be empty, otherwise something with the remerge failed
 +$ git push origin backport_remerge  # This can be pushed to your fork
 +```
 +
 +After this, open a PR to the master branch. In this PR, the commit hash of the
 +`HEAD` of the `beta` branch must exists. In addition to that, no files should
 +be changed by this PR.
 +
 +
 +## Update the `beta` branch
 +
 +This step must be done **after** the PR of the previous step was merged.
 +
 +First, the Clippy commit of the `beta` branch of the Rust repository has to be
 +determined.
 +
 +```bash
 +# Assuming the current directory corresponds to the Rust repository
 +$ git checkout beta
 +$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
 +```
 +
 +After finding the Clippy commit, the `beta` branch in the Clippy repository can
 +be updated.
 +
 +```bash
 +# Assuming the current directory corresponds to the Clippy repository
 +$ git checkout beta
 +$ git reset --hard $BETA_SHA
 +$ git push upstream beta
 +```
 +
 +
 +## Find the Clippy commit
 +
 +The first step is to tag the Clippy commit, that is included in the stable Rust
 +release. This commit can be found in the Rust repository.
 +
 +```bash
 +# Assuming the current directory corresponds to the Rust repository
 +$ git fetch upstream    # `upstream` is the `rust-lang/rust` remote
 +$ git checkout 1.XX.0   # XX should be exchanged with the corresponding version
 +$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
 +```
 +
 +
 +## Tag the stable commit
 +
 +After finding the Clippy commit, it can be tagged with the release number.
 +
 +```bash
 +# Assuming the current directory corresponds to the Clippy repository
 +$ git checkout $SHA
 +$ git tag rust-1.XX.0               # XX should be exchanged with the corresponding version
 +$ git push upstream rust-1.XX.0     # `upstream` is the `rust-lang/rust-clippy` remote
 +```
 +
 +After this, the release should be available on the Clippy [release page].
 +
 +[release page]: https://github.com/rust-lang/rust-clippy/releases
 +
++## Update the `stable` branch
++
++At this step you should have already checked out the commit of the `rust-1.XX.0`
++tag. Updating the stable branch from here is as easy as:
++
++```bash
++# Assuming the current directory corresponds to the Clippy repository and the
++# commit of the just created rust-1.XX.0 tag is checked out.
++$ git push upstream rust-1.XX.0:stable  # `upstream` is the `rust-lang/rust-clippy` remote
++```
++
++_NOTE: Usually there are no stable backports for Clippy, so this update should
++be possible without force pushing or anything like this. If there should have
++happened a stable backport, make sure to re-merge those changes just as with the
++`beta` branch._
 +
 +## Update `CHANGELOG.md`
 +
 +For this see the document on [how to update the changelog].
 +
 +[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
index a61070d8a80efc11fb1c3aae004b5c14d1fc28b8,0000000000000000000000000000000000000000..8c169506e533d7b7892ff558924ca46c784529ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `-Zunstable-options --fix` and
 +## `cargo lintcheck`
 +
 +Runs clippy on a fixed set of crates read from
 +`lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the
 +repo.  We can then check the diff and spot new or disappearing warnings.
 +
 +From the repo root, run:
 +
 +```
 +cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml
 +```
 +
 +or
 +
 +```
 +cargo lintcheck
 +```
 +
 +By default the logs will be saved into
 +`lintcheck-logs/lintcheck_crates_logs.txt`.
 +
 +You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
 +`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from
 +the repo root.
 +
 +The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 +
 +### Configuring the Crate Sources
 +
 +The sources to check are saved in a `toml` file. There are three types of
 +sources.
 +
 +1. Crates-io Source
 +
 +   ```toml
 +   bitflags = {name = "bitflags", versions = ['1.2.1']}
 +   ```
 +   Requires a "name" and one or multiple "versions" to be checked.
 +
 +2. `git` Source
 +   ````toml
 +   puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
 +   ````
 +   Requires a name, the url to the repo and unique identifier of a commit,
 +   branch or tag which is checked out before linting.  There is no way to always
 +   check `HEAD` because that would lead to changing lint-results as the repo
 +   would get updated.  If `git_url` or `git_hash` is missing, an error will be
 +   thrown.
 +
 +3. Local Dependency
 +   ```toml
 +   clippy = {name = "clippy", path = "/home/user/clippy"}
 +   ```
 +   For when you want to add a repository that is not published yet.
 +
 +#### Command Line Options (optional)
 +
 +```toml
 +bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
 +```
 +
 +It is possible to specify command line options for each crate. This makes it
 +possible to only check a crate for certain lint groups. If no options are
 +specified, the lint groups `clippy::all`, `clippy::pedantic`, and
 +`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
 +is checked.
 +
 +**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
 +is explicitly specified in the options.
 +
 +### Fix mode
++You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
 +print a warning if Clippys suggestions fail to apply (if the resulting code does not build).  
 +This lets us spot bad suggestions or false positives automatically in some cases.  
 +
 +Please note that the target dir should be cleaned afterwards since clippy will modify
 +the downloaded sources which can lead to unexpected results when running lintcheck again afterwards.
index f6a75595c986b1294e21bf8236eafd90cd97237e,0000000000000000000000000000000000000000..7d2f3173fb0e7cd4d5d475decac18787b14f60b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,932 -1,0 +1,925 @@@
-             vec![
-                 "-Zunstable-options",
-                 "--fix",
-                 "-Zunstable-options",
-                 "--allow-no-vcs",
-                 "--",
-                 "--cap-lints=warn",
-             ]
 +// Run clippy on a fixed set of crates and collect the warnings.
 +// This helps observing the impact clippy changes have on a set of real-world code (and not just our
 +// testsuite).
 +//
 +// When a new lint is introduced, we can search the results for new warnings and check for false
 +// positives.
 +
 +#![allow(clippy::collapsible_else_if)]
 +
 +use std::ffi::OsStr;
 +use std::process::Command;
 +use std::sync::atomic::{AtomicUsize, Ordering};
 +use std::{collections::HashMap, io::ErrorKind};
 +use std::{
 +    env, fmt,
 +    fs::write,
 +    path::{Path, PathBuf},
 +};
 +
 +use clap::{App, Arg, ArgMatches};
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use serde_json::Value;
 +
 +#[cfg(not(windows))]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
 +#[cfg(not(windows))]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 +
 +#[cfg(windows)]
 +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
 +#[cfg(windows)]
 +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
 +
 +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 +
 +/// List of sources to check, loaded from a .toml file
 +#[derive(Debug, Serialize, Deserialize)]
 +struct SourceList {
 +    crates: HashMap<String, TomlCrate>,
 +}
 +
 +/// A crate source stored inside the .toml
 +/// will be translated into on one of the `CrateSource` variants
 +#[derive(Debug, Serialize, Deserialize)]
 +struct TomlCrate {
 +    name: String,
 +    versions: Option<Vec<String>>,
 +    git_url: Option<String>,
 +    git_hash: Option<String>,
 +    path: Option<String>,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
 +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
 +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 +enum CrateSource {
 +    CratesIo {
 +        name: String,
 +        version: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Git {
 +        name: String,
 +        url: String,
 +        commit: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Path {
 +        name: String,
 +        path: PathBuf,
 +        options: Option<Vec<String>>,
 +    },
 +}
 +
 +/// Represents the actual source code of a crate that we ran "cargo clippy" on
 +#[derive(Debug)]
 +struct Crate {
 +    version: String,
 +    name: String,
 +    // path to the extracted sources that clippy can check
 +    path: PathBuf,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// A single warning that clippy issued while checking a `Crate`
 +#[derive(Debug)]
 +struct ClippyWarning {
 +    crate_name: String,
 +    crate_version: String,
 +    file: String,
 +    line: String,
 +    column: String,
 +    linttype: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +impl std::fmt::Display for ClippyWarning {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        writeln!(
 +            f,
 +            r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#,
 +            &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
 +        )
 +    }
 +}
 +
 +impl CrateSource {
 +    /// Makes the sources available on the disk for clippy to check.
 +    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
 +    /// copies a local folder
 +    fn download_and_extract(&self) -> Crate {
 +        match self {
 +            CrateSource::CratesIo { name, version, options } => {
 +                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
 +                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
 +
 +                // url to download the crate from crates.io
 +                let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
 +                println!("Downloading and extracting {} {} from {}", name, version, url);
 +                create_dirs(&krate_download_dir, &extract_dir);
 +
 +                let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
 +                // don't download/extract if we already have done so
 +                if !krate_file_path.is_file() {
 +                    // create a file path to download and write the crate data into
 +                    let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
 +                    let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
 +                    // copy the crate into the file
 +                    std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
 +
 +                    // unzip the tarball
 +                    let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
 +                    // extract the tar archive
 +                    let mut archive = tar::Archive::new(ungz_tar);
 +                    archive.unpack(&extract_dir).expect("Failed to extract!");
 +                }
 +                // crate is extracted, return a new Krate object which contains the path to the extracted
 +                // sources that clippy can check
 +                Crate {
 +                    version: version.clone(),
 +                    name: name.clone(),
 +                    path: extract_dir.join(format!("{}-{}/", name, version)),
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Git {
 +                name,
 +                url,
 +                commit,
 +                options,
 +            } => {
 +                let repo_path = {
 +                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
 +                    // add a -git suffix in case we have the same crate from crates.io and a git repo
 +                    repo_path.push(format!("{}-git", name));
 +                    repo_path
 +                };
 +                // clone the repo if we have not done so
 +                if !repo_path.is_dir() {
 +                    println!("Cloning {} and checking out {}", url, commit);
 +                    if !Command::new("git")
 +                        .arg("clone")
 +                        .arg(url)
 +                        .arg(&repo_path)
 +                        .status()
 +                        .expect("Failed to clone git repo!")
 +                        .success()
 +                    {
 +                        eprintln!("Failed to clone {} into {}", url, repo_path.display())
 +                    }
 +                }
 +                // check out the commit/branch/whatever
 +                if !Command::new("git")
 +                    .arg("checkout")
 +                    .arg(commit)
 +                    .current_dir(&repo_path)
 +                    .status()
 +                    .expect("Failed to check out commit")
 +                    .success()
 +                {
 +                    eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
 +                }
 +
 +                Crate {
 +                    version: commit.clone(),
 +                    name: name.clone(),
 +                    path: repo_path,
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Path { name, path, options } => {
 +                use fs_extra::dir;
 +
 +                // simply copy the entire directory into our target dir
 +                let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES));
 +
 +                // the source path of the crate we copied,  ${copy_dest}/crate_name
 +                let crate_root = copy_dest.join(name); // .../crates/local_crate
 +
 +                if crate_root.exists() {
 +                    println!(
 +                        "Not copying {} to {}, destination already exists",
 +                        path.display(),
 +                        crate_root.display()
 +                    );
 +                } else {
 +                    println!("Copying {} to {}", path.display(), copy_dest.display());
 +
 +                    dir::copy(path, &copy_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| {
 +                        panic!("Failed to copy from {}, to  {}", path.display(), crate_root.display())
 +                    });
 +                }
 +
 +                Crate {
 +                    version: String::from("local"),
 +                    name: name.clone(),
 +                    path: crate_root,
 +                    options: options.clone(),
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +impl Crate {
 +    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
 +    /// issued
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        thread_limit: usize,
 +        total_crates_to_lint: usize,
 +        fix: bool,
 +    ) -> Vec<ClippyWarning> {
 +        // advance the atomic index by one
 +        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
 +        // "loop" the index within 0..thread_limit
 +        let thread_index = index % thread_limit;
 +        let perc = (index * 100) / total_crates_to_lint;
 +
 +        if thread_limit == 1 {
 +            println!(
 +                "{}/{} {}% Linting {} {}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version
 +            );
 +        } else {
 +            println!(
 +                "{}/{} {}% Linting {} {} in target dir {:?}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
 +            );
 +        }
 +
 +        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
 +
 +        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 +
 +        let mut args = if fix {
++            vec!["--fix", "--allow-no-vcs", "--", "--cap-lints=warn"]
 +        } else {
 +            vec!["--", "--message-format=json", "--", "--cap-lints=warn"]
 +        };
 +
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                args.push(opt);
 +            }
 +        } else {
 +            args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
 +        }
 +
 +        let all_output = std::process::Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
 +            .env(
 +                "CARGO_TARGET_DIR",
 +                shared_target_dir.join(format!("_{:?}", thread_index)),
 +            )
 +            // lint warnings will look like this:
 +            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
 +            .args(&args)
 +            .current_dir(&self.path)
 +            .output()
 +            .unwrap_or_else(|error| {
 +                panic!(
 +                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    error,
 +                    &cargo_clippy_path.display(),
 +                    &self.path.display()
 +                );
 +            });
 +        let stdout = String::from_utf8_lossy(&all_output.stdout);
 +        let stderr = String::from_utf8_lossy(&all_output.stderr);
 +        let status = &all_output.status;
 +
 +        if !status.success() {
 +            eprintln!(
 +                "\nWARNING: bad exit status after checking {} {} \n",
 +                self.name, self.version
 +            );
 +        }
 +
 +        if fix {
 +            if let Some(stderr) = stderr
 +                .lines()
 +                .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate"))
 +            {
 +                let subcrate = &stderr[63..];
 +                println!(
 +                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {}",
 +                    self.name, subcrate
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        let output_lines = stdout.lines();
 +        let warnings: Vec<ClippyWarning> = output_lines
 +            .into_iter()
 +            // get all clippy warnings and ICEs
 +            .filter(|line| filter_clippy_warnings(&line))
 +            .map(|json_msg| parse_json_message(json_msg, &self))
 +            .collect();
 +
 +        warnings
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct LintcheckConfig {
 +    // max number of jobs to spawn (default 1)
 +    max_jobs: usize,
 +    // we read the sources to check from here
 +    sources_toml_path: PathBuf,
 +    // we save the clippy lint results here
 +    lintcheck_results_path: PathBuf,
 +    // whether to just run --fix and not collect all the warnings
 +    fix: bool,
 +}
 +
 +impl LintcheckConfig {
 +    fn from_clap(clap_config: &ArgMatches) -> Self {
 +        // first, check if we got anything passed via the LINTCHECK_TOML env var,
 +        // if not, ask clap if we got any value for --crates-toml  <foo>
 +        // if not, use the default "lintcheck/lintcheck_crates.toml"
 +        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
 +            clap_config
 +                .value_of("crates-toml")
 +                .clone()
 +                .unwrap_or("lintcheck/lintcheck_crates.toml")
 +                .to_string()
 +        });
 +
 +        let sources_toml_path = PathBuf::from(sources_toml);
 +
 +        // for the path where we save the lint results, get the filename without extension (so for
 +        // wasd.toml, use "wasd"...)
 +        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
 +        let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display()));
 +
 +        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
 +        // use half of that for the physical core count
 +        // by default use a single thread
 +        let max_jobs = match clap_config.value_of("threads") {
 +            Some(threads) => {
 +                let threads: usize = threads
 +                    .parse()
 +                    .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads));
 +                if threads == 0 {
 +                    // automatic choice
 +                    // Rayon seems to return thread count so half that for core count
 +                    (rayon::current_num_threads() / 2) as usize
 +                } else {
 +                    threads
 +                }
 +            },
 +            // no -j passed, use a single thread
 +            None => 1,
 +        };
 +        let fix: bool = clap_config.is_present("fix");
 +
 +        LintcheckConfig {
 +            max_jobs,
 +            sources_toml_path,
 +            lintcheck_results_path,
 +            fix,
 +        }
 +    }
 +}
 +
 +/// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
 +/// or false (we aren't)
 +fn filter_clippy_warnings(line: &str) -> bool {
 +    // we want to collect ICEs because clippy might have crashed.
 +    // these are summarized later
 +    if line.contains("internal compiler error: ") {
 +        return true;
 +    }
 +    // in general, we want all clippy warnings
 +    // however due to some kind of bug, sometimes there are absolute paths
 +    // to libcore files inside the message
 +    // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
 +
 +    // filter out these message to avoid unnecessary noise in the logs
 +    if line.contains("clippy::")
 +        && !(line.contains("could not read cargo metadata")
 +            || (line.contains(".rustup") && line.contains("toolchains")))
 +    {
 +        return true;
 +    }
 +    false
 +}
 +
 +/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 +fn build_clippy() {
 +    let status = Command::new("cargo")
 +        .arg("build")
 +        .status()
 +        .expect("Failed to build clippy!");
 +    if !status.success() {
 +        eprintln!("Error: Failed to compile Clippy!");
 +        std::process::exit(1);
 +    }
 +}
 +
 +/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
 +fn read_crates(toml_path: &Path) -> Vec<CrateSource> {
 +    let toml_content: String =
 +        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
 +        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
 +    // parse the hashmap of the toml file into a list of crates
 +    let tomlcrates: Vec<TomlCrate> = crate_list
 +        .crates
 +        .into_iter()
 +        .map(|(_cratename, tomlcrate)| tomlcrate)
 +        .collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
 +    tomlcrates.into_iter().for_each(|tk| {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        }
 +
 +        // if we have multiple versions, save each one
 +        if let Some(ref versions) = tk.versions {
 +            versions.iter().for_each(|ver| {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
 +            })
 +        }
 +        // otherwise, we should have a git source
 +        if tk.git_url.is_some() && tk.git_hash.is_some() {
 +            crate_sources.push(CrateSource::Git {
 +                name: tk.name.clone(),
 +                url: tk.git_url.clone().unwrap(),
 +                commit: tk.git_hash.clone().unwrap(),
 +                options: tk.options.clone(),
 +            });
 +        }
 +        // if we have a version as well as a git data OR only one git data, something is funky
 +        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
 +            || tk.git_hash.is_some() != tk.git_url.is_some()
 +        {
 +            eprintln!("tomlkrate: {:?}", tk);
 +            if tk.git_hash.is_some() != tk.git_url.is_some() {
 +                panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!");
 +            }
 +            if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) {
 +                panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields");
 +            }
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
 +    });
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    crate_sources
 +}
 +
 +/// Parse the json output of clippy and return a `ClippyWarning`
 +fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
 +    let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
 +
 +    let file: String = jmsg["message"]["spans"][0]["file_name"]
 +        .to_string()
 +        .trim_matches('"')
 +        .into();
 +
 +    let file = if file.contains(".cargo") {
 +        // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from
 +        // the registry.
 +        // don't show the full path in that case.
 +
 +        // /home/matthias/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs
 +        let path = PathBuf::from(file);
 +        let mut piter = path.iter();
 +        // consume all elements until we find ".cargo", so that "/home/matthias" is skipped
 +        let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo"));
 +        // collect the remaining segments
 +        let file = piter.collect::<PathBuf>();
 +        format!("{}", file.display())
 +    } else {
 +        file
 +    };
 +
 +    ClippyWarning {
 +        crate_name: krate.name.to_string(),
 +        crate_version: krate.version.to_string(),
 +        file,
 +        line: jmsg["message"]["spans"][0]["line_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
 +            .to_string()
 +            .trim_matches('"')
 +            .into(),
 +        linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
 +        message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
 +        is_ice: json_message.contains("internal compiler error: "),
 +    }
 +}
 +
 +/// Generate a short list of occuring lints-types and their count
 +fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
 +    // count lint type occurrences
 +    let mut counter: HashMap<&String, usize> = HashMap::new();
 +    clippy_warnings
 +        .iter()
 +        .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
 +
 +    // collect into a tupled list for sorting
 +    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
 +    // sort by "000{count} {clippy::lintname}"
 +    // to not have a lint with 200 and 2 warnings take the same spot
 +    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
 +
 +    let stats_string = stats
 +        .iter()
 +        .map(|(lint, count)| format!("{} {}\n", lint, count))
 +        .collect::<String>();
 +
 +    (stats_string, counter)
 +}
 +
 +/// check if the latest modification of the logfile is older than the modification date of the
 +/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
 +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
 +    if !lintcheck_logs_path.exists() {
 +        return true;
 +    }
 +
 +    let clippy_modified: std::time::SystemTime = {
 +        let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| {
 +            std::fs::metadata(p)
 +                .expect("failed to get metadata of file")
 +                .modified()
 +                .expect("failed to get modification date")
 +        });
 +        // the oldest modification of either of the binaries
 +        std::cmp::max(times.next().unwrap(), times.next().unwrap())
 +    };
 +
 +    let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path)
 +        .expect("failed to get metadata of file")
 +        .modified()
 +        .expect("failed to get modification date");
 +
 +    // time is represented in seconds since X
 +    // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck
 +    logs_modified < clippy_modified
 +}
 +
 +fn is_in_clippy_root() -> bool {
 +    if let Ok(pb) = std::env::current_dir() {
 +        if let Some(file) = pb.file_name() {
 +            return file == PathBuf::from("rust-clippy");
 +        }
 +    }
 +
 +    false
 +}
 +
 +/// lintchecks `main()` function
 +///
 +/// # Panics
 +///
 +/// This function panics if the clippy binaries don't exist
 +/// or if lintcheck is executed from the wrong directory (aka none-repo-root)
 +pub fn main() {
 +    // assert that we launch lintcheck from the repo root (via cargo lintcheck)
 +    if !is_in_clippy_root() {
 +        eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
 +        std::process::exit(3);
 +    }
 +
 +    let clap_config = &get_clap_config();
 +
 +    let config = LintcheckConfig::from_clap(clap_config);
 +
 +    println!("Compiling clippy...");
 +    build_clippy();
 +    println!("Done compiling");
 +
 +    // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
 +    // refresh the logs
 +    if lintcheck_needs_rerun(&config.lintcheck_results_path) {
 +        let shared_target_dir = "target/lintcheck/shared_target_dir";
 +        // if we get an Err here, the shared target dir probably does simply not exist
 +        if let Ok(metadata) = std::fs::metadata(&shared_target_dir) {
 +            if metadata.is_dir() {
 +                println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
 +                std::fs::remove_dir_all(&shared_target_dir)
 +                    .expect("failed to remove target/lintcheck/shared_target_dir");
 +            }
 +        }
 +    }
 +
 +    let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH)
 +        .canonicalize()
 +        .expect("failed to canonicalize path to clippy binary");
 +
 +    // assert that clippy is found
 +    assert!(
 +        cargo_clippy_path.is_file(),
 +        "target/debug/cargo-clippy binary not found! {}",
 +        cargo_clippy_path.display()
 +    );
 +
 +    let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH)
 +        .arg("--version")
 +        .output()
 +        .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
 +        .expect("could not get clippy version!");
 +
 +    // download and extract the crates, then run clippy on them and collect clippys warnings
 +    // flatten into one big list of warnings
 +
 +    let crates = read_crates(&config.sources_toml_path);
 +    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
 +
 +    let counter = AtomicUsize::new(1);
 +
 +    let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
 +        // if we don't have the specified crate in the .toml, throw an error
 +        if !crates.iter().any(|krate| {
 +            let name = match krate {
 +                CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => {
 +                    name
 +                },
 +            };
 +            name == only_one_crate
 +        }) {
 +            eprintln!(
 +                "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
 +                only_one_crate
 +            );
 +            std::process::exit(1);
 +        }
 +
 +        // only check a single crate that was passed via cmdline
 +        crates
 +            .into_iter()
 +            .map(|krate| krate.download_and_extract())
 +            .filter(|krate| krate.name == only_one_crate)
 +            .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix))
 +            .collect()
 +    } else {
 +        if config.max_jobs > 1 {
 +            // run parallel with rayon
 +
 +            // Ask rayon for thread count. Assume that half of that is the number of physical cores
 +            // Use one target dir for each core so that we can run N clippys in parallel.
 +            // We need to use different target dirs because cargo would lock them for a single build otherwise,
 +            // killing the parallelism. However this also means that deps will only be reused half/a
 +            // quarter of the time which might result in a longer wall clock runtime
 +
 +            // This helps when we check many small crates with dep-trees that don't have a lot of branches in
 +            // order to achive some kind of parallelism
 +
 +            // by default, use a single thread
 +            let num_cpus = config.max_jobs;
 +            let num_crates = crates.len();
 +
 +            // check all crates (default)
 +            crates
 +                .into_par_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| {
 +                    krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates, config.fix)
 +                })
 +                .collect()
 +        } else {
 +            // run sequential
 +            let num_crates = crates.len();
 +            crates
 +                .into_iter()
 +                .map(|krate| krate.download_and_extract())
 +                .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix))
 +                .collect()
 +        }
 +    };
 +
 +    // if we are in --fix mode, don't change the log files, terminate here
 +    if config.fix {
 +        return;
 +    }
 +
 +    // generate some stats
 +    let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
 +
 +    // grab crashes/ICEs, save the crate name and the ice message
 +    let ices: Vec<(&String, &String)> = clippy_warnings
 +        .iter()
 +        .filter(|warning| warning.is_ice)
 +        .map(|w| (&w.crate_name, &w.message))
 +        .collect();
 +
 +    let mut all_msgs: Vec<String> = clippy_warnings.iter().map(ToString::to_string).collect();
 +    all_msgs.sort();
 +    all_msgs.push("\n\n\n\nStats:\n".into());
 +    all_msgs.push(stats_formatted);
 +
 +    // save the text into lintcheck-logs/logs.txt
 +    let mut text = clippy_ver; // clippy version number on top
 +    text.push_str(&format!("\n{}", all_msgs.join("")));
 +    text.push_str("ICEs:\n");
 +    ices.iter()
 +        .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    write(&config.lintcheck_results_path, text).unwrap();
 +
 +    print_stats(old_stats, new_stats);
 +}
 +
 +/// read the previous stats from the lintcheck-log file
 +fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
 +    let file_content: String = match std::fs::read_to_string(file_path).ok() {
 +        Some(content) => content,
 +        None => {
 +            return HashMap::new();
 +        },
 +    };
 +
 +    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
 +
 +    // search for the beginning "Stats:" and the end "ICEs:" of the section we want
 +    let start = lines.iter().position(|line| line == "Stats:").unwrap();
 +    let end = lines.iter().position(|line| line == "ICEs:").unwrap();
 +
 +    let stats_lines = &lines[start + 1..end];
 +
 +    stats_lines
 +        .iter()
 +        .map(|line| {
 +            let mut spl = line.split(' ');
 +            (
 +                spl.next().unwrap().to_string(),
 +                spl.next().unwrap().parse::<usize>().unwrap(),
 +            )
 +        })
 +        .collect::<HashMap<String, usize>>()
 +}
 +
 +/// print how lint counts changed between runs
 +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>) {
 +    let same_in_both_hashmaps = old_stats
 +        .iter()
 +        .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val))
 +        .map(|(k, v)| (k.to_string(), *v))
 +        .collect::<Vec<(String, usize)>>();
 +
 +    let mut old_stats_deduped = old_stats;
 +    let mut new_stats_deduped = new_stats;
 +
 +    // remove duplicates from both hashmaps
 +    same_in_both_hashmaps.iter().for_each(|(k, v)| {
 +        assert!(old_stats_deduped.remove(k) == Some(*v));
 +        assert!(new_stats_deduped.remove(k) == Some(*v));
 +    });
 +
 +    println!("\nStats:");
 +
 +    // list all new counts  (key is in new stats but not in old stats)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none())
 +        .for_each(|(new_key, new_value)| {
 +            println!("{} 0 => {}", new_key, new_value);
 +        });
 +
 +    // list all changed counts (key is in both maps but value differs)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some())
 +        .for_each(|(new_key, new_val)| {
 +            let old_val = old_stats_deduped.get::<str>(&new_key).unwrap();
 +            println!("{} {} => {}", new_key, old_val, new_val);
 +        });
 +
 +    // list all gone counts (key is in old status but not in new stats)
 +    old_stats_deduped
 +        .iter()
 +        .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none())
 +        .for_each(|(old_key, old_value)| {
 +            println!("{} {} => 0", old_key, old_value);
 +        });
 +}
 +
 +/// Create necessary directories to run the lintcheck tool.
 +///
 +/// # Panics
 +///
 +/// This function panics if creating one of the dirs fails.
 +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
 +    std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create lintcheck target dir");
 +        }
 +    });
 +    std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate download dir");
 +        }
 +    });
 +    std::fs::create_dir(&extract_dir).unwrap_or_else(|err| {
 +        if err.kind() != ErrorKind::AlreadyExists {
 +            panic!("cannot create crate extraction dir");
 +        }
 +    });
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("lintcheck")
 +        .about("run clippy on a set of crates and check output")
 +        .arg(
 +            Arg::with_name("only")
 +                .takes_value(true)
 +                .value_name("CRATE")
 +                .long("only")
 +                .help("only process a single crate of the list"),
 +        )
 +        .arg(
 +            Arg::with_name("crates-toml")
 +                .takes_value(true)
 +                .value_name("CRATES-SOURCES-TOML-PATH")
 +                .long("crates-toml")
 +                .help("set the path for a crates.toml where lintcheck should read the sources from"),
 +        )
 +        .arg(
 +            Arg::with_name("threads")
 +                .takes_value(true)
 +                .value_name("N")
 +                .short("j")
 +                .long("jobs")
 +                .help("number of threads to use, 0 automatic choice"),
 +        )
 +        .arg(
 +            Arg::with_name("fix")
 +                .long("--fix")
 +                .help("runs cargo clippy --fix and checks if all suggestions apply"),
 +        )
 +        .get_matches()
 +}
 +
 +/// Returns the path to the Clippy project directory
 +///
 +/// # Panics
 +///
 +/// Panics if the current directory could not be retrieved, there was an error reading any of the
 +/// Cargo.toml files or ancestor directory is the clippy root directory
 +#[must_use]
 +pub fn clippy_project_root() -> PathBuf {
 +    let current_dir = std::env::current_dir().unwrap();
 +    for path in current_dir.ancestors() {
 +        let result = std::fs::read_to_string(path.join("Cargo.toml"));
 +        if let Err(err) = &result {
 +            if err.kind() == std::io::ErrorKind::NotFound {
 +                continue;
 +            }
 +        }
 +
 +        let content = result.unwrap();
 +        if content.contains("[package]\nname = \"clippy\"") {
 +            return path.to_path_buf();
 +        }
 +    }
 +    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
 +}
 +
 +#[test]
 +fn lintcheck_test() {
 +    let args = [
 +        "run",
 +        "--target-dir",
 +        "lintcheck/target",
 +        "--manifest-path",
 +        "./lintcheck/Cargo.toml",
 +        "--",
 +        "--crates-toml",
 +        "lintcheck/test_sources.toml",
 +    ];
 +    let status = std::process::Command::new("cargo")
 +        .args(&args)
 +        .current_dir("..") // repo root
 +        .status();
 +    //.output();
 +
 +    assert!(status.unwrap().success());
 +}
index e3863c46288c2e3587445dc494a4c14d6ffa023d,0000000000000000000000000000000000000000..2d3c65c1d3982fa68d7555c7308ee11aecbbde2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2021-06-03"
 +[toolchain]
++channel = "nightly-2021-07-01"
 +components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 7bb80b1196e5900dd2169a91e6d7ab5977c918d6,0000000000000000000000000000000000000000..6bd4123ddeb45d92917f1dd043fa9d09ecf208de
mode 100644,000000..100644
--- /dev/null
@@@ -1,219 -1,0 +1,201 @@@
-         let mut unstable_options = false;
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use rustc_tools_util::VersionInfo;
 +use std::env;
 +use std::ffi::OsString;
 +use std::path::PathBuf;
 +use std::process::{self, Command};
 +
 +const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    -h, --help               Print this message
 +    -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)]
 +"#;
 +
 +fn show_help() {
 +    println!("{}", CARGO_CLIPPY_HELP);
 +}
 +
 +fn show_version() {
 +    let version_info = rustc_tools_util::get_version_info!();
 +    println!("{}", version_info);
 +}
 +
 +pub fn main() {
 +    // Check for version and help flags even when invoked as 'cargo-clippy'
 +    if env::args().any(|a| a == "--help" || a == "-h") {
 +        show_help();
 +        return;
 +    }
 +
 +    if env::args().any(|a| a == "--version" || a == "-V") {
 +        show_version();
 +        return;
 +    }
 +
 +    if let Err(code) = process(env::args().skip(2)) {
 +        process::exit(code);
 +    }
 +}
 +
 +struct ClippyCmd {
 +    cargo_subcommand: &'static str,
 +    args: Vec<String>,
 +    clippy_args: Vec<String>,
 +}
 +
 +impl ClippyCmd {
 +    fn new<I>(mut old_args: I) -> Self
 +    where
 +        I: Iterator<Item = String>,
 +    {
 +        let mut cargo_subcommand = "check";
-                 // Cover -Zunstable-options and -Z unstable-options
-                 s if s.ends_with("unstable-options") => unstable_options = true,
 +        let mut args = vec![];
 +
 +        for arg in old_args.by_ref() {
 +            match arg.as_str() {
 +                "--fix" => {
 +                    cargo_subcommand = "fix";
 +                    continue;
 +                },
 +                "--" => break,
-         if cargo_subcommand == "fix" && !unstable_options {
-             panic!("Usage of `--fix` requires `-Z unstable-options`");
-         }
 +                _ => {},
 +            }
 +
 +            args.push(arg);
 +        }
 +
-     #[should_panic]
-     fn fix_without_unstable() {
 +        let mut clippy_args: Vec<String> = old_args.collect();
 +        if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
 +            clippy_args.push("--no-deps".into());
 +        }
 +
 +        ClippyCmd {
 +            cargo_subcommand,
 +            args,
 +            clippy_args,
 +        }
 +    }
 +
 +    fn path() -> PathBuf {
 +        let mut path = env::current_exe()
 +            .expect("current executable path invalid")
 +            .with_file_name("clippy-driver");
 +
 +        if cfg!(windows) {
 +            path.set_extension("exe");
 +        }
 +
 +        path
 +    }
 +
 +    fn target_dir() -> Option<(&'static str, OsString)> {
 +        env::var_os("CLIPPY_DOGFOOD")
 +            .map(|_| {
 +                env::var_os("CARGO_MANIFEST_DIR").map_or_else(
 +                    || std::ffi::OsString::from("clippy_dogfood"),
 +                    |d| {
 +                        std::path::PathBuf::from(d)
 +                            .join("target")
 +                            .join("dogfood")
 +                            .into_os_string()
 +                    },
 +                )
 +            })
 +            .map(|p| ("CARGO_TARGET_DIR", p))
 +    }
 +
 +    fn into_std_cmd(self) -> Command {
 +        let mut cmd = Command::new("cargo");
 +        let clippy_args: String = self
 +            .clippy_args
 +            .iter()
 +            .map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
 +            .collect();
 +
 +        cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
 +            .envs(ClippyCmd::target_dir())
 +            .env("CLIPPY_ARGS", clippy_args)
 +            .arg(self.cargo_subcommand)
 +            .args(&self.args);
 +
 +        cmd
 +    }
 +}
 +
 +fn process<I>(old_args: I) -> Result<(), i32>
 +where
 +    I: Iterator<Item = String>,
 +{
 +    let cmd = ClippyCmd::new(old_args);
 +
 +    let mut cmd = cmd.into_std_cmd();
 +
 +    let exit_status = cmd
 +        .spawn()
 +        .expect("could not run cargo")
 +        .wait()
 +        .expect("failed to wait for cargo?");
 +
 +    if exit_status.success() {
 +        Ok(())
 +    } else {
 +        Err(exit_status.code().unwrap_or(-1))
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::ClippyCmd;
 +
 +    #[test]
-         ClippyCmd::new(args);
-     }
-     #[test]
-     fn fix_unstable() {
-         let args = "cargo clippy --fix -Zunstable-options"
-             .split_whitespace()
-             .map(ToString::to_string);
++    fn fix() {
 +        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
-         assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("fix", cmd.cargo_subcommand);
-         let args = "cargo clippy --fix -Zunstable-options"
-             .split_whitespace()
-             .map(ToString::to_string);
++        assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
 +    }
 +
 +    #[test]
 +    fn fix_implies_no_deps() {
-         let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
++        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
 +    }
 +
 +    #[test]
 +    fn no_deps_not_duplicated_with_fix() {
++        let args = "cargo clippy --fix -- --no-deps"
 +            .split_whitespace()
 +            .map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
 +    }
 +
 +    #[test]
 +    fn check() {
 +        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("check", cmd.cargo_subcommand);
 +    }
 +}
index 7d266a36bb666387803a921db506712d073720b4,0000000000000000000000000000000000000000..caa19e39ccd3af1baad8a61ad79688cabee02c8d
mode 100644,000000..100644
--- /dev/null
@@@ -1,289 -1,0 +1,306 @@@
-                         panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::path::{Path, PathBuf};
 +
 +mod cargo;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
 +
 +fn host_lib() -> PathBuf {
 +    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
 +}
 +
 +fn clippy_driver_path() -> PathBuf {
 +    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
 +}
 +
 +// When we'll want to use `extern crate ..` for a dependency that is used
 +// both by the crate and the compiler itself, we can't simply pass -L flags
 +// as we'll get a duplicate matching versions. Instead, disambiguate with
 +// `--extern dep=path`.
 +// See https://github.com/rust-lang/rust-clippy/issues/4015.
 +//
 +// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
 +//        Because it would force-rebuild if the options passed to `build` command is not the same
 +//        as what we manually pass to `cargo` invocation
 +fn third_party_crates() -> String {
 +    use std::collections::HashMap;
 +    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
 +    let dep_dir = cargo::TARGET_LIB.join("deps");
 +    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
 +    for entry in fs::read_dir(dep_dir).unwrap() {
 +        let path = match entry {
 +            Ok(entry) => entry.path(),
 +            Err(_) => continue,
 +        };
 +        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
 +            for dep in CRATES {
 +                if name.starts_with(&format!("lib{}-", dep))
 +                    && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
 +                {
 +                    if let Some(old) = crates.insert(dep, path.clone()) {
++                        // Check which action should be done in order to remove compiled deps.
++                        // If pre-installed version of compiler is used, `cargo clean` will do.
++                        // Otherwise (for bootstrapped compiler), the dependencies directory
++                        // must be removed manually.
++                        let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
++                            "remove the stageN-tools directory"
++                        } else {
++                            "run `cargo clean`"
++                        };
++
++                        panic!(
++                            "\n---------------------------------------------------\n\n \
++                            Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \
++                            Probably, you need to {} before running tests again.\n \
++                            \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \
++                            \n---------------------------------------------------\n",
++                            dep, old, path, suggested_action
++                        );
 +                    }
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    let v: Vec<_> = crates
 +        .into_iter()
 +        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
 +        .collect();
 +    v.join(" ")
 +}
 +
 +fn default_config() -> compiletest::Config {
 +    let mut config = compiletest::Config::default();
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +
 +    config.target_rustcflags = Some(format!(
 +        "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
 +        host_lib().join("deps").display(),
 +        cargo::TARGET_LIB.join("deps").display(),
 +        third_party_crates(),
 +    ));
 +
 +    config.build_base = host_lib().join("test_build_base");
 +    config.rustc_path = clippy_driver_path();
 +    config
 +}
 +
 +fn run_ui(cfg: &mut compiletest::Config) {
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui");
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
 +    compiletest::run_tests(cfg);
 +}
 +
 +fn run_internal_tests(cfg: &mut compiletest::Config) {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui-internal");
 +    compiletest::run_tests(cfg);
 +}
 +
 +fn run_ui_toml(config: &mut compiletest::Config) {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(config);
 +
 +    let res = run_tests(config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo(config: &mut compiletest::Config) {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn prepare_env() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    set_var("__CLIPPY_INTERNAL_TESTS", "true");
 +    //set_var("RUST_BACKTRACE", "0");
 +}
 +
 +#[test]
 +fn compile_test() {
 +    prepare_env();
 +    let mut config = default_config();
 +    run_ui(&mut config);
 +    run_ui_toml(&mut config);
 +    run_ui_cargo(&mut config);
 +    run_internal_tests(&mut config);
 +}
 +
 +/// Restores an env var on drop
 +#[must_use]
 +struct VarGuard {
 +    key: &'static str,
 +    value: Option<OsString>,
 +}
 +
 +impl VarGuard {
 +    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
 +        let value = var_os(key);
 +        set_var(key, val);
 +        Self { key, value }
 +    }
 +}
 +
 +impl Drop for VarGuard {
 +    fn drop(&mut self) {
 +        match self.value.as_deref() {
 +            None => remove_var(self.key),
 +            Some(value) => set_var(self.key, value),
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05ba822874d596f86c1acff77e0a9a285bb49d44
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++enforced-import-renames = [
++    { path = "std::option::Option", rename = "Maybe" },
++    { path = "std::process::Child", rename = "Kid" },
++    { path = "std::process::exit", rename = "goodbye" },
++    { path = "std::collections::BTreeMap", rename = "Map" },
++    { path = "std::clone", rename = "foo" },
++    { path = "std::thread::sleep", rename = "thread_sleep" },
++    { path = "std::any::type_name", rename = "ident" },
++    { path = "std::sync::Mutex", rename = "StdMutie" }
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f60058c862888670b741b9229c8ff6d3ce549f38
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![warn(clippy::missing_enforced_import_renames)]
++
++use std::alloc as colla;
++use std::option::Option as Maybe;
++use std::process::{exit as wrong_exit, Child as Kid};
++use std::thread::sleep;
++#[rustfmt::skip]
++use std::{
++    any::{type_name, Any},
++    clone,
++    sync :: Mutex,
++};
++
++fn main() {
++    use std::collections::BTreeMap as OopsWrongRename;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45de8fdffef76d3e22687d9becc41b61d0a9590e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:5:20
++   |
++LL | use std::process::{exit as wrong_exit, Child as Kid};
++   |                    ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye`
++   |
++   = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings`
++
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:6:1
++   |
++LL | use std::thread::sleep;
++   | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep`
++
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:9:11
++   |
++LL |     any::{type_name, Any},
++   |           ^^^^^^^^^ help: try: `type_name as ident`
++
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:10:5
++   |
++LL |     clone,
++   |     ^^^^^ help: try: `clone as foo`
++
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:11:5
++   |
++LL |     sync :: Mutex,
++   |     ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie`
++
++error: this import should be renamed
++  --> $DIR/conf_missing_enforced_import_rename.rs:15:5
++   |
++LL |     use std::collections::BTreeMap as OopsWrongRename;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bced8948a02412124fda554499a11a82e99ffc9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++standard-macro-braces = [
++    { name = "quote", brace = "{" },
++    { name = "quote::quote", brace = "{" },
++    { name = "eprint", brace = "[" },
++    { name = "type_pos", brace = "[" },
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ae6864cbb0b8b23f5a9c6d6213fea82b53aea73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++// #![warn(clippy::nonstandard_macro_braces)]
++
++extern crate quote;
++
++use quote::quote;
++
++#[rustfmt::skip]
++macro_rules! test {
++    () => {
++        vec!{0, 0, 0}
++    };
++}
++
++#[rustfmt::skip]
++macro_rules! test2 {
++    ($($arg:tt)*) => {
++        format_args!($($arg)*)
++    };
++}
++
++macro_rules! type_pos {
++    ($what:ty) => {
++        Vec<$what>
++    };
++}
++
++#[rustfmt::skip]
++fn main() {
++    let _ = vec! {1, 2, 3};
++    let _ = format!["ugh {} stop being such a good compiler", "hello"];
++    let _ = quote!(let x = 1;);
++    let _ = quote::quote!(match match match);
++    let _ = test!();
++    let _ = vec![1,2,3];
++
++    let _ = quote::quote! {true || false};
++    let _ = vec! [0 ,0 ,0];
++    let _ = format!("fds{}fds", 10);
++    let _ = test2!["{}{}{}", 1, 2, 3];
++
++    let _: type_pos!(usize) = vec![];
++
++    eprint!("test if user config overrides defaults");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7bcd1829524ddcb208ae6e791a26c6fb2757e1ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++error: use of irregular braces for `vec!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:29:13
++   |
++LL |     let _ = vec! {1, 2, 3};
++   |             ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings`
++help: consider writing `vec![1, 2, 3]`
++  --> $DIR/conf_nonstandard_macro_braces.rs:29:13
++   |
++LL |     let _ = vec! {1, 2, 3};
++   |             ^^^^^^^^^^^^^^
++
++error: use of irregular braces for `format!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:30:13
++   |
++LL |     let _ = format!["ugh {} stop being such a good compiler", "hello"];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
++  --> $DIR/conf_nonstandard_macro_braces.rs:30:13
++   |
++LL |     let _ = format!["ugh {} stop being such a good compiler", "hello"];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of irregular braces for `quote!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:31:13
++   |
++LL |     let _ = quote!(let x = 1;);
++   |             ^^^^^^^^^^^^^^^^^^
++   |
++help: consider writing `quote! {let x = 1;}`
++  --> $DIR/conf_nonstandard_macro_braces.rs:31:13
++   |
++LL |     let _ = quote!(let x = 1;);
++   |             ^^^^^^^^^^^^^^^^^^
++
++error: use of irregular braces for `quote::quote!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:32:13
++   |
++LL |     let _ = quote::quote!(match match match);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider writing `quote::quote! {match match match}`
++  --> $DIR/conf_nonstandard_macro_braces.rs:32:13
++   |
++LL |     let _ = quote::quote!(match match match);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of irregular braces for `vec!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:10:9
++   |
++LL |         vec!{0, 0, 0}
++   |         ^^^^^^^^^^^^^
++...
++LL |     let _ = test!();
++   |             ------- in this macro invocation
++   |
++help: consider writing `vec![0, 0, 0]`
++  --> $DIR/conf_nonstandard_macro_braces.rs:10:9
++   |
++LL |         vec!{0, 0, 0}
++   |         ^^^^^^^^^^^^^
++...
++LL |     let _ = test!();
++   |             ------- in this macro invocation
++   = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: use of irregular braces for `type_pos!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:41:12
++   |
++LL |     let _: type_pos!(usize) = vec![];
++   |            ^^^^^^^^^^^^^^^^
++   |
++help: consider writing `type_pos![usize]`
++  --> $DIR/conf_nonstandard_macro_braces.rs:41:12
++   |
++LL |     let _: type_pos!(usize) = vec![];
++   |            ^^^^^^^^^^^^^^^^
++
++error: use of irregular braces for `eprint!` macro
++  --> $DIR/conf_nonstandard_macro_braces.rs:43:5
++   |
++LL |     eprint!("test if user config overrides defaults");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider writing `eprint!["test if user config overrides defaults"];`
++  --> $DIR/conf_nonstandard_macro_braces.rs:43:5
++   |
++LL |     eprint!("test if user config overrides defaults");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index c0df3b6e8af5dd12d5ac7d618e6d9084525a3776,0000000000000000000000000000000000000000..a3245da68250c3d28939ddb5da02dc08fbb938ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,5 @@@
- disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"]
++disallowed-methods = [
++    "std::iter::Iterator::sum",
++    "regex::Regex::is_match",
++    "regex::Regex::new"
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2eff854c22c3078e55432a8297c3b52190041de1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++disallowed-types = [
++    "std::collections::HashMap",
++    "std::sync::atomic::AtomicU32",
++    "syn::TypePath",
++    "proc_macro2::Ident",
++    "std::thread::Thread",
++    "std::time::Instant",
++    "std::io::Read",
++]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..567afb5aec1df83290478d785c28621b15c619fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![warn(clippy::disallowed_type)]
++
++extern crate quote;
++extern crate syn;
++
++use std::sync as foo;
++use std::sync::atomic::AtomicU32;
++use std::time::Instant as Sneaky;
++
++struct HashMap;
++
++fn bad_return_type() -> fn() -> Sneaky {
++    todo!()
++}
++
++fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
++    todo!()
++}
++
++fn trait_obj(_: &dyn std::io::Read) {
++    todo!()
++}
++
++static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
++
++#[allow(clippy::diverging_sub_expression)]
++fn main() {
++    let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
++    let _ = Sneaky::now();
++    let _ = foo::atomic::AtomicU32::new(0);
++    static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
++    let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
++    let _ = syn::Ident::new("", todo!());
++    let _ = HashMap;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e6fd91fba1192d8d27702073679acbd609aff01
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++error: `std::sync::atomic::AtomicU32` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:7:1
++   |
++LL | use std::sync::atomic::AtomicU32;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::disallowed-type` implied by `-D warnings`
++
++error: `std::time::Instant` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:8:1
++   |
++LL | use std::time::Instant as Sneaky;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::time::Instant` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:12:33
++   |
++LL | fn bad_return_type() -> fn() -> Sneaky {
++   |                                 ^^^^^^
++
++error: `std::time::Instant` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:16:28
++   |
++LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
++   |                            ^^^^^^
++
++error: `std::sync::atomic::AtomicU32` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:16:39
++   |
++LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
++   |                                       ^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::io::Read` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:20:22
++   |
++LL | fn trait_obj(_: &dyn std::io::Read) {
++   |                      ^^^^^^^^^^^^^
++
++error: `std::collections::HashMap` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:28:48
++   |
++LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
++   |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::collections::HashMap` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:28:12
++   |
++LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::time::Instant` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:29:13
++   |
++LL |     let _ = Sneaky::now();
++   |             ^^^^^^
++
++error: `std::sync::atomic::AtomicU32` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:30:13
++   |
++LL |     let _ = foo::atomic::AtomicU32::new(0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::sync::atomic::AtomicU32` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:31:17
++   |
++LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: `std::sync::atomic::AtomicU32` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:31:48
++   |
++LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
++   |                                                ^^^^^^^^^^^^^^^^^^^^^^
++
++error: `syn::TypePath` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:32:43
++   |
++LL |     let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
++   |                                           ^^^^^^^^^^^^^
++
++error: `proc_macro2::Ident` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:33:13
++   |
++LL |     let _ = syn::Ident::new("", todo!());
++   |             ^^^^^^^^^^
++
++error: aborting due to 14 previous errors
++
index a7be00426c41c4896cea47a60f534b172db1e648,0000000000000000000000000000000000000000..e0029ebeb27ac4ad94966097b9c02f5914e9a4b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,4 -1,0 +1,4 @@@
- error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `third-party` at line 5 column 1
 +
 +error: aborting due to previous error
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..283358333cdfd30bb4dfef176b0c25c54cc65519
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// run-rustfix
++#![warn(clippy::append_instead_of_extend)]
++use std::collections::BinaryHeap;
++fn main() {
++    //gets linted
++    let mut vec1 = vec![0u8; 1024];
++    let mut vec2: std::vec::Vec<u8> = Vec::new();
++
++    vec2.append(&mut vec1);
++
++    let mut vec3 = vec![0u8; 1024];
++    let mut vec4: std::vec::Vec<u8> = Vec::new();
++
++    vec4.append(&mut vec3);
++
++    let mut vec11: std::vec::Vec<u8> = Vec::new();
++
++    vec11.append(&mut return_vector());
++
++    //won't get linted it dosen't move the entire content of a vec into another
++    let mut test1 = vec![0u8, 10];
++    let mut test2: std::vec::Vec<u8> = Vec::new();
++
++    test2.extend(test1.drain(4..10));
++
++    let mut vec3 = vec![0u8; 104];
++    let mut vec7: std::vec::Vec<u8> = Vec::new();
++
++    vec3.append(&mut vec7);
++
++    let mut vec5 = vec![0u8; 1024];
++    let mut vec6: std::vec::Vec<u8> = Vec::new();
++
++    vec5.extend(vec6.drain(..4));
++
++    let mut vec9: std::vec::Vec<u8> = Vec::new();
++
++    return_vector().append(&mut vec9);
++
++    //won't get linted because it is not a vec
++
++    let mut heap = BinaryHeap::from(vec![1, 3]);
++    let mut heap2 = BinaryHeap::from(vec![]);
++    heap2.extend(heap.drain())
++}
++
++fn return_vector() -> Vec<u8> {
++    let mut new_vector = vec![];
++
++    for i in 1..10 {
++        new_vector.push(i)
++    }
++
++    new_vector
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abde5cdac5cf7f6079c3d1b77c3595b515c13d19
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// run-rustfix
++#![warn(clippy::append_instead_of_extend)]
++use std::collections::BinaryHeap;
++fn main() {
++    //gets linted
++    let mut vec1 = vec![0u8; 1024];
++    let mut vec2: std::vec::Vec<u8> = Vec::new();
++
++    vec2.extend(vec1.drain(..));
++
++    let mut vec3 = vec![0u8; 1024];
++    let mut vec4: std::vec::Vec<u8> = Vec::new();
++
++    vec4.extend(vec3.drain(..));
++
++    let mut vec11: std::vec::Vec<u8> = Vec::new();
++
++    vec11.extend(return_vector().drain(..));
++
++    //won't get linted it dosen't move the entire content of a vec into another
++    let mut test1 = vec![0u8, 10];
++    let mut test2: std::vec::Vec<u8> = Vec::new();
++
++    test2.extend(test1.drain(4..10));
++
++    let mut vec3 = vec![0u8; 104];
++    let mut vec7: std::vec::Vec<u8> = Vec::new();
++
++    vec3.append(&mut vec7);
++
++    let mut vec5 = vec![0u8; 1024];
++    let mut vec6: std::vec::Vec<u8> = Vec::new();
++
++    vec5.extend(vec6.drain(..4));
++
++    let mut vec9: std::vec::Vec<u8> = Vec::new();
++
++    return_vector().append(&mut vec9);
++
++    //won't get linted because it is not a vec
++
++    let mut heap = BinaryHeap::from(vec![1, 3]);
++    let mut heap2 = BinaryHeap::from(vec![]);
++    heap2.extend(heap.drain())
++}
++
++fn return_vector() -> Vec<u8> {
++    let mut new_vector = vec![];
++
++    for i in 1..10 {
++        new_vector.push(i)
++    }
++
++    new_vector
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d309d981def99cbbc86ceeae681ed097a315b5b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: use of `extend` instead of `append` for adding the full range of a second vector
++  --> $DIR/append_instead_of_extend.rs:9:5
++   |
++LL |     vec2.extend(vec1.drain(..));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)`
++   |
++   = note: `-D clippy::append-instead-of-extend` implied by `-D warnings`
++
++error: use of `extend` instead of `append` for adding the full range of a second vector
++  --> $DIR/append_instead_of_extend.rs:14:5
++   |
++LL |     vec4.extend(vec3.drain(..));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)`
++
++error: use of `extend` instead of `append` for adding the full range of a second vector
++  --> $DIR/append_instead_of_extend.rs:18:5
++   |
++LL |     vec11.extend(return_vector().drain(..));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())`
++
++error: aborting due to 3 previous errors
++
index e989de654045632a1a496859c9cff7640742492a,0000000000000000000000000000000000000000..6617ca183a8cb2cbd37c0fa60c3b4fe1a1eb23cc
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,34 @@@
 +#![allow(non_fmt_panic)]
 +
 +macro_rules! assert_const {
 +    ($len:expr) => {
 +        assert!($len > 0);
 +        debug_assert!($len < 0);
 +    };
 +}
 +
 +fn main() {
 +    assert!(true);
 +    assert!(false);
 +    assert!(true, "true message");
 +    assert!(false, "false message");
 +
 +    let msg = "panic message";
 +    assert!(false, msg.to_uppercase());
 +
 +    const B: bool = true;
 +    assert!(B);
 +
 +    const C: bool = false;
 +    assert!(C);
 +    assert!(C, "C message");
 +
 +    debug_assert!(true);
 +    // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
 +    debug_assert!(false); // #3948
 +    assert_const!(3);
 +    assert_const!(-1);
++
++    // Don't lint on this:
++    assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
 +}
index d4470d3f40708517027b62f158c11c692b70ecaf,0000000000000000000000000000000000000000..170955e726cc573853764fbaaf16b187450bada7
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,115 @@@
 +#![allow(dead_code)]
 +
 +//! Used to test that certain lints don't trigger in imported external macros
 +
 +#[macro_export]
 +macro_rules! foofoo {
 +    () => {
 +        loop {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! must_use_unit {
 +    () => {
 +        #[must_use]
 +        fn foo() {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! try_err {
 +    () => {
 +        pub fn try_err_fn() -> Result<i32, i32> {
 +            let err: i32 = 1;
 +            // To avoid warnings during rustfix
 +            if true { Err(err)? } else { Ok(2) }
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! string_add {
 +    () => {
 +        let y = "".to_owned();
 +        let z = y + "...";
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! take_external {
 +    ($s:expr) => {
 +        std::mem::replace($s, Default::default())
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! option_env_unwrap_external {
 +    ($env: expr) => {
 +        option_env!($env).unwrap()
 +    };
 +    ($env: expr, $message: expr) => {
 +        option_env!($env).expect($message)
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_binding {
 +    () => {
 +        let ref _y = 42;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_function {
 +    () => {
 +        fn fun_example(ref _x: usize) {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv_with_arg {
 +    (0u32 as u64) => {
 +        ()
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv {
 +    () => {
 +        0u32 as u64
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! large_enum_variant {
 +    () => {
 +        enum LargeEnumInMacro {
 +            A(i32),
 +            B([i32; 8000]),
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! field_reassign_with_default {
 +    () => {
 +        #[derive(Default)]
 +        struct A {
 +            pub i: i32,
 +            pub j: i64,
 +        }
 +        fn lint() {
 +            let mut a: A = Default::default();
 +            a.i = 42;
 +            a;
 +        }
 +    };
 +}
++
++#[macro_export]
++macro_rules! default_numeric_fallback {
++    () => {
++        let x = 22;
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..420232f9f8d89706353f530900e223ed53a7187b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// Stripped down version of the ErrorKind enum of std
++#[non_exhaustive]
++pub enum ErrorKind {
++    NotFound,
++    PermissionDenied,
++    #[doc(hidden)]
++    Uncategorized,
++}
index cb15bdd2f1b2d4cca8383b2967a535ee3d3ae20f,0000000000000000000000000000000000000000..57d7139fef5683d7652d5a7c968b25326564fd6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,57 @@@
 +#![allow(
 +    dead_code,
 +    clippy::similar_names,
 +    clippy::single_match,
 +    clippy::toplevel_ref_arg,
 +    unused_mut,
 +    unused_variables
 +)]
 +#![warn(clippy::blacklisted_name)]
 +
 +fn test(foo: ()) {}
 +
 +fn main() {
 +    let foo = 42;
 +    let baz = 42;
 +    let quux = 42;
 +    // Unlike these others, `bar` is actually considered an acceptable name.
 +    // Among many other legitimate uses, bar commonly refers to a period of time in music.
 +    // See https://github.com/rust-lang/rust-clippy/issues/5225.
 +    let bar = 42;
 +
 +    let food = 42;
 +    let foodstuffs = 42;
 +    let bazaar = 42;
 +
 +    match (42, Some(1337), Some(0)) {
 +        (foo, Some(baz), quux @ Some(_)) => (),
 +        _ => (),
 +    }
 +}
 +
 +fn issue_1647(mut foo: u8) {
 +    let mut baz = 0;
 +    if let Some(mut quux) = Some(42) {}
 +}
 +
 +fn issue_1647_ref() {
 +    let ref baz = 0;
 +    if let Some(ref quux) = Some(42) {}
 +}
 +
 +fn issue_1647_ref_mut() {
 +    let ref mut baz = 0;
 +    if let Some(ref mut quux) = Some(42) {}
 +}
++
++mod tests {
++    fn issue_7305() {
++        // `blackisted_name` lint should not be triggered inside of the test code.
++        let foo = 0;
++
++        // Check that even in nested functions warning is still not triggere.
++        fn nested() {
++            let foo = 0;
++        }
++    }
++}
index 43468872db0b45f69c61799afc1c6b75521e7100,0000000000000000000000000000000000000000..c0625fd1b75e8af15b1e2af3f9d43f749963974b
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,159 @@@
++// aux-build:macro_rules.rs
++
 +#![warn(clippy::default_numeric_fallback)]
 +#![allow(unused)]
 +#![allow(clippy::never_loop)]
 +#![allow(clippy::no_effect)]
 +#![allow(clippy::unnecessary_operation)]
 +#![allow(clippy::branches_sharing_code)]
 +
++#[macro_use]
++extern crate macro_rules;
++
 +mod basic_expr {
 +    fn test() {
 +        // Should lint unsuffixed literals typed `i32`.
 +        let x = 22;
 +        let x = [1, 2, 3];
 +        let x = if true { (1, 2) } else { (3, 4) };
 +        let x = match 1 {
 +            1 => 1,
 +            _ => 2,
 +        };
 +
 +        // Should lint unsuffixed literals typed `f64`.
 +        let x = 0.12;
 +
 +        // Should NOT lint suffixed literals.
 +        let x = 22_i32;
 +        let x = 0.12_f64;
 +
 +        // Should NOT lint literals in init expr if `Local` has a type annotation.
 +        let x: f64 = 0.1;
 +        let x: [i32; 3] = [1, 2, 3];
 +        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
 +        let x: _ = 1;
 +    }
 +}
 +
 +mod nested_local {
 +    fn test() {
 +        let x: _ = {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        };
 +
 +        let x: _ = if true {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            1
 +        } else {
 +            // Should lint this because this literal is not bound to any types.
 +            let y = 1;
 +
 +            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
 +            2
 +        };
 +    }
 +}
 +
 +mod function_def {
 +    fn ret_i32() -> i32 {
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        1
 +    }
 +
 +    fn test() {
 +        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
 +        // type.
 +        let f = || -> _ { 1 };
 +
 +        // Even though the output type is specified,
 +        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
 +        let f = || -> i32 { 1 };
 +    }
 +}
 +
 +mod function_calls {
 +    fn concrete_arg(x: i32) {}
 +
 +    fn generic_arg<T>(t: T) {}
 +
 +    fn test() {
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        concrete_arg(1);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        generic_arg(1);
 +
 +        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
 +        let x: _ = generic_arg(1);
 +    }
 +}
 +
 +mod struct_ctor {
 +    struct ConcreteStruct {
 +        x: i32,
 +    }
 +
 +    struct GenericStruct<T> {
 +        x: T,
 +    }
 +
 +    fn test() {
 +        // Should NOT lint this because the field type is bound to a concrete type.
 +        ConcreteStruct { x: 1 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        GenericStruct { x: 1 };
 +
 +        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
 +        let _ = GenericStruct { x: 1 };
 +    }
 +}
 +
 +mod method_calls {
 +    struct StructForMethodCallTest {}
 +
 +    impl StructForMethodCallTest {
 +        fn concrete_arg(&self, x: i32) {}
 +
 +        fn generic_arg<T>(&self, t: T) {}
 +    }
 +
 +    fn test() {
 +        let s = StructForMethodCallTest {};
 +
 +        // Should NOT lint this because the argument type is bound to a concrete type.
 +        s.concrete_arg(1);
 +
 +        // Should lint this because the argument type is bound to a concrete type.
 +        s.generic_arg(1);
 +    }
 +}
 +
++mod in_macro {
++    macro_rules! internal_macro {
++        () => {
++            let x = 22;
++        };
++    }
++
++    // Should lint in internal macro.
++    fn internal() {
++        internal_macro!();
++    }
++
++    // Should NOT lint in external macro.
++    fn external() {
++        default_numeric_fallback!();
++    }
++}
++
 +fn main() {}
index d1c4c8203dd832a07875d668dd9d7178928f64ca,0000000000000000000000000000000000000000..5862cd936ac104ac03822912e75bed4dd4130c61
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,159 @@@
-   --> $DIR/default_numeric_fallback.rs:11:17
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:12:18
++  --> $DIR/default_numeric_fallback.rs:16:17
 +   |
 +LL |         let x = 22;
 +   |                 ^^ help: consider adding suffix: `22_i32`
 +   |
 +   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:12:21
++  --> $DIR/default_numeric_fallback.rs:17:18
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                  ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:12:24
++  --> $DIR/default_numeric_fallback.rs:17:21
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                     ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:13:28
++  --> $DIR/default_numeric_fallback.rs:17:24
 +   |
 +LL |         let x = [1, 2, 3];
 +   |                        ^ help: consider adding suffix: `3_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:13:31
++  --> $DIR/default_numeric_fallback.rs:18:28
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                            ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:13:44
++  --> $DIR/default_numeric_fallback.rs:18:31
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                               ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:13:47
++  --> $DIR/default_numeric_fallback.rs:18:44
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                                            ^ help: consider adding suffix: `3_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:14:23
++  --> $DIR/default_numeric_fallback.rs:18:47
 +   |
 +LL |         let x = if true { (1, 2) } else { (3, 4) };
 +   |                                               ^ help: consider adding suffix: `4_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:15:13
++  --> $DIR/default_numeric_fallback.rs:19:23
 +   |
 +LL |         let x = match 1 {
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:15:18
++  --> $DIR/default_numeric_fallback.rs:20:13
 +   |
 +LL |             1 => 1,
 +   |             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:16:18
++  --> $DIR/default_numeric_fallback.rs:20:18
 +   |
 +LL |             1 => 1,
 +   |                  ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:20:17
++  --> $DIR/default_numeric_fallback.rs:21:18
 +   |
 +LL |             _ => 2,
 +   |                  ^ help: consider adding suffix: `2_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:38:21
++  --> $DIR/default_numeric_fallback.rs:25:17
 +   |
 +LL |         let x = 0.12;
 +   |                 ^^^^ help: consider adding suffix: `0.12_f64`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:46:21
++  --> $DIR/default_numeric_fallback.rs:43:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:52:21
++  --> $DIR/default_numeric_fallback.rs:51:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:64:9
++  --> $DIR/default_numeric_fallback.rs:57:21
 +   |
 +LL |             let y = 1;
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:70:27
++  --> $DIR/default_numeric_fallback.rs:69:9
 +   |
 +LL |         1
 +   |         ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:74:29
++  --> $DIR/default_numeric_fallback.rs:75:27
 +   |
 +LL |         let f = || -> _ { 1 };
 +   |                           ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:88:21
++  --> $DIR/default_numeric_fallback.rs:79:29
 +   |
 +LL |         let f = || -> i32 { 1 };
 +   |                             ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:91:32
++  --> $DIR/default_numeric_fallback.rs:93:21
 +   |
 +LL |         generic_arg(1);
 +   |                     ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:109:28
++  --> $DIR/default_numeric_fallback.rs:96:32
 +   |
 +LL |         let x: _ = generic_arg(1);
 +   |                                ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:112:36
++  --> $DIR/default_numeric_fallback.rs:114:28
 +   |
 +LL |         GenericStruct { x: 1 };
 +   |                            ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
-   --> $DIR/default_numeric_fallback.rs:132:23
++  --> $DIR/default_numeric_fallback.rs:117:36
 +   |
 +LL |         let _ = GenericStruct { x: 1 };
 +   |                                    ^ help: consider adding suffix: `1_i32`
 +
 +error: default numeric fallback might occur
- error: aborting due to 24 previous errors
++  --> $DIR/default_numeric_fallback.rs:137:23
 +   |
 +LL |         s.generic_arg(1);
 +   |                       ^ help: consider adding suffix: `1_i32`
 +
++error: default numeric fallback might occur
++  --> $DIR/default_numeric_fallback.rs:144:21
++   |
++LL |             let x = 22;
++   |                     ^^ help: consider adding suffix: `22_i32`
++...
++LL |         internal_macro!();
++   |         ------------------ in this macro invocation
++   |
++   = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 25 previous errors
 +
index 03c9f438891363fe3250e951db20ad94299e0365,0000000000000000000000000000000000000000..0af6b500115dc1ae828261e7f38baa9cf6281ebf
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,100 @@@
- error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items
 +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
 +  --> $DIR/deprecated.rs:1:8
 +   |
 +LL | #[warn(clippy::unstable_as_slice)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 +
 +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
 +  --> $DIR/deprecated.rs:2:8
 +   |
 +LL | #[warn(clippy::unstable_as_mut_slice)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
 +  --> $DIR/deprecated.rs:3:8
 +   |
 +LL | #[warn(clippy::misaligned_transmute)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
 +  --> $DIR/deprecated.rs:4:8
 +   |
 +LL | #[warn(clippy::unused_collect)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
 +  --> $DIR/deprecated.rs:5:8
 +   |
 +LL | #[warn(clippy::invalid_ref)]
 +   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 +
 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
 +  --> $DIR/deprecated.rs:6:8
 +   |
 +LL | #[warn(clippy::into_iter_on_array)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 +
 +error: lint `clippy::unused_label` has been renamed to `unused_labels`
 +  --> $DIR/deprecated.rs:7:8
 +   |
 +LL | #[warn(clippy::unused_label)]
 +   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 +
 +error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
 +  --> $DIR/deprecated.rs:8:8
 +   |
 +LL | #[warn(clippy::regex_macro)]
 +   |        ^^^^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
 +  --> $DIR/deprecated.rs:9:8
 +   |
 +LL | #[warn(clippy::drop_bounds)]
 +   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 +
 +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
 +  --> $DIR/deprecated.rs:10:8
 +   |
 +LL | #[warn(clippy::temporary_cstring_as_ptr)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 +
 +error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
 +  --> $DIR/deprecated.rs:11:8
 +   |
 +LL | #[warn(clippy::panic_params)]
 +   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
 +
 +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
 +  --> $DIR/deprecated.rs:12:8
 +   |
 +LL | #[warn(clippy::unknown_clippy_lints)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 +
 +error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
 +  --> $DIR/deprecated.rs:13:8
 +   |
 +LL | #[warn(clippy::find_map)]
 +   |        ^^^^^^^^^^^^^^^^
 +
 +error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
 +  --> $DIR/deprecated.rs:14:8
 +   |
 +LL | #[warn(clippy::filter_map)]
 +   |        ^^^^^^^^^^^^^^^^^^
 +
- error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items
++error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
 +  --> $DIR/deprecated.rs:15:8
 +   |
 +LL | #[warn(clippy::pub_enum_variant_names)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
 +  --> $DIR/deprecated.rs:16:8
 +   |
 +LL | #[warn(clippy::wrong_pub_self_convention)]
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 16 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cfdda35971fb617829cfd0bb8c2bc46c72efc7e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#![deny(clippy::disallowed_script_idents)]
++#![allow(dead_code)]
++
++fn main() {
++    let counter = 10; // OK, latin is allowed.
++    let zähler = 10; // OK, it's still latin.
++
++    let счётчик = 10; // Cyrillic is not allowed by default.
++    let カウンタ = 10; // Same for japanese.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc84dc1d43c59839d04b2497529344c85daa1f45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: identifier `счётчик` has a Unicode script that is not allowed by configuration: Cyrillic
++  --> $DIR/disallowed_script_idents.rs:8:9
++   |
++LL |     let счётчик = 10; // Cyrillic is not allowed by default.
++   |         ^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/disallowed_script_idents.rs:1:9
++   |
++LL | #![deny(clippy::disallowed_script_idents)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana
++  --> $DIR/disallowed_script_idents.rs:9:9
++   |
++LL |     let カウンタ = 10; // Same for japanese.
++   |         ^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8afef6b23d47653c27a9f2d2398ed411fd08667a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,221 @@@
++//! This file tests for the `DOC_MARKDOWN` lint.
++
++#![allow(dead_code, incomplete_features)]
++#![warn(clippy::doc_markdown)]
++#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)]
++#![rustfmt::skip]
++
++/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
++/// which should be reported only once despite being __doubly bad__.
++/// Here be ::a::global:path.
++/// That's not code ~NotInCodeBlock~.
++/// be_sure_we_got_to_the_end_of_it
++fn foo_bar() {
++}
++
++/// That one tests multiline ticks.
++/// ```rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ```
++///
++/// ~~~rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ~~~
++/// be_sure_we_got_to_the_end_of_it
++fn multiline_codeblock() {
++}
++
++/// This _is a test for
++/// multiline
++/// emphasis_.
++/// be_sure_we_got_to_the_end_of_it
++fn test_emphasis() {
++}
++
++/// This tests units. See also #835.
++/// kiB MiB GiB TiB PiB EiB
++/// kib Mib Gib Tib Pib Eib
++/// kB MB GB TB PB EB
++/// kb Mb Gb Tb Pb Eb
++/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
++/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
++/// 32kB 32MB 32GB 32TB 32PB 32EB
++/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
++/// NaN
++/// be_sure_we_got_to_the_end_of_it
++fn test_units() {
++}
++
++/// This tests allowed identifiers.
++/// KiB MiB GiB TiB PiB EiB
++/// DirectX
++/// ECMAScript
++/// GPLv2 GPLv3
++/// GitHub GitLab
++/// IPv4 IPv6
++/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
++/// NaN NaNs
++/// OAuth GraphQL
++/// OCaml
++/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
++/// WebGL
++/// TensorFlow
++/// TrueType
++/// iOS macOS FreeBSD
++/// TeX LaTeX BibTeX BibLaTeX
++/// MinGW
++/// CamelCase (see also #2395)
++/// be_sure_we_got_to_the_end_of_it
++fn test_allowed() {
++}
++
++/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
++/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
++/// It can also be [inline_link2].
++///
++/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
++/// [inline_link]: https://foobar
++/// [inline_link2]: https://foobar
++/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
++/// `multiline_ticks` functions.
++///
++/// expression of the type  `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
++/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
++/// be_sure_we_got_to_the_end_of_it
++fn main() {
++    foo_bar();
++    multiline_codeblock();
++    test_emphasis();
++    test_units();
++}
++
++/// ## CamelCaseThing
++/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
++///
++/// # CamelCaseThing
++///
++/// Not a title #897 CamelCaseThing
++/// be_sure_we_got_to_the_end_of_it
++fn issue897() {
++}
++
++/// I am confused by brackets? (`x_y`)
++/// I am confused by brackets? (foo `x_y`)
++/// I am confused by brackets? (`x_y` foo)
++/// be_sure_we_got_to_the_end_of_it
++fn issue900() {
++}
++
++/// Diesel queries also have a similar problem to [Iterator][iterator], where
++/// /// More talking
++/// returning them from a function requires exposing the implementation of that
++/// function. The [`helper_types`][helper_types] module exists to help with this,
++/// but you might want to hide the return type or have it conditionally change.
++/// Boxing can achieve both.
++///
++/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
++/// [helper_types]: ../helper_types/index.html
++/// be_sure_we_got_to_the_end_of_it
++fn issue883() {
++}
++
++/// `foo_bar
++/// baz_quz`
++/// [foo
++/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
++fn multiline() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: `PackedNode`
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: PackedNode
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073_alt() {
++}
++
++/// Tests more than three quotes:
++/// ````
++/// DoNotWarn
++/// ```
++/// StillDont
++/// ````
++/// be_sure_we_got_to_the_end_of_it
++fn four_quotes() {
++}
++
++/// See [NIST SP 800-56A, revision 2].
++///
++/// [NIST SP 800-56A, revision 2]:
++///     https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419
++fn issue_902_comment() {}
++
++#[cfg_attr(feature = "a", doc = " ```")]
++#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
++/// fn main() {
++///     let s = "localhost:10000".to_string();
++///     println!("{}", s);
++/// }
++/// ```
++fn issue_1469() {}
++
++/**
++ * This is a doc comment that should not be a list
++ *This would also be an error under a strict common mark interpretation
++ */
++fn issue_1920() {}
++
++/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
++///
++/// Not ok: http://www.unicode.org
++/// Not ok: https://www.unicode.org
++/// Not ok: http://www.unicode.org/
++/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
++fn issue_1832() {}
++
++/// An iterator over mycrate::Collection's values.
++/// It should not lint a `'static` lifetime in ticks.
++fn issue_2210() {}
++
++/// This should not cause the lint to trigger:
++/// #REQ-data-family.lint_partof_exists
++fn issue_2343() {}
++
++/// This should not cause an ICE:
++/// __|_ _|__||_|
++fn pulldown_cmark_crash() {}
++
++// issue #7033 - const_evaluatable_checked ICE
++struct S<T, const N: usize>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++    arr: [T; N.checked_next_power_of_two().unwrap()],
++    n: usize,
++}
++
++impl<T: Copy + Default, const N: usize> S<T, N>
++where [(); N.checked_next_power_of_two().unwrap()]: {
++    fn new() -> Self {
++        Self {
++            arr: [T::default(); N.checked_next_power_of_two().unwrap()],
++            n: 0,
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7eab8a85f093d6f1de5a51e517605338f98fee33
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,190 @@@
++error: you should put `foo_bar` between ticks in the documentation
++  --> $DIR/doc.rs:8:9
++   |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++   |         ^^^^^^^
++   |
++   = note: `-D clippy::doc-markdown` implied by `-D warnings`
++
++error: you should put `foo::bar` between ticks in the documentation
++  --> $DIR/doc.rs:8:51
++   |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++   |                                                   ^^^^^^^^
++
++error: you should put `Foo::some_fun` between ticks in the documentation
++  --> $DIR/doc.rs:9:83
++   |
++LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
++   |                                                                                   ^^^^^^^^^^^^^
++
++error: you should put `a::global:path` between ticks in the documentation
++  --> $DIR/doc.rs:11:15
++   |
++LL | /// Here be ::a::global:path.
++   |               ^^^^^^^^^^^^^^
++
++error: you should put `NotInCodeBlock` between ticks in the documentation
++  --> $DIR/doc.rs:12:22
++   |
++LL | /// That's not code ~NotInCodeBlock~.
++   |                      ^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:13:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:27:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:34:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:48:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:71:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `link_with_underscores` between ticks in the documentation
++  --> $DIR/doc.rs:75:22
++   |
++LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++   |                      ^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `inline_link2` between ticks in the documentation
++  --> $DIR/doc.rs:78:21
++   |
++LL | /// It can also be [inline_link2].
++   |                     ^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:88:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:96:8
++   |
++LL | /// ## CamelCaseThing
++   |        ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:99:7
++   |
++LL | /// # CamelCaseThing
++   |       ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:101:22
++   |
++LL | /// Not a title #897 CamelCaseThing
++   |                      ^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:102:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:109:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:122:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++  --> $DIR/doc.rs:133:43
++   |
++LL | /** E.g., serialization of an empty list: FooBar
++   |                                           ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++  --> $DIR/doc.rs:138:5
++   |
++LL | And BarQuz too.
++   |     ^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:139:1
++   |
++LL | be_sure_we_got_to_the_end_of_it
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++  --> $DIR/doc.rs:144:43
++   |
++LL | /** E.g., serialization of an empty list: FooBar
++   |                                           ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++  --> $DIR/doc.rs:149:5
++   |
++LL | And BarQuz too.
++   |     ^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:150:1
++   |
++LL | be_sure_we_got_to_the_end_of_it
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:161:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:188:13
++   |
++LL | /// Not ok: http://www.unicode.org
++   |             ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:189:13
++   |
++LL | /// Not ok: https://www.unicode.org
++   |             ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:190:13
++   |
++LL | /// Not ok: http://www.unicode.org/
++   |             ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:191:13
++   |
++LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `mycrate::Collection` between ticks in the documentation
++  --> $DIR/doc.rs:194:22
++   |
++LL | /// An iterator over mycrate::Collection's values.
++   |                      ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 31 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78e87bc69062a351c4e3bccbd5e773b8b277c443
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++//! This file tests for the `DOC_MARKDOWN` lint, specifically cases
++//! where ticks are unbalanced (see issue #6753).
++
++#![allow(dead_code)]
++#![warn(clippy::doc_markdown)]
++
++/// This is a doc comment with `unbalanced_tick marks and several words that
++/// should be `encompassed_by` tick marks because they `contain_underscores`.
++/// Because of the initial `unbalanced_tick` pair, the error message is
++/// very `confusing_and_misleading`.
++fn main() {}
++
++/// This paragraph has `unbalanced_tick marks and should stop_linting.
++///
++/// This paragraph is fine and should_be linted normally.
++///
++/// Double unbalanced backtick from ``here to here` should lint.
++///
++/// Double balanced back ticks ``start end`` is fine.
++fn multiple_paragraphs() {}
++
++/// ```
++/// // Unbalanced tick mark in code block shouldn't warn:
++/// `
++/// ```
++fn in_code_block() {}
++
++/// # `Fine`
++///
++/// ## not_fine
++///
++/// ### `unbalanced
++///
++/// - This `item has unbalanced tick marks
++/// - This item needs backticks_here
++fn other_markdown() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45ca34e2a8c8b34a2e6ede2509ad9367abac2709
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++error: backticks are unbalanced
++  --> $DIR/unbalanced_ticks.rs:7:1
++   |
++LL | / /// This is a doc comment with `unbalanced_tick marks and several words that
++LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`.
++LL | | /// Because of the initial `unbalanced_tick` pair, the error message is
++LL | | /// very `confusing_and_misleading`.
++   | |____________________________________^
++   |
++   = note: `-D clippy::doc-markdown` implied by `-D warnings`
++   = help: a backtick may be missing a pair
++
++error: backticks are unbalanced
++  --> $DIR/unbalanced_ticks.rs:13:1
++   |
++LL | /// This paragraph has `unbalanced_tick marks and should stop_linting.
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: a backtick may be missing a pair
++
++error: you should put `should_be` between ticks in the documentation
++  --> $DIR/unbalanced_ticks.rs:15:32
++   |
++LL | /// This paragraph is fine and should_be linted normally.
++   |                                ^^^^^^^^^
++
++error: backticks are unbalanced
++  --> $DIR/unbalanced_ticks.rs:17:1
++   |
++LL | /// Double unbalanced backtick from ``here to here` should lint.
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: a backtick may be missing a pair
++
++error: you should put `not_fine` between ticks in the documentation
++  --> $DIR/unbalanced_ticks.rs:30:8
++   |
++LL | /// ## not_fine
++   |        ^^^^^^^^
++
++error: backticks are unbalanced
++  --> $DIR/unbalanced_ticks.rs:32:1
++   |
++LL | /// ### `unbalanced
++   | ^^^^^^^^^^^^^^^^^^^
++   |
++   = help: a backtick may be missing a pair
++
++error: backticks are unbalanced
++  --> $DIR/unbalanced_ticks.rs:34:1
++   |
++LL | /// - This `item has unbalanced tick marks
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: a backtick may be missing a pair
++
++error: you should put `backticks_here` between ticks in the documentation
++  --> $DIR/unbalanced_ticks.rs:35:23
++   |
++LL | /// - This item needs backticks_here
++   |                       ^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 1368c5d79848032b423bb3f6520655a8e6567bf5,0000000000000000000000000000000000000000..787053fb00064c861a75c37410ae26767ab2322e
mode 100644,000000..100644
--- /dev/null
@@@ -1,165 -1,0 +1,185 @@@
 +// aux-build:proc_macro_derive.rs
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::field_reassign_with_default)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +#[macro_use]
 +extern crate macro_rules;
 +
 +// Don't lint on derives that derive `Default`
 +// See https://github.com/rust-lang/rust-clippy/issues/6545
 +#[derive(FieldReassignWithDefault)]
 +struct DerivedStruct;
 +
 +#[derive(Default)]
 +struct A {
 +    i: i32,
 +    j: i64,
 +}
 +
 +struct B {
 +    i: i32,
 +    j: i64,
 +}
 +
 +#[derive(Default)]
 +struct C {
 +    i: Vec<i32>,
 +    j: i64,
 +}
++
++#[derive(Default)]
++struct D {
++    a: Option<i32>,
++    b: Option<i32>,
++}
++
++macro_rules! m {
++    ($key:ident: $value:tt) => {{
++        let mut data = $crate::D::default();
++        data.$key = Some($value);
++        data
++    }};
++}
++
 +/// Implements .next() that returns a different number each time.
 +struct SideEffect(i32);
 +
 +impl SideEffect {
 +    fn new() -> SideEffect {
 +        SideEffect(0)
 +    }
 +    fn next(&mut self) -> i32 {
 +        self.0 += 1;
 +        self.0
 +    }
 +}
 +
 +fn main() {
 +    // wrong, produces first error in stderr
 +    let mut a: A = Default::default();
 +    a.i = 42;
 +
 +    // right
 +    let mut a: A = Default::default();
 +
 +    // right
 +    let a = A {
 +        i: 42,
 +        ..Default::default()
 +    };
 +
 +    // right
 +    let mut a: A = Default::default();
 +    if a.i == 0 {
 +        a.j = 12;
 +    }
 +
 +    // right
 +    let mut a: A = Default::default();
 +    let b = 5;
 +
 +    // right
 +    let mut b = 32;
 +    let mut a: A = Default::default();
 +    b = 2;
 +
 +    // right
 +    let b: B = B { i: 42, j: 24 };
 +
 +    // right
 +    let mut b: B = B { i: 42, j: 24 };
 +    b.i = 52;
 +
 +    // right
 +    let mut b = B { i: 15, j: 16 };
 +    let mut a: A = Default::default();
 +    b.i = 2;
 +
 +    // wrong, produces second error in stderr
 +    let mut a: A = Default::default();
 +    a.j = 43;
 +    a.i = 42;
 +
 +    // wrong, produces third error in stderr
 +    let mut a: A = Default::default();
 +    a.i = 42;
 +    a.j = 43;
 +    a.j = 44;
 +
 +    // wrong, produces fourth error in stderr
 +    let mut a = A::default();
 +    a.i = 42;
 +
 +    // wrong, but does not produce an error in stderr, because we can't produce a correct kind of
 +    // suggestion with current implementation
 +    let mut c: (i32, i32) = Default::default();
 +    c.0 = 42;
 +    c.1 = 21;
 +
 +    // wrong, produces the fifth error in stderr
 +    let mut a: A = Default::default();
 +    a.i = Default::default();
 +
 +    // wrong, produces the sixth error in stderr
 +    let mut a: A = Default::default();
 +    a.i = Default::default();
 +    a.j = 45;
 +
 +    // right, because an assignment refers to another field
 +    let mut x = A::default();
 +    x.i = 42;
 +    x.j = 21 + x.i as i64;
 +
 +    // right, we bail out if there's a reassignment to the same variable, since there is a risk of
 +    // side-effects affecting the outcome
 +    let mut x = A::default();
 +    let mut side_effect = SideEffect::new();
 +    x.i = side_effect.next();
 +    x.j = 2;
 +    x.i = side_effect.next();
 +
 +    // don't lint - some private fields
 +    let mut x = m::F::default();
 +    x.a = 1;
 +
 +    // don't expand macros in the suggestion (#6522)
 +    let mut a: C = C::default();
 +    a.i = vec![1];
 +
 +    // Don't lint in external macros
 +    field_reassign_with_default!();
 +
 +    // be sure suggestion is correct with generics
 +    let mut a: Wrapper<bool> = Default::default();
 +    a.i = true;
 +
 +    let mut a: WrapperMulti<i32, i64> = Default::default();
 +    a.i = 42;
++
++    // Don't lint in macros
++    m! {
++        a: 42
++    };
 +}
 +
 +mod m {
 +    #[derive(Default)]
 +    pub struct F {
 +        pub a: u64,
 +        b: u64,
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Wrapper<T> {
 +    i: T,
 +}
 +
 +#[derive(Default)]
 +struct WrapperMulti<T, U> {
 +    i: T,
 +    j: U,
 +}
index dd7c0360bb1e2bc40355e216bec4fad739615f43,0000000000000000000000000000000000000000..b56db08ec8a787f63c3edc31fde93178a0237982
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,111 @@@
-   --> $DIR/field_reassign_with_default.rs:48:5
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:47:5
++  --> $DIR/field_reassign_with_default.rs:63:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:88:5
++  --> $DIR/field_reassign_with_default.rs:62:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:87:5
++  --> $DIR/field_reassign_with_default.rs:103:5
 +   |
 +LL |     a.j = 43;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:93:5
++  --> $DIR/field_reassign_with_default.rs:102:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:92:5
++  --> $DIR/field_reassign_with_default.rs:108:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:99:5
++  --> $DIR/field_reassign_with_default.rs:107:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:98:5
++  --> $DIR/field_reassign_with_default.rs:114:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:109:5
++  --> $DIR/field_reassign_with_default.rs:113:5
 +   |
 +LL |     let mut a = A::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:108:5
++  --> $DIR/field_reassign_with_default.rs:124:5
 +   |
 +LL |     a.i = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:113:5
++  --> $DIR/field_reassign_with_default.rs:123:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:112:5
++  --> $DIR/field_reassign_with_default.rs:128:5
 +   |
 +LL |     a.i = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:135:5
++  --> $DIR/field_reassign_with_default.rs:127:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:134:5
++  --> $DIR/field_reassign_with_default.rs:150:5
 +   |
 +LL |     a.i = vec![1];
 +   |     ^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:142:5
++  --> $DIR/field_reassign_with_default.rs:149:5
 +   |
 +LL |     let mut a: C = C::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:141:5
++  --> $DIR/field_reassign_with_default.rs:157:5
 +   |
 +LL |     a.i = true;
 +   |     ^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
-   --> $DIR/field_reassign_with_default.rs:145:5
++  --> $DIR/field_reassign_with_default.rs:156:5
 +   |
 +LL |     let mut a: Wrapper<bool> = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
-   --> $DIR/field_reassign_with_default.rs:144:5
++  --> $DIR/field_reassign_with_default.rs:160:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
++  --> $DIR/field_reassign_with_default.rs:159:5
 +   |
 +LL |     let mut a: WrapperMulti<i32, i64> = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 9 previous errors
 +
index 23ce28d8e9be4c2238afe40f3a664419763305d0,0000000000000000000000000000000000000000..a5860aa49b3bb7f909aeee04e130285188fd0ae8
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,19 @@@
- #![allow(unused_imports)]
 +// run-rustfix
 +
++#![allow(unused_imports, clippy::needless_return)]
 +#![warn(clippy::filter_map_identity)]
 +
 +fn main() {
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.flatten();
 +
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.flatten();
 +
 +    use std::convert::identity;
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.flatten();
++
++    let iterator = vec![Some(1), None, Some(2)].into_iter();
++    let _ = iterator.flatten();
 +}
index e698df13eea47ef4ff3eaca9319a0a0b82b14ad5,0000000000000000000000000000000000000000..7e998b9cdf7010f4cd6e1fa84af582a3450dd6eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,19 @@@
- #![allow(unused_imports)]
 +// run-rustfix
 +
++#![allow(unused_imports, clippy::needless_return)]
 +#![warn(clippy::filter_map_identity)]
 +
 +fn main() {
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.filter_map(|x| x);
 +
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.filter_map(std::convert::identity);
 +
 +    use std::convert::identity;
 +    let iterator = vec![Some(1), None, Some(2)].into_iter();
 +    let _ = iterator.filter_map(identity);
++
++    let iterator = vec![Some(1), None, Some(2)].into_iter();
++    let _ = iterator.filter_map(|x| return x);
 +}
index 596a6320608c7c0acf8725c40c88c82461b20309,0000000000000000000000000000000000000000..43c9fdca4fbe0251b54e6e6c70f4ab2fe3873eb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,28 @@@
- error: called `filter_map(|x| x)` on an `Iterator`
++error: use of `filter_map` with an identity function
 +  --> $DIR/filter_map_identity.rs:8:22
 +   |
 +LL |     let _ = iterator.filter_map(|x| x);
 +   |                      ^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 +   |
 +   = note: `-D clippy::filter-map-identity` implied by `-D warnings`
 +
- error: called `filter_map(std::convert::identity)` on an `Iterator`
++error: use of `filter_map` with an identity function
 +  --> $DIR/filter_map_identity.rs:11:22
 +   |
 +LL |     let _ = iterator.filter_map(std::convert::identity);
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 +
- error: called `filter_map(std::convert::identity)` on an `Iterator`
++error: use of `filter_map` with an identity function
 +  --> $DIR/filter_map_identity.rs:15:22
 +   |
 +LL |     let _ = iterator.filter_map(identity);
 +   |                      ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 +
- error: aborting due to 3 previous errors
++error: use of `filter_map` with an identity function
++  --> $DIR/filter_map_identity.rs:18:22
++   |
++LL |     let _ = iterator.filter_map(|x| return x);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
++
++error: aborting due to 4 previous errors
 +
index dfe3bd47e1394b4517cdbc88ee1b288a86a0c036,0000000000000000000000000000000000000000..1f4b880ef5bcd8fa1cea502a8b9bd5efae1d7ba9
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,17 @@@
- #![allow(unused_imports)]
 +// run-rustfix
 +
++#![allow(unused_imports, clippy::needless_return)]
 +#![warn(clippy::flat_map_identity)]
 +
 +use std::convert;
 +
 +fn main() {
 +    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
 +    let _ = iterator.flatten();
 +
 +    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
 +    let _ = iterator.flatten();
++
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flatten();
 +}
index 393b95692554ca200ab9e5ac37f998e350f37d99,0000000000000000000000000000000000000000..de14a06d4e6b3377a40d277105d073005e68229a
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,17 @@@
- #![allow(unused_imports)]
 +// run-rustfix
 +
++#![allow(unused_imports, clippy::needless_return)]
 +#![warn(clippy::flat_map_identity)]
 +
 +use std::convert;
 +
 +fn main() {
 +    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
 +    let _ = iterator.flat_map(|x| x);
 +
 +    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
 +    let _ = iterator.flat_map(convert::identity);
++
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flat_map(|x| return x);
 +}
index e4686ae5a5493ca2d409d56eebb347d84e0530d8,0000000000000000000000000000000000000000..e776c9fdf512e2bda03316138086e2eabd4e2dcf
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,22 @@@
- error: called `flat_map(|x| x)` on an `Iterator`
++error: use of `flat_map` with an identity function
 +  --> $DIR/flat_map_identity.rs:10:22
 +   |
 +LL |     let _ = iterator.flat_map(|x| x);
 +   |                      ^^^^^^^^^^^^^^^ help: try: `flatten()`
 +   |
 +   = note: `-D clippy::flat-map-identity` implied by `-D warnings`
 +
- error: called `flat_map(std::convert::identity)` on an `Iterator`
++error: use of `flat_map` with an identity function
 +  --> $DIR/flat_map_identity.rs:13:22
 +   |
 +LL |     let _ = iterator.flat_map(convert::identity);
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
 +
- error: aborting due to 2 previous errors
++error: use of `flat_map` with an identity function
++  --> $DIR/flat_map_identity.rs:16:22
++   |
++LL |     let _ = iterator.flat_map(|x| return x);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
++
++error: aborting due to 3 previous errors
 +
index 0549886a1e8ec7ad8a52f497c1ff647033e63528,0000000000000000000000000000000000000000..e48451acfe8f5f4c67f76c65298e40b9a463e9f5
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,146 @@@
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:13:14
 +   |
 +LL |         _ => 0, //~ ERROR match arms have same body
 +   |              ^
 +   |
 +   = note: `-D clippy::match-same-arms` implied by `-D warnings`
 +note: same as this
 +  --> $DIR/match_same_arms.rs:11:19
 +   |
 +LL |         Abc::A => 0,
 +   |                   ^
 +note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it
 +  --> $DIR/match_same_arms.rs:11:19
 +   |
 +LL |         Abc::A => 0,
 +   |                   ^
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:18:20
 +   |
 +LL |         (.., 3) => 42, //~ ERROR match arms have same body
 +   |                    ^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:17:23
 +   |
 +LL |         (1, .., 3) => 42,
 +   |                       ^^
 +help: consider refactoring into `(1, .., 3) | (.., 3)`
 +  --> $DIR/match_same_arms.rs:17:9
 +   |
 +LL |         (1, .., 3) => 42,
 +   |         ^^^^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:24:15
 +   |
 +LL |         51 => 1, //~ ERROR match arms have same body
 +   |               ^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:23:15
 +   |
 +LL |         42 => 1,
 +   |               ^
 +help: consider refactoring into `42 | 51`
 +  --> $DIR/match_same_arms.rs:23:9
 +   |
 +LL |         42 => 1,
 +   |         ^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:26:15
 +   |
 +LL |         52 => 2, //~ ERROR match arms have same body
 +   |               ^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:25:15
 +   |
 +LL |         41 => 2,
 +   |               ^
 +help: consider refactoring into `41 | 52`
 +  --> $DIR/match_same_arms.rs:25:9
 +   |
 +LL |         41 => 2,
 +   |         ^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:32:14
 +   |
 +LL |         2 => 2, //~ ERROR 2nd matched arms have same body
 +   |              ^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:31:14
 +   |
 +LL |         1 => 2,
 +   |              ^
 +help: consider refactoring into `1 | 2`
 +  --> $DIR/match_same_arms.rs:31:9
 +   |
 +LL |         1 => 2,
 +   |         ^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:33:14
 +   |
 +LL |         3 => 2, //~ ERROR 3rd matched arms have same body
 +   |              ^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:31:14
 +   |
 +LL |         1 => 2,
 +   |              ^
 +help: consider refactoring into `1 | 3`
 +  --> $DIR/match_same_arms.rs:31:9
 +   |
 +LL |         1 => 2,
 +   |         ^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:33:14
 +   |
 +LL |         3 => 2, //~ ERROR 3rd matched arms have same body
 +   |              ^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:32:14
 +   |
 +LL |         2 => 2, //~ ERROR 2nd matched arms have same body
 +   |              ^
 +help: consider refactoring into `2 | 3`
 +  --> $DIR/match_same_arms.rs:32:9
 +   |
 +LL |         2 => 2, //~ ERROR 2nd matched arms have same body
 +   |         ^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms.rs:50:55
 +   |
 +LL |                 CommandInfo::External { name, .. } => name.to_string(),
 +   |                                                       ^^^^^^^^^^^^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms.rs:49:54
 +   |
 +LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
 +   |                                                      ^^^^^^^^^^^^^^^^
 +help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }`
 +  --> $DIR/match_same_arms.rs:49:17
 +   |
 +LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: aborting due to 8 previous errors
 +
index 430021a0f7f5acddf88545d543bef5f20d90b077,0000000000000000000000000000000000000000..e1ed12f9370877b8b0d59411ce345529cf64b84e
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,188 @@@
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:20:14
 +   |
 +LL |           _ => {
 +   |  ______________^
 +LL | |             //~ ERROR match arms have same body
 +LL | |             foo();
 +LL | |             let mut a = 42 + [23].len() as i32;
 +...  |
 +LL | |             a
 +LL | |         },
 +   | |_________^
 +   |
 +   = note: `-D clippy::match-same-arms` implied by `-D warnings`
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:11:15
 +   |
 +LL |           42 => {
 +   |  _______________^
 +LL | |             foo();
 +LL | |             let mut a = 42 + [23].len() as i32;
 +LL | |             if true {
 +...  |
 +LL | |             a
 +LL | |         },
 +   | |_________^
 +note: `42` has the same arm body as the `_` wildcard, consider removing it
 +  --> $DIR/match_same_arms2.rs:11:15
 +   |
 +LL |           42 => {
 +   |  _______________^
 +LL | |             foo();
 +LL | |             let mut a = 42 + [23].len() as i32;
 +LL | |             if true {
 +...  |
 +LL | |             a
 +LL | |         },
 +   | |_________^
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:34:15
 +   |
 +LL |         51 => foo(), //~ ERROR match arms have same body
 +   |               ^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:33:15
 +   |
 +LL |         42 => foo(),
 +   |               ^^^^^
 +help: consider refactoring into `42 | 51`
 +  --> $DIR/match_same_arms2.rs:33:9
 +   |
 +LL |         42 => foo(),
 +   |         ^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:40:17
 +   |
 +LL |         None => 24, //~ ERROR match arms have same body
 +   |                 ^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:39:20
 +   |
 +LL |         Some(_) => 24,
 +   |                    ^^
 +help: consider refactoring into `Some(_) | None`
 +  --> $DIR/match_same_arms2.rs:39:9
 +   |
 +LL |         Some(_) => 24,
 +   |         ^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:62:28
 +   |
 +LL |         (None, Some(a)) => bar(a), //~ ERROR match arms have same body
 +   |                            ^^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:61:28
 +   |
 +LL |         (Some(a), None) => bar(a),
 +   |                            ^^^^^^
 +help: consider refactoring into `(Some(a), None) | (None, Some(a))`
 +  --> $DIR/match_same_arms2.rs:61:9
 +   |
 +LL |         (Some(a), None) => bar(a),
 +   |         ^^^^^^^^^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:68:26
 +   |
 +LL |         (.., Some(a)) => bar(a), //~ ERROR match arms have same body
 +   |                          ^^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:67:26
 +   |
 +LL |         (Some(a), ..) => bar(a),
 +   |                          ^^^^^^
 +help: consider refactoring into `(Some(a), ..) | (.., Some(a))`
 +  --> $DIR/match_same_arms2.rs:67:9
 +   |
 +LL |         (Some(a), ..) => bar(a),
 +   |         ^^^^^^^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:102:29
 +   |
 +LL |         (Ok(_), Some(x)) => println!("ok {}", x),
 +   |                             ^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:101:29
 +   |
 +LL |         (Ok(x), Some(_)) => println!("ok {}", x),
 +   |                             ^^^^^^^^^^^^^^^^^^^^
 +help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))`
 +  --> $DIR/match_same_arms2.rs:101:9
 +   |
 +LL |         (Ok(x), Some(_)) => println!("ok {}", x),
 +   |         ^^^^^^^^^^^^^^^^
++   = help: ...or consider changing the match arm bodies
 +   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:117:18
 +   |
 +LL |         Ok(_) => println!("ok"),
 +   |                  ^^^^^^^^^^^^^^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:116:18
 +   |
 +LL |         Ok(3) => println!("ok"),
 +   |                  ^^^^^^^^^^^^^^
 +help: consider refactoring into `Ok(3) | Ok(_)`
 +  --> $DIR/match_same_arms2.rs:116:9
 +   |
 +LL |         Ok(3) => println!("ok"),
 +   |         ^^^^^
++   = help: ...or consider changing the match arm bodies
 +   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: this `match` has identical arm bodies
 +  --> $DIR/match_same_arms2.rs:144:14
 +   |
 +LL |           1 => {
 +   |  ______________^
 +LL | |             empty!(0);
 +LL | |         },
 +   | |_________^
 +   |
 +note: same as this
 +  --> $DIR/match_same_arms2.rs:141:14
 +   |
 +LL |           0 => {
 +   |  ______________^
 +LL | |             empty!(0);
 +LL | |         },
 +   | |_________^
 +help: consider refactoring into `0 | 1`
 +  --> $DIR/match_same_arms2.rs:141:9
 +   |
 +LL |         0 => {
 +   |         ^
++   = help: ...or consider changing the match arm bodies
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_same_arms2.rs:162:16
 +   |
 +LL |       let _ans = match x {
 +   |  ________________^
 +LL | |         E::A => false,
 +LL | |         E::B => false,
 +LL | |         _ => true,
 +LL | |     };
 +   | |_____^ help: try this: `!matches!(x, E::A | E::B)`
 +   |
 +   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 +
 +error: aborting due to 9 previous errors
 +
index 31b743f7a18d73f53457e986d5572269a2746385,0000000000000000000000000000000000000000..e675c183ea7117c646b99e04438c524f07fec3dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,134 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_wildcard_for_single_variants)]
 +#![allow(dead_code)]
 +
 +enum Foo {
 +    A,
 +    B,
 +    C,
 +}
 +
 +enum Color {
 +    Red,
 +    Green,
 +    Blue,
 +    Rgb(u8, u8, u8),
 +}
 +impl Color {
 +    fn f(self) {
 +        match self {
 +            Self::Red => (),
 +            Self::Green => (),
 +            Self::Blue => (),
 +            Self::Rgb(..) => (),
 +        };
 +    }
 +}
 +
 +fn main() {
 +    let f = Foo::A;
 +    match f {
 +        Foo::A => {},
 +        Foo::B => {},
 +        Foo::C => {},
 +    }
 +
 +    let color = Color::Red;
 +
 +    // check exhaustive bindings
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(_r, _g, _b) => {},
 +        Color::Blue => {},
 +    }
 +
 +    // check exhaustive wild
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(..) => {},
 +        Color::Blue => {},
 +    }
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(_, _, _) => {},
 +        Color::Blue => {},
 +    }
 +
 +    // shouldn't lint as there is one missing variant
 +    // and one that isn't exhaustively covered
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(255, _, _) => {},
 +        _ => {},
 +    }
 +
 +    // References shouldn't change anything
 +    match &color {
 +        &Color::Red => (),
 +        Color::Green => (),
 +        &Color::Rgb(..) => (),
 +        Color::Blue => (),
 +    }
 +
 +    use self::Color as C;
 +
 +    match color {
 +        C::Red => (),
 +        C::Green => (),
 +        C::Rgb(..) => (),
 +        C::Blue => (),
 +    }
 +
 +    match color {
 +        C::Red => (),
 +        Color::Green => (),
 +        Color::Rgb(..) => (),
 +        Color::Blue => (),
 +    }
 +
 +    match Some(0) {
 +        Some(0) => 0,
 +        Some(_) => 1,
 +        _ => 2,
 +    };
 +
 +    #[non_exhaustive]
 +    enum Bar {
 +        A,
 +        B,
 +        C,
 +    }
 +    match Bar::A {
 +        Bar::A => (),
 +        Bar::B => (),
 +        _ => (),
 +    };
 +
 +    //#6984
 +    {
 +        #![allow(clippy::manual_non_exhaustive)]
 +        pub enum Enum {
 +            A,
 +            B,
++            C,
 +            #[doc(hidden)]
 +            __Private,
 +        }
++        match Enum::A {
++            Enum::A => (),
++            Enum::B => (),
++            Enum::C => (),
++            _ => (),
++        }
 +        match Enum::A {
 +            Enum::A => (),
 +            Enum::B => (),
 +            _ => (),
 +        }
 +    }
 +}
index d19e6ab7eb2e1ee8aa588f075550da007be81a22,0000000000000000000000000000000000000000..38c3ffc00c71b4bc8cad9251ad1aa4b674899203
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,134 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_wildcard_for_single_variants)]
 +#![allow(dead_code)]
 +
 +enum Foo {
 +    A,
 +    B,
 +    C,
 +}
 +
 +enum Color {
 +    Red,
 +    Green,
 +    Blue,
 +    Rgb(u8, u8, u8),
 +}
 +impl Color {
 +    fn f(self) {
 +        match self {
 +            Self::Red => (),
 +            Self::Green => (),
 +            Self::Blue => (),
 +            _ => (),
 +        };
 +    }
 +}
 +
 +fn main() {
 +    let f = Foo::A;
 +    match f {
 +        Foo::A => {},
 +        Foo::B => {},
 +        _ => {},
 +    }
 +
 +    let color = Color::Red;
 +
 +    // check exhaustive bindings
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(_r, _g, _b) => {},
 +        _ => {},
 +    }
 +
 +    // check exhaustive wild
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(..) => {},
 +        _ => {},
 +    }
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(_, _, _) => {},
 +        _ => {},
 +    }
 +
 +    // shouldn't lint as there is one missing variant
 +    // and one that isn't exhaustively covered
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Rgb(255, _, _) => {},
 +        _ => {},
 +    }
 +
 +    // References shouldn't change anything
 +    match &color {
 +        &Color::Red => (),
 +        Color::Green => (),
 +        &Color::Rgb(..) => (),
 +        &_ => (),
 +    }
 +
 +    use self::Color as C;
 +
 +    match color {
 +        C::Red => (),
 +        C::Green => (),
 +        C::Rgb(..) => (),
 +        _ => (),
 +    }
 +
 +    match color {
 +        C::Red => (),
 +        Color::Green => (),
 +        Color::Rgb(..) => (),
 +        _ => (),
 +    }
 +
 +    match Some(0) {
 +        Some(0) => 0,
 +        Some(_) => 1,
 +        _ => 2,
 +    };
 +
 +    #[non_exhaustive]
 +    enum Bar {
 +        A,
 +        B,
 +        C,
 +    }
 +    match Bar::A {
 +        Bar::A => (),
 +        Bar::B => (),
 +        _ => (),
 +    };
 +
 +    //#6984
 +    {
 +        #![allow(clippy::manual_non_exhaustive)]
 +        pub enum Enum {
 +            A,
 +            B,
++            C,
 +            #[doc(hidden)]
 +            __Private,
 +        }
++        match Enum::A {
++            Enum::A => (),
++            Enum::B => (),
++            Enum::C => (),
++            _ => (),
++        }
 +        match Enum::A {
 +            Enum::A => (),
 +            Enum::B => (),
 +            _ => (),
 +        }
 +    }
 +}
index 8d6a259c7e385f307ada794ea2e9ca0f06f01853,0000000000000000000000000000000000000000..a8460b06ca6038ef3b976e9b6867ca33049cfcc1
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,28 @@@
-    = note: `#[deny(clippy::mutable_key_type)]` on by default
 +error: mutable key type
 +  --> $DIR/mut_key.rs:27:32
 +   |
 +LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
++   = note: `-D clippy::mutable-key-type` implied by `-D warnings`
 +
 +error: mutable key type
 +  --> $DIR/mut_key.rs:27:72
 +   |
 +LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
 +   |                                                                        ^^^^^^^^^^^^
 +
 +error: mutable key type
 +  --> $DIR/mut_key.rs:28:5
 +   |
 +LL |     let _other: HashMap<Key, bool> = HashMap::new();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: mutable key type
 +  --> $DIR/mut_key.rs:47:22
 +   |
 +LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 4 previous errors
 +
index 0abe2cca26757325fc4f7d16c45d9d07ea5912f2,0000000000000000000000000000000000000000..79ba7402f1f49285a79b0de83952e5f46c272333
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,99 @@@
 +#![warn(clippy::semicolon_if_nothing_returned)]
 +#![feature(label_break_value)]
 +
 +fn get_unit() {}
 +
 +// the functions below trigger the lint
 +fn main() {
 +    println!("Hello")
 +}
 +
 +fn hello() {
 +    get_unit()
 +}
 +
 +fn basic101(x: i32) {
 +    let y: i32;
 +    y = x + 1
 +}
 +
++#[rustfmt::skip]
++fn closure_error() {
++    let _d = || {
++        hello()
++    };
++}
++
++#[rustfmt::skip]
++fn unsafe_checks_error() {
++    use std::mem::MaybeUninit;
++    use std::ptr;
++
++    let mut s = MaybeUninit::<String>::uninit();
++    let _d = || unsafe { 
++        ptr::drop_in_place(s.as_mut_ptr()) 
++    };
++}
++
 +// this is fine
 +fn print_sum(a: i32, b: i32) {
 +    println!("{}", a + b);
 +    assert_eq!(true, false);
 +}
 +
 +fn foo(x: i32) {
 +    let y: i32;
 +    if x < 1 {
 +        y = 4;
 +    } else {
 +        y = 5;
 +    }
 +}
 +
 +fn bar(x: i32) {
 +    let y: i32;
 +    match x {
 +        1 => y = 4,
 +        _ => y = 32,
 +    }
 +}
 +
 +fn foobar(x: i32) {
 +    let y: i32;
 +    'label: {
 +        y = x + 1;
 +    }
 +}
 +
 +fn loop_test(x: i32) {
 +    let y: i32;
 +    for &ext in &["stdout", "stderr", "fixed"] {
 +        println!("{}", ext);
 +    }
 +}
++
++fn closure() {
++    let _d = || hello();
++}
++
++#[rustfmt::skip]
++fn closure_block() {
++    let _d = || { hello() };
++}
++
++unsafe fn some_unsafe_op() {}
++unsafe fn some_other_unsafe_fn() {}
++
++fn do_something() {
++    unsafe { some_unsafe_op() };
++
++    unsafe { some_other_unsafe_fn() };
++}
++
++fn unsafe_checks() {
++    use std::mem::MaybeUninit;
++    use std::ptr;
++
++    let mut s = MaybeUninit::<String>::uninit();
++    let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
++}
index b73f89675383f12d53aeb4298898502c443af5b0,0000000000000000000000000000000000000000..e88ebe2ad35f0bcacf64ed5f8ee3bc57e1227ae7
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,34 @@@
- error: aborting due to 3 previous errors
 +error: consider adding a `;` to the last statement for consistent formatting
 +  --> $DIR/semicolon_if_nothing_returned.rs:8:5
 +   |
 +LL |     println!("Hello")
 +   |     ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
 +   |
 +   = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
 +
 +error: consider adding a `;` to the last statement for consistent formatting
 +  --> $DIR/semicolon_if_nothing_returned.rs:12:5
 +   |
 +LL |     get_unit()
 +   |     ^^^^^^^^^^ help: add a `;` here: `get_unit();`
 +
 +error: consider adding a `;` to the last statement for consistent formatting
 +  --> $DIR/semicolon_if_nothing_returned.rs:17:5
 +   |
 +LL |     y = x + 1
 +   |     ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
 +
++error: consider adding a `;` to the last statement for consistent formatting
++  --> $DIR/semicolon_if_nothing_returned.rs:23:9
++   |
++LL |         hello()
++   |         ^^^^^^^ help: add a `;` here: `hello();`
++
++error: consider adding a `;` to the last statement for consistent formatting
++  --> $DIR/semicolon_if_nothing_returned.rs:34:9
++   |
++LL |         ptr::drop_in_place(s.as_mut_ptr()) 
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
++
++error: aborting due to 5 previous errors
 +
index 388fc7400820947ab912c088099d94e20391995d,0000000000000000000000000000000000000000..63fc9ecb79a987923cc00f0cb5589e7b5020afa4
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
-    = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
 +error: suspicious use of binary operator in `Add` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:13:20
 +   |
 +LL |         Foo(self.0 - other.0)
 +   |                    ^
 +   |
 +   = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
 +
 +error: suspicious use of binary operator in `AddAssign` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:19:23
 +   |
 +LL |         *self = *self - other;
 +   |                       ^
 +   |
++   = note: `-D clippy::suspicious-op-assign-impl` implied by `-D warnings`
 +
 +error: suspicious use of binary operator in `MulAssign` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:32:16
 +   |
 +LL |         self.0 /= other.0;
 +   |                ^^
 +
 +error: suspicious use of binary operator in `Rem` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:70:20
 +   |
 +LL |         Foo(self.0 / other.0)
 +   |                    ^
 +
 +error: suspicious use of binary operator in `BitAnd` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:78:20
 +   |
 +LL |         Foo(self.0 | other.0)
 +   |                    ^
 +
 +error: suspicious use of binary operator in `BitOr` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:86:20
 +   |
 +LL |         Foo(self.0 ^ other.0)
 +   |                    ^
 +
 +error: suspicious use of binary operator in `BitXor` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:94:20
 +   |
 +LL |         Foo(self.0 & other.0)
 +   |                    ^
 +
 +error: suspicious use of binary operator in `Shl` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:102:20
 +   |
 +LL |         Foo(self.0 >> other.0)
 +   |                    ^^
 +
 +error: suspicious use of binary operator in `Shr` impl
 +  --> $DIR/suspicious_arithmetic_impl.rs:110:20
 +   |
 +LL |         Foo(self.0 << other.0)
 +   |                    ^^
 +
 +error: aborting due to 9 previous errors
 +
index 7fbce58a82f8e340e7d1ddc76eeb65732de23e7d,0000000000000000000000000000000000000000..bda0f2c47cdd525a97a62f5e080334dc20e627c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,40 @@@
- #![allow(clippy::no_effect, clippy::unnecessary_operation)]
 +// run-rustfix
 +
 +#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)]
 +
 +fn main() {
 +    // casting integer literal to float is unnecessary
 +    100_f32;
 +    100_f64;
 +    100_f64;
 +    let _ = -100_f32;
 +    let _ = -100_f64;
 +    let _ = -100_f64;
 +    100_f32;
 +    100_f64;
 +    // Should not trigger
 +    #[rustfmt::skip]
 +    let v = vec!(1);
 +    &v as &[i32];
 +    0x10 as f32;
 +    0o10 as f32;
 +    0b10 as f32;
 +    0x11 as f64;
 +    0o11 as f64;
 +    0b11 as f64;
 +
 +    1_u32;
 +    0x10_i32;
 +    0b10_usize;
 +    0o73_u16;
 +    1_000_000_000_u32;
 +
 +    1.0_f64;
 +    0.5_f32;
 +
 +    1.0 as u16;
 +
 +    let _ = -1_i32;
 +    let _ = -1.0_f32;
 +}
index a71363ea4d265ac86a4aaf3081fdc5aabdd62536,0000000000000000000000000000000000000000..f7a4f2a5988fd42c38fc5dca1c255a7a556e07ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,40 @@@
- #![allow(clippy::no_effect, clippy::unnecessary_operation)]
 +// run-rustfix
 +
 +#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)]
 +
 +fn main() {
 +    // casting integer literal to float is unnecessary
 +    100 as f32;
 +    100 as f64;
 +    100_i32 as f64;
 +    let _ = -100 as f32;
 +    let _ = -100 as f64;
 +    let _ = -100_i32 as f64;
 +    100. as f32;
 +    100. as f64;
 +    // Should not trigger
 +    #[rustfmt::skip]
 +    let v = vec!(1);
 +    &v as &[i32];
 +    0x10 as f32;
 +    0o10 as f32;
 +    0b10 as f32;
 +    0x11 as f64;
 +    0o11 as f64;
 +    0b11 as f64;
 +
 +    1 as u32;
 +    0x10 as i32;
 +    0b10 as usize;
 +    0o73 as u16;
 +    1_000_000_000 as u32;
 +
 +    1.0 as f64;
 +    0.5 as f32;
 +
 +    1.0 as u16;
 +
 +    let _ = -1 as i32;
 +    let _ = -1.0 as f32;
 +}
index 631da6fe066dde1703ee467b7d7e56a837e44d54,0000000000000000000000000000000000000000..e2c28542efc76e38c89aec06fc9bf7d670800dd6
mode 100644,000000..100644
--- /dev/null
@@@ -1,494 -1,0 +1,517 @@@
 +// run-rustfix
 +// edition:2018
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code)]
 +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo {}
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo {}
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            Self(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo {}
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo {}
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Self {
 +                    Self { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Self::B(42);
 +            let _ = Self::C { field: true };
 +            let _ = Self::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A {}
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            Self::fun_1();
 +            Self::A;
 +
 +            Self {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct {}
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            Self::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S {}
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> Self {
 +            Self {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[Self::A..Self::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Self {
 +            Self { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo {}
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo {}
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B {}
 +        pub struct C {}
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            Self::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
++
++mod self_is_ty_param {
++    trait Trait {
++        type Type;
++        type Hi;
++
++        fn test();
++    }
++
++    impl<I> Trait for I
++    where
++        I: Iterator,
++        I::Item: Trait, // changing this to Self would require <Self as Iterator>
++    {
++        type Type = I;
++        type Hi = I::Item;
++
++        fn test() {
++            let _: I::Item;
++            let _: I; // this could lint, but is questionable
++        }
++    }
++}
index 7a10d755faa18e0ad96c21939b2694bc6c3ae27d,0000000000000000000000000000000000000000..3cd99b9f5cd88775aaa07a061c5c8092fd852b96
mode 100644,000000..100644
--- /dev/null
@@@ -1,494 -1,0 +1,517 @@@
-             Foo { value }
 +// run-rustfix
 +// edition:2018
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code)]
 +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo {}
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +        fn test() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo {}
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            TS(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo {}
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo {}
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Bar {
 +                    Bar { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Enum::B(42);
 +            let _ = Enum::C { field: true };
 +            let _ = Enum::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A {}
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            nested::A::fun_1();
 +            nested::A::A;
 +
 +            nested::A {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct {}
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            TestStruct::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S {}
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> S {
 +            S {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[S::A..S::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Foo<T> {
++            Foo::<T> { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo {}
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo {}
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B {}
 +        pub struct C {}
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            A::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            S2::new()
 +        }
 +    }
 +}
++
++mod self_is_ty_param {
++    trait Trait {
++        type Type;
++        type Hi;
++
++        fn test();
++    }
++
++    impl<I> Trait for I
++    where
++        I: Iterator,
++        I::Item: Trait, // changing this to Self would require <Self as Iterator>
++    {
++        type Type = I;
++        type Hi = I::Item;
++
++        fn test() {
++            let _: I::Item;
++            let _: I; // this could lint, but is questionable
++        }
++    }
++}
index cf6222c9b453235e2eb8d068495e7d6843da097a,0000000000000000000000000000000000000000..6ac26c9e5a9cece3a876924c17595799c6efca9d
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,172 @@@
- LL |             Foo { value }
-    |             ^^^ help: use the applicable keyword: `Self`
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:18:21
 +   |
 +LL |         fn new() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +   = note: `-D clippy::use-self` implied by `-D warnings`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:19:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:21:22
 +   |
 +LL |         fn test() -> Foo {
 +   |                      ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:22:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:27:25
 +   |
 +LL |         fn default() -> Foo {
 +   |                         ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:28:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:93:24
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                        ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:93:55
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                                                       ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:108:13
 +   |
 +LL |             TS(0)
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:143:29
 +   |
 +LL |                 fn bar() -> Bar {
 +   |                             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:144:21
 +   |
 +LL |                     Bar { foo: Foo {} }
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:155:21
 +   |
 +LL |         fn baz() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:156:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:173:21
 +   |
 +LL |             let _ = Enum::B(42);
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:174:21
 +   |
 +LL |             let _ = Enum::C { field: true };
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:175:21
 +   |
 +LL |             let _ = Enum::A;
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:217:13
 +   |
 +LL |             nested::A::fun_1();
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:218:13
 +   |
 +LL |             nested::A::A;
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:220:13
 +   |
 +LL |             nested::A {};
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:239:13
 +   |
 +LL |             TestStruct::from_something()
 +   |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:253:25
 +   |
 +LL |         async fn g() -> S {
 +   |                         ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:254:13
 +   |
 +LL |             S {}
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:258:16
 +   |
 +LL |             &p[S::A..S::B]
 +   |                ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:258:22
 +   |
 +LL |             &p[S::A..S::B]
 +   |                      ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:281:29
 +   |
 +LL |         fn foo(value: T) -> Foo<T> {
 +   |                             ^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:282:13
 +   |
++LL |             Foo::<T> { value }
++   |             ^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:454:13
 +   |
 +LL |             A::new::<submod::B>(submod::B {})
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:491:13
 +   |
 +LL |             S2::new()
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: aborting due to 28 previous errors
 +
index da35f2e5c1bbe34dff67105e692626a722c99368,0000000000000000000000000000000000000000..318f9c2dceb641a206a821a81e96473648e9551d
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,78 @@@
 +// run-rustfix
++#![allow(clippy::nonstandard_macro_braces)]
 +#![warn(clippy::useless_vec)]
 +
 +#[derive(Debug)]
 +struct NonCopy;
 +
 +fn on_slice(_: &[u8]) {}
 +
 +fn on_mut_slice(_: &mut [u8]) {}
 +
 +#[allow(clippy::ptr_arg)]
 +fn on_vec(_: &Vec<u8>) {}
 +
 +fn on_mut_vec(_: &mut Vec<u8>) {}
 +
 +struct Line {
 +    length: usize,
 +}
 +
 +impl Line {
 +    fn length(&self) -> usize {
 +        self.length
 +    }
 +}
 +
 +fn main() {
 +    on_slice(&[]);
 +    on_slice(&[]);
 +    on_mut_slice(&mut []);
 +
 +    on_slice(&[1, 2]);
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut [1, 2]);
 +
 +    on_slice(&[1, 2]);
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut [1, 2]);
 +    #[rustfmt::skip]
 +    on_slice(&[1, 2]);
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut [1, 2]);
 +
 +    on_slice(&[1; 2]);
 +    on_slice(&[1; 2]);
 +    on_mut_slice(&mut [1; 2]);
 +
 +    on_vec(&vec![]);
 +    on_vec(&vec![1, 2]);
 +    on_vec(&vec![1; 2]);
 +    on_mut_vec(&mut vec![]);
 +    on_mut_vec(&mut vec![1, 2]);
 +    on_mut_vec(&mut vec![1; 2]);
 +
 +    // Now with non-constant expressions
 +    let line = Line { length: 2 };
 +
 +    on_slice(&vec![2; line.length]);
 +    on_slice(&vec![2; line.length()]);
 +    on_mut_slice(&mut vec![2; line.length]);
 +    on_mut_slice(&mut vec![2; line.length()]);
 +
 +    for a in &[1, 2, 3] {
 +        println!("{:?}", a);
 +    }
 +
 +    for a in vec![NonCopy, NonCopy] {
 +        println!("{:?}", a);
 +    }
 +
 +    on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
 +    on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
 +
 +    // Ok
 +    for a in vec![1; 201] {
 +        println!("{:?}", a);
 +    }
 +}
index e9ed83e5c5aef2e3a4613bbbb1d9337a81852b04,0000000000000000000000000000000000000000..d7673ce3e6437dbf7422cc218c5e17084afb857d
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,78 @@@
 +// run-rustfix
++#![allow(clippy::nonstandard_macro_braces)]
 +#![warn(clippy::useless_vec)]
 +
 +#[derive(Debug)]
 +struct NonCopy;
 +
 +fn on_slice(_: &[u8]) {}
 +
 +fn on_mut_slice(_: &mut [u8]) {}
 +
 +#[allow(clippy::ptr_arg)]
 +fn on_vec(_: &Vec<u8>) {}
 +
 +fn on_mut_vec(_: &mut Vec<u8>) {}
 +
 +struct Line {
 +    length: usize,
 +}
 +
 +impl Line {
 +    fn length(&self) -> usize {
 +        self.length
 +    }
 +}
 +
 +fn main() {
 +    on_slice(&vec![]);
 +    on_slice(&[]);
 +    on_mut_slice(&mut vec![]);
 +
 +    on_slice(&vec![1, 2]);
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut vec![1, 2]);
 +
 +    on_slice(&vec![1, 2]);
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut vec![1, 2]);
 +    #[rustfmt::skip]
 +    on_slice(&vec!(1, 2));
 +    on_slice(&[1, 2]);
 +    on_mut_slice(&mut vec![1, 2]);
 +
 +    on_slice(&vec![1; 2]);
 +    on_slice(&[1; 2]);
 +    on_mut_slice(&mut vec![1; 2]);
 +
 +    on_vec(&vec![]);
 +    on_vec(&vec![1, 2]);
 +    on_vec(&vec![1; 2]);
 +    on_mut_vec(&mut vec![]);
 +    on_mut_vec(&mut vec![1, 2]);
 +    on_mut_vec(&mut vec![1; 2]);
 +
 +    // Now with non-constant expressions
 +    let line = Line { length: 2 };
 +
 +    on_slice(&vec![2; line.length]);
 +    on_slice(&vec![2; line.length()]);
 +    on_mut_slice(&mut vec![2; line.length]);
 +    on_mut_slice(&mut vec![2; line.length()]);
 +
 +    for a in vec![1, 2, 3] {
 +        println!("{:?}", a);
 +    }
 +
 +    for a in vec![NonCopy, NonCopy] {
 +        println!("{:?}", a);
 +    }
 +
 +    on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
 +    on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack`
 +
 +    // Ok
 +    for a in vec![1; 201] {
 +        println!("{:?}", a);
 +    }
 +}
index c3e2cf0c4a4bd485e8a869e3a1b7f140eacdc9e3,0000000000000000000000000000000000000000..52e80ceee83cf31439c07cfff967fc46a03e09fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,328 -1,0 +1,342 @@@
 +// run-rustfix
 +
 +#![warn(clippy::while_let_on_iterator)]
 +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
 +
 +fn base() {
 +    let mut iter = 1..20;
 +    for x in iter {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    for x in iter {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    for _ in iter {}
 +
 +    let mut iter = 1..20;
 +    while let None = iter.next() {} // this is fine (if nonsensical)
 +
 +    let mut iter = 1..20;
 +    if let Some(x) = iter.next() {
 +        // also fine
 +        println!("{}", x)
 +    }
 +
 +    // the following shouldn't warn because it can't be written with a for loop
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next())
 +    }
 +
 +    // neither can this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next());
 +    }
 +
 +    // or this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        iter = 1..20;
 +    }
 +}
 +
 +// Issue #1188
 +fn refutable() {
 +    let a = [42, 1337];
 +    let mut b = a.iter();
 +
 +    // consume all the 42s
 +    while let Some(&42) = b.next() {}
 +
 +    let a = [(1, 2, 3)];
 +    let mut b = a.iter();
 +
 +    while let Some(&(1, 2, 3)) = b.next() {}
 +
 +    let a = [Some(42)];
 +    let mut b = a.iter();
 +
 +    while let Some(&None) = b.next() {}
 +
 +    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
 +    for &42 in b {}
 +    for &(1, 2, 3) in b {}
 +    for &Option::None in b.next() {}
 +    // */
 +}
 +
 +fn refutable2() {
 +    // Issue 3780
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.windows(2);
 +        while let Some([x, y]) = it.next() {
 +            println!("x: {}", x);
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([x, ..]) = it.next() {
 +            println!("x: {}", x);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([.., y]) = it.next() {
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        for [..] in it {}
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some([1]) = it.next() {}
 +
 +        let mut it = v.iter();
 +        for [_x] in it {}
 +    }
 +
 +    // binding
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter();
 +        while let Some(x @ 1) = it.next() {
 +            println!("{}", x);
 +        }
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        for x @ [_] in it {
 +            println!("{:?}", x);
 +        }
 +    }
 +
 +    // false negative
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter().map(Some);
 +        while let Some(Some(_) | None) = it.next() {
 +            println!("1");
 +        }
 +    }
 +}
 +
 +fn nested_loops() {
 +    let a = [42, 1337];
 +
 +    loop {
 +        let mut y = a.iter();
 +        for _ in y {
 +            // use a for loop here
 +        }
 +    }
 +}
 +
 +fn issue1121() {
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(&value) = values.iter().next() {
 +        values.remove(&value);
 +    }
 +}
 +
 +fn issue2965() {
 +    // This should not cause an ICE
 +
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {}
 +}
 +
 +fn issue3670() {
 +    let array = [Some(0), None, Some(1)];
 +    let mut iter = array.iter();
 +
 +    while let Some(elem) = iter.next() {
 +        let _ = elem.or_else(|| *iter.next()?);
 +    }
 +}
 +
 +fn issue1654() {
 +    // should not lint if the iterator is generated on every iteration
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {
 +        values.remove(&1);
 +    }
 +
 +    while let Some(..) = values.iter().map(|x| x + 1).next() {}
 +
 +    let chars = "Hello, World!".char_indices();
 +    while let Some((i, ch)) = chars.clone().next() {
 +        println!("{}: {}", i, ch);
 +    }
 +}
 +
 +fn issue6491() {
 +    // Used in outer loop, needs &mut
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        for m in &mut it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +
 +    // This is fine, inner loop uses a new iterator.
 +    let mut it = 1..40;
 +    for n in it {
 +        let mut it = 1..40;
 +        for m in it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Weird binding shouldn't change anything.
 +        let (mut it, _) = (1..40, 0);
 +        for m in it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Used after the loop, needs &mut.
 +        let mut it = 1..40;
 +        for m in &mut it {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("next item {}", it.next().unwrap());
 +
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue6231() {
 +    // Closure in the outer loop, needs &mut
 +    let mut it = 1..40;
 +    let mut opt = Some(0);
 +    while let Some(n) = opt.take().or_else(|| it.next()) {
 +        for m in &mut it {
 +            if n % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue1924() {
 +    struct S<T>(T);
 +    impl<T: Iterator<Item = u32>> S<T> {
 +        fn f(&mut self) -> Option<u32> {
 +            // Used as a field.
 +            for i in &mut self.0 {
 +                if !(3..=7).contains(&i) {
 +                    return Some(i);
 +                }
 +            }
 +            None
 +        }
 +
 +        fn f2(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.next() {
 +                if i == 1 {
 +                    return self.f();
 +                }
 +            }
 +            None
 +        }
 +    }
 +    impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
 +        fn f3(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.0.f();
 +                }
 +            }
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.f3();
 +                }
 +            }
 +            // This one is fine, a different field is borrowed
 +            for i in &mut self.0.0.0 {
 +                if i == 1 {
 +                    return self.0.1.take();
 +                } else {
 +                    self.0.1 = Some(i);
 +                }
 +            }
 +            None
 +        }
 +    }
 +
 +    struct S2<T>(T, u32);
 +    impl<T: Iterator<Item = u32>> Iterator for S2<T> {
 +        type Item = u32;
 +        fn next(&mut self) -> Option<u32> {
 +            self.0.next()
 +        }
 +    }
 +
 +    // Don't lint, field of the iterator is accessed in the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == it.1 {
 +            break;
 +        }
 +    }
 +
 +    // Needs &mut, field of the iterator is accessed after the loop
 +    let mut it = S2(1..40, 0);
 +    for n in &mut it {
 +        if n == 0 {
 +            break;
 +        }
 +    }
 +    println!("iterator field {}", it.1);
 +}
 +
++fn issue7249() {
++    let mut it = 0..10;
++    let mut x = || {
++        // Needs &mut, the closure can be called multiple times
++        for x in &mut it {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    };
++    x();
++    x();
++}
++
 +fn main() {
 +    let mut it = 0..20;
 +    for _ in it {
 +        println!("test");
 +    }
 +}
index 1717006a4490e59884b5e6db16858021ada5e383,0000000000000000000000000000000000000000..5078a3c9028c4762f9d7ea09e3b1baf5c837c7c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,328 -1,0 +1,342 @@@
 +// run-rustfix
 +
 +#![warn(clippy::while_let_on_iterator)]
 +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
 +
 +fn base() {
 +    let mut iter = 1..20;
 +    while let Option::Some(x) = iter.next() {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    while let Some(x) = iter.next() {
 +        println!("{}", x);
 +    }
 +
 +    let mut iter = 1..20;
 +    while let Some(_) = iter.next() {}
 +
 +    let mut iter = 1..20;
 +    while let None = iter.next() {} // this is fine (if nonsensical)
 +
 +    let mut iter = 1..20;
 +    if let Some(x) = iter.next() {
 +        // also fine
 +        println!("{}", x)
 +    }
 +
 +    // the following shouldn't warn because it can't be written with a for loop
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next())
 +    }
 +
 +    // neither can this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        println!("next: {:?}", iter.next());
 +    }
 +
 +    // or this
 +    let mut iter = 1u32..20;
 +    while let Some(_) = iter.next() {
 +        iter = 1..20;
 +    }
 +}
 +
 +// Issue #1188
 +fn refutable() {
 +    let a = [42, 1337];
 +    let mut b = a.iter();
 +
 +    // consume all the 42s
 +    while let Some(&42) = b.next() {}
 +
 +    let a = [(1, 2, 3)];
 +    let mut b = a.iter();
 +
 +    while let Some(&(1, 2, 3)) = b.next() {}
 +
 +    let a = [Some(42)];
 +    let mut b = a.iter();
 +
 +    while let Some(&None) = b.next() {}
 +
 +    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
 +    for &42 in b {}
 +    for &(1, 2, 3) in b {}
 +    for &Option::None in b.next() {}
 +    // */
 +}
 +
 +fn refutable2() {
 +    // Issue 3780
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.windows(2);
 +        while let Some([x, y]) = it.next() {
 +            println!("x: {}", x);
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([x, ..]) = it.next() {
 +            println!("x: {}", x);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([.., y]) = it.next() {
 +            println!("y: {}", y);
 +        }
 +
 +        let mut it = v.windows(2);
 +        while let Some([..]) = it.next() {}
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some([1]) = it.next() {}
 +
 +        let mut it = v.iter();
 +        while let Some([_x]) = it.next() {}
 +    }
 +
 +    // binding
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter();
 +        while let Some(x @ 1) = it.next() {
 +            println!("{}", x);
 +        }
 +
 +        let v = vec![[1], [2], [3]];
 +        let mut it = v.iter();
 +        while let Some(x @ [_]) = it.next() {
 +            println!("{:?}", x);
 +        }
 +    }
 +
 +    // false negative
 +    {
 +        let v = vec![1, 2, 3];
 +        let mut it = v.iter().map(Some);
 +        while let Some(Some(_) | None) = it.next() {
 +            println!("1");
 +        }
 +    }
 +}
 +
 +fn nested_loops() {
 +    let a = [42, 1337];
 +
 +    loop {
 +        let mut y = a.iter();
 +        while let Some(_) = y.next() {
 +            // use a for loop here
 +        }
 +    }
 +}
 +
 +fn issue1121() {
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(&value) = values.iter().next() {
 +        values.remove(&value);
 +    }
 +}
 +
 +fn issue2965() {
 +    // This should not cause an ICE
 +
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {}
 +}
 +
 +fn issue3670() {
 +    let array = [Some(0), None, Some(1)];
 +    let mut iter = array.iter();
 +
 +    while let Some(elem) = iter.next() {
 +        let _ = elem.or_else(|| *iter.next()?);
 +    }
 +}
 +
 +fn issue1654() {
 +    // should not lint if the iterator is generated on every iteration
 +    use std::collections::HashSet;
 +    let mut values = HashSet::new();
 +    values.insert(1);
 +
 +    while let Some(..) = values.iter().next() {
 +        values.remove(&1);
 +    }
 +
 +    while let Some(..) = values.iter().map(|x| x + 1).next() {}
 +
 +    let chars = "Hello, World!".char_indices();
 +    while let Some((i, ch)) = chars.clone().next() {
 +        println!("{}: {}", i, ch);
 +    }
 +}
 +
 +fn issue6491() {
 +    // Used in outer loop, needs &mut
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +
 +    // This is fine, inner loop uses a new iterator.
 +    let mut it = 1..40;
 +    while let Some(n) = it.next() {
 +        let mut it = 1..40;
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Weird binding shouldn't change anything.
 +        let (mut it, _) = (1..40, 0);
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +
 +        // Used after the loop, needs &mut.
 +        let mut it = 1..40;
 +        while let Some(m) = it.next() {
 +            if m % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("next item {}", it.next().unwrap());
 +
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue6231() {
 +    // Closure in the outer loop, needs &mut
 +    let mut it = 1..40;
 +    let mut opt = Some(0);
 +    while let Some(n) = opt.take().or_else(|| it.next()) {
 +        while let Some(m) = it.next() {
 +            if n % 10 == 0 {
 +                break;
 +            }
 +            println!("doing something with m: {}", m);
 +        }
 +        println!("n still is {}", n);
 +    }
 +}
 +
 +fn issue1924() {
 +    struct S<T>(T);
 +    impl<T: Iterator<Item = u32>> S<T> {
 +        fn f(&mut self) -> Option<u32> {
 +            // Used as a field.
 +            while let Some(i) = self.0.next() {
 +                if i < 3 || i > 7 {
 +                    return Some(i);
 +                }
 +            }
 +            None
 +        }
 +
 +        fn f2(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.next() {
 +                if i == 1 {
 +                    return self.f();
 +                }
 +            }
 +            None
 +        }
 +    }
 +    impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
 +        fn f3(&mut self) -> Option<u32> {
 +            // Don't lint, self borrowed inside the loop
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.0.f();
 +                }
 +            }
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.f3();
 +                }
 +            }
 +            // This one is fine, a different field is borrowed
 +            while let Some(i) = self.0.0.0.next() {
 +                if i == 1 {
 +                    return self.0.1.take();
 +                } else {
 +                    self.0.1 = Some(i);
 +                }
 +            }
 +            None
 +        }
 +    }
 +
 +    struct S2<T>(T, u32);
 +    impl<T: Iterator<Item = u32>> Iterator for S2<T> {
 +        type Item = u32;
 +        fn next(&mut self) -> Option<u32> {
 +            self.0.next()
 +        }
 +    }
 +
 +    // Don't lint, field of the iterator is accessed in the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == it.1 {
 +            break;
 +        }
 +    }
 +
 +    // Needs &mut, field of the iterator is accessed after the loop
 +    let mut it = S2(1..40, 0);
 +    while let Some(n) = it.next() {
 +        if n == 0 {
 +            break;
 +        }
 +    }
 +    println!("iterator field {}", it.1);
 +}
 +
++fn issue7249() {
++    let mut it = 0..10;
++    let mut x = || {
++        // Needs &mut, the closure can be called multiple times
++        while let Some(x) = it.next() {
++            if x % 2 == 0 {
++                break;
++            }
++        }
++    };
++    x();
++    x();
++}
++
 +fn main() {
 +    let mut it = 0..20;
 +    while let Some(..) = it.next() {
 +        println!("test");
 +    }
 +}
index eff559bef7e3b87a3ab9491876558389070393a9,0000000000000000000000000000000000000000..cb0afeae15ee0ead89f0bbe769e7af04660ccd72
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,120 @@@
-   --> $DIR/while_let_on_iterator.rs:325:5
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:8:5
 +   |
 +LL |     while let Option::Some(x) = iter.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 +   |
 +   = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:13:5
 +   |
 +LL |     while let Some(x) = iter.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:18:5
 +   |
 +LL |     while let Some(_) = iter.next() {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:94:9
 +   |
 +LL |         while let Some([..]) = it.next() {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:101:9
 +   |
 +LL |         while let Some([_x]) = it.next() {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:114:9
 +   |
 +LL |         while let Some(x @ [_]) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:134:9
 +   |
 +LL |         while let Some(_) = y.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:191:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:202:5
 +   |
 +LL |     while let Some(n) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:204:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:213:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:222:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:239:9
 +   |
 +LL |         while let Some(m) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:254:13
 +   |
 +LL |             while let Some(i) = self.0.next() {
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0`
 +
 +error: manual `!RangeInclusive::contains` implementation
 +  --> $DIR/while_let_on_iterator.rs:255:20
 +   |
 +LL |                 if i < 3 || i > 7 {
 +   |                    ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
 +   |
 +   = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:286:13
 +   |
 +LL |             while let Some(i) = self.0.0.0.next() {
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0`
 +
 +error: this loop could be written as a `for` loop
 +  --> $DIR/while_let_on_iterator.rs:315:5
 +   |
 +LL |     while let Some(n) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
 +
 +error: this loop could be written as a `for` loop
- error: aborting due to 18 previous errors
++  --> $DIR/while_let_on_iterator.rs:327:9
++   |
++LL |         while let Some(x) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:339:5
 +   |
 +LL |     while let Some(..) = it.next() {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 +
++error: aborting due to 19 previous errors
 +
index 5ad27bb14501feb8988331dfaefd1dee909e6431,0000000000000000000000000000000000000000..3ee4ab48ac84b215c6a21d34e4be0e85f74fa414
mode 100644,000000..100644
--- /dev/null
@@@ -1,105 -1,0 +1,104 @@@
- use std::io::ErrorKind;
 +// run-rustfix
++// aux-build:non-exhaustive-enum.rs
 +
 +#![deny(clippy::wildcard_enum_match_arm)]
 +#![allow(
 +    unreachable_code,
 +    unused_variables,
 +    dead_code,
 +    clippy::single_match,
 +    clippy::wildcard_in_or_patterns,
 +    clippy::unnested_or_patterns,
 +    clippy::diverging_sub_expression
 +)]
 +
-         ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | ErrorKind::OutOfMemory | _ => {},
++extern crate non_exhaustive_enum;
++
++use non_exhaustive_enum::ErrorKind;
 +
 +#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 +enum Color {
 +    Red,
 +    Green,
 +    Blue,
 +    Rgb(u8, u8, u8),
 +    Cyan,
 +}
 +
 +impl Color {
 +    fn is_monochrome(self) -> bool {
 +        match self {
 +            Color::Red | Color::Green | Color::Blue => true,
 +            Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
 +            Color::Cyan => false,
 +        }
 +    }
 +}
 +
 +fn main() {
 +    let color = Color::Rgb(0, 0, 127);
 +    match color {
 +        Color::Red => println!("Red"),
 +        Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => eprintln!("Not red"),
 +    };
 +    match color {
 +        Color::Red => println!("Red"),
 +        _not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan => eprintln!("Not red"),
 +    };
 +    let _str = match color {
 +        Color::Red => "Red".to_owned(),
 +        not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan => format!("{:?}", not_red),
 +    };
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Blue => {},
 +        Color::Cyan => {},
 +        c if c.is_monochrome() => {},
 +        Color::Rgb(_, _, _) => {},
 +    };
 +    let _str = match color {
 +        Color::Red => "Red",
 +        c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
 +    };
 +    match color {
 +        Color::Rgb(r, _, _) if r > 0 => "Some red",
 +        Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => "No red",
 +    };
 +    match color {
 +        Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
 +        Color::Rgb(..) => {},
 +    };
 +    let x: u8 = unimplemented!();
 +    match x {
 +        0 => {},
 +        140 => {},
 +        _ => {},
 +    };
 +    // We need to use an enum not defined in this test because non_exhaustive is ignored for the
 +    // purposes of dead code analysis within a crate.
 +    let error_kind = ErrorKind::NotFound;
 +    match error_kind {
 +        ErrorKind::NotFound => {},
-         ErrorKind::ConnectionRefused => {},
-         ErrorKind::ConnectionReset => {},
-         ErrorKind::ConnectionAborted => {},
-         ErrorKind::NotConnected => {},
-         ErrorKind::AddrInUse => {},
-         ErrorKind::AddrNotAvailable => {},
-         ErrorKind::BrokenPipe => {},
-         ErrorKind::AlreadyExists => {},
-         ErrorKind::WouldBlock => {},
-         ErrorKind::InvalidInput => {},
-         ErrorKind::InvalidData => {},
-         ErrorKind::TimedOut => {},
-         ErrorKind::WriteZero => {},
-         ErrorKind::Interrupted => {},
-         ErrorKind::Other => {},
-         ErrorKind::UnexpectedEof => {},
-         ErrorKind::Unsupported => {},
-         ErrorKind::OutOfMemory => {},
++        ErrorKind::PermissionDenied | _ => {},
 +    }
 +    match error_kind {
 +        ErrorKind::NotFound => {},
 +        ErrorKind::PermissionDenied => {},
 +        _ => {},
 +    }
++
++    {
++        #![allow(clippy::manual_non_exhaustive)]
++        pub enum Enum {
++            A,
++            B,
++            #[doc(hidden)]
++            __Private,
++        }
++        match Enum::A {
++            Enum::A => (),
++            Enum::B | _ => (),
++        }
++    }
 +}
index adca0738bba5b3cdee4e8255d79916ed98a3912c,0000000000000000000000000000000000000000..46886550453308a340353c5a8c9c8b143a0877a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,105 -1,0 +1,104 @@@
- use std::io::ErrorKind;
 +// run-rustfix
++// aux-build:non-exhaustive-enum.rs
 +
 +#![deny(clippy::wildcard_enum_match_arm)]
 +#![allow(
 +    unreachable_code,
 +    unused_variables,
 +    dead_code,
 +    clippy::single_match,
 +    clippy::wildcard_in_or_patterns,
 +    clippy::unnested_or_patterns,
 +    clippy::diverging_sub_expression
 +)]
 +
-         ErrorKind::ConnectionRefused => {},
-         ErrorKind::ConnectionReset => {},
-         ErrorKind::ConnectionAborted => {},
-         ErrorKind::NotConnected => {},
-         ErrorKind::AddrInUse => {},
-         ErrorKind::AddrNotAvailable => {},
-         ErrorKind::BrokenPipe => {},
-         ErrorKind::AlreadyExists => {},
-         ErrorKind::WouldBlock => {},
-         ErrorKind::InvalidInput => {},
-         ErrorKind::InvalidData => {},
-         ErrorKind::TimedOut => {},
-         ErrorKind::WriteZero => {},
-         ErrorKind::Interrupted => {},
-         ErrorKind::Other => {},
-         ErrorKind::UnexpectedEof => {},
-         ErrorKind::Unsupported => {},
-         ErrorKind::OutOfMemory => {},
++extern crate non_exhaustive_enum;
++
++use non_exhaustive_enum::ErrorKind;
 +
 +#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 +enum Color {
 +    Red,
 +    Green,
 +    Blue,
 +    Rgb(u8, u8, u8),
 +    Cyan,
 +}
 +
 +impl Color {
 +    fn is_monochrome(self) -> bool {
 +        match self {
 +            Color::Red | Color::Green | Color::Blue => true,
 +            Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
 +            Color::Cyan => false,
 +        }
 +    }
 +}
 +
 +fn main() {
 +    let color = Color::Rgb(0, 0, 127);
 +    match color {
 +        Color::Red => println!("Red"),
 +        _ => eprintln!("Not red"),
 +    };
 +    match color {
 +        Color::Red => println!("Red"),
 +        _not_red => eprintln!("Not red"),
 +    };
 +    let _str = match color {
 +        Color::Red => "Red".to_owned(),
 +        not_red => format!("{:?}", not_red),
 +    };
 +    match color {
 +        Color::Red => {},
 +        Color::Green => {},
 +        Color::Blue => {},
 +        Color::Cyan => {},
 +        c if c.is_monochrome() => {},
 +        Color::Rgb(_, _, _) => {},
 +    };
 +    let _str = match color {
 +        Color::Red => "Red",
 +        c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
 +    };
 +    match color {
 +        Color::Rgb(r, _, _) if r > 0 => "Some red",
 +        _ => "No red",
 +    };
 +    match color {
 +        Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
 +        Color::Rgb(..) => {},
 +    };
 +    let x: u8 = unimplemented!();
 +    match x {
 +        0 => {},
 +        140 => {},
 +        _ => {},
 +    };
 +    // We need to use an enum not defined in this test because non_exhaustive is ignored for the
 +    // purposes of dead code analysis within a crate.
 +    let error_kind = ErrorKind::NotFound;
 +    match error_kind {
 +        ErrorKind::NotFound => {},
 +        _ => {},
 +    }
 +    match error_kind {
 +        ErrorKind::NotFound => {},
 +        ErrorKind::PermissionDenied => {},
 +        _ => {},
 +    }
++
++    {
++        #![allow(clippy::manual_non_exhaustive)]
++        pub enum Enum {
++            A,
++            B,
++            #[doc(hidden)]
++            __Private,
++        }
++        match Enum::A {
++            Enum::A => (),
++            _ => (),
++        }
++    }
 +}
index 73f6a4a80c960d36c8611b63fca88f59d1103c5d,0000000000000000000000000000000000000000..d63f2090353151a55ec431d50b814d8e2c6c7bc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,44 @@@
-   --> $DIR/wildcard_enum_match_arm.rs:39:9
 +error: wildcard match will also match any future added variants
-   --> $DIR/wildcard_enum_match_arm.rs:3:9
++  --> $DIR/wildcard_enum_match_arm.rs:42:9
 +   |
 +LL |         _ => eprintln!("Not red"),
 +   |         ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
 +   |
 +note: the lint level is defined here
-   --> $DIR/wildcard_enum_match_arm.rs:43:9
++  --> $DIR/wildcard_enum_match_arm.rs:4:9
 +   |
 +LL | #![deny(clippy::wildcard_enum_match_arm)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: wildcard match will also match any future added variants
-   --> $DIR/wildcard_enum_match_arm.rs:47:9
++  --> $DIR/wildcard_enum_match_arm.rs:46:9
 +   |
 +LL |         _not_red => eprintln!("Not red"),
 +   |         ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
 +
 +error: wildcard match will also match any future added variants
-   --> $DIR/wildcard_enum_match_arm.rs:63:9
++  --> $DIR/wildcard_enum_match_arm.rs:50:9
 +   |
 +LL |         not_red => format!("{:?}", not_red),
 +   |         ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
 +
 +error: wildcard match will also match any future added variants
-   --> $DIR/wildcard_enum_match_arm.rs:80:9
++  --> $DIR/wildcard_enum_match_arm.rs:66:9
 +   |
 +LL |         _ => "No red",
 +   |         ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
 +
 +error: wildcard matches known variants and will also match future added variants
-    |         ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | ErrorKind::OutOfMemory | _`
++  --> $DIR/wildcard_enum_match_arm.rs:83:9
 +   |
 +LL |         _ => {},
- error: aborting due to 5 previous errors
++   |         ^ help: try this: `ErrorKind::PermissionDenied | _`
 +
++error: wildcard matches known variants and will also match future added variants
++  --> $DIR/wildcard_enum_match_arm.rs:101:13
++   |
++LL |             _ => (),
++   |             ^ help: try this: `Enum::B | _`
++
++error: aborting due to 6 previous errors
 +
index 2de904376ad45b438ef580b99f2791fe7d5ea9bd,0000000000000000000000000000000000000000..6c190a4c86c48a3175f5b94a0c22312757de088d
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,18 @@@
-         let x = &() as *const ();
-         x.offset(0);
-         x.wrapping_add(0);
-         x.sub(0);
-         x.wrapping_sub(0);
 +fn main() {
 +    unsafe {
-         let y = &1 as *const u8;
-         y.offset(0);
++        let m = &mut () as *mut ();
++        m.offset(0);
++        m.wrapping_add(0);
++        m.sub(0);
++        m.wrapping_sub(0);
 +
++        let c = &() as *const ();
++        c.offset(0);
++        c.wrapping_add(0);
++        c.sub(0);
++        c.wrapping_sub(0);
++
++        let sized = &1 as *const i32;
++        sized.offset(0);
 +    }
 +}
index cfcd7de2b3d2c6b4503cbb125ff03be2ee0896b8,0000000000000000000000000000000000000000..b12c8e9a73c6dab3692dc2bc97cbce8ea81b3bc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,9 -1,0 +1,52 @@@
- error[E0606]: casting `&i32` as `*const u8` is invalid
-   --> $DIR/zero_offset.rs:9:17
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:4:9
 +   |
- LL |         let y = &1 as *const u8;
-    |                 ^^^^^^^^^^^^^^^
++LL |         m.offset(0);
++   |         ^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::zst_offset)]` on by default
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:5:9
++   |
++LL |         m.wrapping_add(0);
++   |         ^^^^^^^^^^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:6:9
++   |
++LL |         m.sub(0);
++   |         ^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:7:9
++   |
++LL |         m.wrapping_sub(0);
++   |         ^^^^^^^^^^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:10:9
++   |
++LL |         c.offset(0);
++   |         ^^^^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:11:9
++   |
++LL |         c.wrapping_add(0);
++   |         ^^^^^^^^^^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:12:9
++   |
++LL |         c.sub(0);
++   |         ^^^^^^^^
++
++error: offset calculation on zero-sized value
++  --> $DIR/zero_offset.rs:13:9
++   |
++LL |         c.wrapping_sub(0);
++   |         ^^^^^^^^^^^^^^^^^
 +
- error: aborting due to previous error
++error: aborting due to 8 previous errors
 +
- For more information about this error, try `rustc --explain E0606`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..528f8953b25d8b81b40633a399def3fd9762726e
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++#!/bin/sh
++
++# hide output
++set -e
++
++# Update lints
++cargo dev update_lints
++git add clippy_lints/src/lib.rs
++
++# Formatting:
++#     Git will not automatically add the formatted code to the staged changes once
++#     fmt was executed. This collects all staged files rs files that are currently staged.
++#     They will later be added back.
++#
++#     This was proudly stolen and adjusted from here:
++#     https://medium.com/@harshitbangar/automatic-code-formatting-with-git-66c3c5c26798
++files=$( (git diff --cached --name-only --diff-filter=ACMR | grep -Ei "\.rs$") || true)
++if [ ! -z "${files}" ]; then
++    cargo dev fmt
++    git add $(echo "$files" | paste -s -d " " -)
++fi
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4bb26b9f900587151545521e862ec67db9701b2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++{
++    "version": "2.0.0",
++    "tasks": [
++        {
++            "label": "cargo check",
++            "type": "shell",
++            "command": "cargo check",
++            "problemMatcher": [],
++            "group": {
++                "kind": "build",
++                "isDefault": true,
++            },
++        },
++        {
++            "label": "cargo dev fmt",
++            "type": "shell",
++            "command": "cargo dev fmt",
++            "problemMatcher": [],
++            "group": "none",
++        },
++        {
++            "label": "cargo uitest",
++            "type": "shell",
++            "command": "cargo uitest",
++            "options": {
++                "env": {
++                    "RUST_BACKTRACE": "1",
++                    // This task will usually execute all UI tests inside `tests/ui` you can
++                    // optionally uncomment the line below and only run a specific test.
++                    //
++                    // See: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#testing
++                    //
++                    // "TESTNAME": "<TODO>",
++                },
++            },
++            "problemMatcher": [],
++            "group": {
++                "kind": "test",
++                "isDefault": true,
++            }
++        },
++        {
++            "label": "cargo test",
++            "type": "shell",
++            "command": "cargo test",
++            "problemMatcher": [],
++            "group": "test",
++        },
++        {
++            "label": "cargo dev bless",
++            "type": "shell",
++            "command": "cargo dev bless",
++            "problemMatcher": [],
++            "group": "none",
++        },
++    ],
++}
index 5707cf0ce0f8c08177f68866b7065c6c9feb2182,0000000000000000000000000000000000000000..9cefb2dbb197caffafcc0ae1cd7c6f40f1fa450c
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,115 @@@
- conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', 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]+)''')
-     confvars = re.findall(confvar_re, match.group(1))
++conf_re = re.compile(r'''define_Conf! {\n((?!\n})[\s\S])*\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',
++    "suspicious": 'Warn',
 +    "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(0))
 +
 +    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