--- /dev/null
--- /dev/null
++[alias]
++uitest = "test --test compile-test"
++dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
++
++[build]
++rustflags = ["-Zunstable-options"]
--- /dev/null
--- /dev/null
++# EditorConfig helps developers define and maintain consistent
++# coding styles between different editors and IDEs
++# editorconfig.org
++
++root = true
++
++[*]
++end_of_line = lf
++charset = utf-8
++trim_trailing_whitespace = true
++insert_final_newline = true
++indent_style = space
++indent_size = 4
++
++[*.md]
++trim_trailing_whitespace = false
++
++[*.yml]
++indent_size = 2
--- /dev/null
--- /dev/null
++* text=auto eol=lf
++*.rs text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
++*.fixed linguist-language=Rust
--- /dev/null
--- /dev/null
++<!--
++Hi there! Whether you've come to make a suggestion for a new lint, an improvement to an existing lint or to report a bug or a false positive in Clippy, you've come to the right place.
++
++For bug reports and false positives, please include the output of `cargo clippy -V` in the report.
++
++Thank you for using Clippy!
++
++Write your comment below this line: -->
--- /dev/null
--- /dev/null
++Thank you for making Clippy better!
++
++We're collecting our changelog from pull request descriptions.
++If your PR only updates to the latest nightly, you can leave the
++`changelog` entry as `none`. Otherwise, please write a short comment
++explaining your change.
++
++If your PR fixes an issue, you can add "fixes #issue_number" into this
++PR description. This way the issue will be automatically closed when
++your PR is merged.
++
++If you added a new lint, here's a checklist for things that will be
++checked during review or continuous integration.
++
++- [ ] Followed [lint naming conventions][lint_naming]
++- [ ] Added passing UI tests (including committed `.stderr` file)
++- [ ] `cargo test` passes locally
++- [ ] Executed `cargo dev update_lints`
++- [ ] Added lint documentation
++- [ ] Run `cargo dev fmt`
++
++[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++
++Note that you can skip the above if you are just opening a WIP PR in
++order to get feedback.
++
++Delete this line and everything above before opening your PR.
++
++---
++
++changelog: none
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++set -ex
++
++echo "Removing the current docs for master"
++rm -rf out/master/ || exit 0
++
++echo "Making the docs for master"
++mkdir out/master/
++cp util/gh-pages/index.html out/master
++python3 ./util/export.py out/master/lints.json
++
++if [[ -n $TAG_NAME ]]; then
++ echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
++ cp -r out/master "out/$TAG_NAME"
++ rm -f out/stable
++ ln -s "$TAG_NAME" out/stable
++fi
++
++if [[ $BETA = "true" ]]; then
++ echo "Update documentation for the beta release"
++ cp -r out/master out/beta
++fi
++
++# Generate version index that is shown as root index page
++cp util/gh-pages/versions.html out/index.html
++
++echo "Making the versions.json file"
++python3 ./util/versions.py out
++
++cd out
++# Now let's go have some fun with the cloned repo
++git config user.name "GHA CI"
++git config user.email "gha@ci.invalid"
++
++if git diff --exit-code --quiet; then
++ echo "No changes to the output on this push; exiting."
++ exit 0
++fi
++
++if [[ -n $TAG_NAME ]]; then
++ # Add the new dir
++ git add "$TAG_NAME"
++ # Update the symlink
++ git add stable
++ # Update versions file
++ git add versions.json
++ git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
++elif [[ $BETA = "true" ]]; then
++ git add beta
++ git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}"
++else
++ git add .
++ git commit -m "Automatic deploy to GitHub Pages: ${SHA}"
++fi
++
++git push "$SSH_REPO" "$TARGET_BRANCH"
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++set -ex
++
++# Check sysroot handling
++sysroot=$(./target/debug/clippy-driver --print sysroot)
++test "$sysroot" = "$(rustc --print sysroot)"
++
++if [[ ${OS} == "Windows" ]]; then
++ desired_sysroot=C:/tmp
++else
++ desired_sysroot=/tmp
++fi
++sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
++test "$sysroot" = $desired_sysroot
++
++sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
++test "$sysroot" = $desired_sysroot
++
++# Make sure this isn't set - clippy-driver should cope without it
++unset CARGO_MANIFEST_DIR
++
++# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
++# FIXME: How to match the clippy invocation in compile-test.rs?
++./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cstring.rs 2> cstring.stderr && exit 1
++sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr
++diff normalized.stderr tests/ui/cstring.stderr
++
++# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
--- /dev/null
--- /dev/null
++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:
++ runs-on: ubuntu-latest
++
++ steps:
++ # Setup
++ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++ with:
++ github_token: "${{ secrets.github_token }}"
++
++ - name: rust-toolchain
++ uses: actions-rs/toolchain@v1.0.3
++ with:
++ toolchain: nightly
++ target: x86_64-unknown-linux-gnu
++ profile: minimal
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ - name: Run cargo update
++ run: cargo update
++
++ - name: Cache cargo dir
++ uses: actions/cache@v1
++ with:
++ path: ~/.cargo
++ key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++ restore-keys: |
++ ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++ - name: Master Toolchain Setup
++ run: bash setup-toolchain.sh
++
++ # Run
++ - name: Set LD_LIBRARY_PATH (Linux)
++ run: |
++ SYSROOT=$(rustc --print sysroot)
++ echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}"
++
++ - name: Build
++ run: cargo build --features deny-warnings
++
++ - name: Test
++ run: cargo test --features deny-warnings
++
++ - name: Test clippy_lints
++ run: cargo test --features deny-warnings
++ 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 clippy-driver
++ run: bash .github/driver.sh
++ env:
++ OS: ${{ runner.os }}
++
++ # Cleanup
++ - name: Run cargo-cache --autoclean
++ run: |
++ cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++ cargo cache
--- /dev/null
--- /dev/null
++name: Clippy Test (bors)
++
++on:
++ push:
++ branches:
++ - auto
++ - try
++
++env:
++ RUST_BACKTRACE: 1
++ CARGO_TARGET_DIR: '${{ github.workspace }}/target'
++ NO_FMT_TEST: 1
++
++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.0.0
++ 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/^#//')
++ output=$(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'])" | \
++ grep "^changelog: " | \
++ sed "s/changelog: //g")
++ if [[ -z "$output" ]]; then
++ echo "ERROR: PR body must contain 'changelog: ...'"
++ exit 1
++ elif [[ "$output" = "none" ]]; then
++ echo "WARNING: changelog is 'none'"
++ 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 }}
++
++ 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: rust-toolchain
++ uses: actions-rs/toolchain@v1.0.3
++ with:
++ toolchain: nightly
++ target: ${{ matrix.host }}
++ profile: minimal
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ - name: Run cargo update
++ run: cargo update
++
++ - name: Cache cargo dir
++ uses: actions/cache@v1
++ with:
++ path: ~/.cargo
++ key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
++ restore-keys: |
++ ${{ runner.os }}-${{ matrix.host }}
++
++ - name: Master Toolchain Setup
++ run: bash setup-toolchain.sh
++ env:
++ HOST_TOOLCHAIN: ${{ matrix.host }}
++ shell: bash
++
++ # Run
++ - name: Set LD_LIBRARY_PATH (Linux)
++ if: runner.os == 'Linux'
++ run: |
++ SYSROOT=$(rustc --print sysroot)
++ echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}"
++ - 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
++ $env:PATH += ';' + $sysroot + '\bin'
++ echo "::set-env name=PATH::$env:PATH"
++
++ - name: Build
++ run: cargo build --features deny-warnings
++ shell: bash
++
++ - name: Test
++ run: cargo test --features deny-warnings
++ shell: bash
++
++ - name: Test clippy_lints
++ run: cargo test --features deny-warnings
++ shell: bash
++ working-directory: clippy_lints
++
++ - name: Test rustc_tools_util
++ run: cargo test --features deny-warnings
++ shell: bash
++ working-directory: rustc_tools_util
++
++ - name: Test clippy_dev
++ run: cargo test --features deny-warnings
++ shell: bash
++ working-directory: clippy_dev
++
++ - name: Test cargo-clippy
++ run: ../target/debug/cargo-clippy
++ shell: bash
++ working-directory: clippy_workspace_tests
++
++ - name: Test clippy-driver
++ run: bash .github/driver.sh
++ shell: bash
++ env:
++ OS: ${{ runner.os }}
++
++ # Cleanup
++ - name: Run cargo-cache --autoclean
++ run: |
++ cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++ cargo cache
++ shell: bash
++ 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: rust-toolchain
++ uses: actions-rs/toolchain@v1.0.3
++ with:
++ toolchain: nightly
++ target: x86_64-unknown-linux-gnu
++ profile: minimal
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ - name: Run cargo update
++ run: cargo update
++
++ - name: Cache cargo dir
++ uses: actions/cache@v1
++ with:
++ path: ~/.cargo
++ key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++ restore-keys: |
++ ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++ - name: Master Toolchain Setup
++ run: bash setup-toolchain.sh
++
++ # 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
++
++ # Cleanup
++ - name: Run cargo-cache --autoclean
++ run: |
++ cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++ cargo cache
++ integration:
++ needs: integration_build
++ strategy:
++ fail-fast: false
++ max-parallel: 6
++ matrix:
++ integration:
++ - 'rust-lang/cargo'
++ - 'rust-lang/rls'
++ - 'rust-lang/chalk'
++ - 'rust-lang/rustfmt'
++ - 'Marwes/combine'
++ - 'Geal/nom'
++ - 'rust-lang/stdarch'
++ - 'serde-rs/serde'
++ - '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: rust-toolchain
++ uses: actions-rs/toolchain@v1.0.3
++ with:
++ toolchain: nightly
++ target: x86_64-unknown-linux-gnu
++ profile: minimal
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ - name: Run cargo update
++ run: cargo update
++
++ - name: Cache cargo dir
++ uses: actions/cache@v1
++ with:
++ path: ~/.cargo
++ key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++ restore-keys: |
++ ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++ - name: Master Toolchain Setup
++ run: bash setup-toolchain.sh
++
++ # 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: $CARGO_TARGET_DIR/debug/integration
++ env:
++ INTEGRATION: ${{ matrix.integration }}
++ RUSTUP_TOOLCHAIN: master
++
++ # Cleanup
++ - name: Run cargo-cache --autoclean
++ run: |
++ cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++ cargo cache
++
++ # 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: [base, 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: [base, integration]
++
++ steps:
++ - name: Mark the job as a failure
++ run: exit 1
--- /dev/null
--- /dev/null
++name: Clippy Dev Test
++
++on:
++ push:
++ branches:
++ - auto
++ - try
++ pull_request:
++ # Only run on paths, that get checked by the clippy_dev tool
++ paths:
++ - 'CHANGELOG.md'
++ - 'README.md'
++ - '**.stderr'
++ - '**.rs'
++
++env:
++ RUST_BACKTRACE: 1
++
++jobs:
++ clippy_dev:
++ runs-on: ubuntu-latest
++
++ steps:
++ # Setup
++ - name: rust-toolchain
++ uses: actions-rs/toolchain@v1.0.3
++ with:
++ toolchain: nightly
++ target: x86_64-unknown-linux-gnu
++ profile: minimal
++ components: rustfmt
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ # Run
++ - name: Build
++ run: cargo build --features deny-warnings
++ working-directory: clippy_dev
++
++ - name: Test limit_stderr_length
++ run: cargo dev limit_stderr_length
++
++ - name: Test update_lints
++ run: cargo dev update_lints --check
++
++ - name: Test fmt
++ run: cargo dev fmt --check
++
++ # These jobs doesn't actually test anything, but they're only used to tell
++ # bors the build completed, as there is no practical way to detect when a
++ # workflow is successful listening to webhooks only.
++ #
++ # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
++
++ end-success:
++ name: bors dev test finished
++ if: github.event.pusher.name == 'bors' && success()
++ runs-on: ubuntu-latest
++ needs: [clippy_dev]
++
++ steps:
++ - name: Mark the job as successful
++ run: exit 0
++
++ end-failure:
++ name: bors dev test finished
++ if: github.event.pusher.name == 'bors' && (failure() || cancelled())
++ runs-on: ubuntu-latest
++ needs: [clippy_dev]
++
++ steps:
++ - name: Mark the job as a failure
++ run: exit 1
--- /dev/null
--- /dev/null
++name: Deploy
++
++on:
++ push:
++ branches:
++ - master
++ - beta
++ tags:
++ - rust-1.**
++
++env:
++ TARGET_BRANCH: 'gh-pages'
++ SHA: '${{ github.sha }}'
++ SSH_REPO: 'git@github.com:${{ github.repository }}.git'
++
++jobs:
++ deploy:
++ runs-on: ubuntu-latest
++ if: github.repository == 'rust-lang/rust-clippy'
++
++ steps:
++ # Setup
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++
++ - name: Checkout
++ uses: actions/checkout@v2.0.0
++ with:
++ ref: ${{ env.TARGET_BRANCH }}
++ path: 'out'
++
++ # Run
++ - name: Set tag name
++ if: startswith(github.ref, 'refs/tags/')
++ run: |
++ TAG=$(basename ${{ github.ref }})
++ echo "::set-env name=TAG_NAME::$TAG"
++ - name: Set beta to true
++ if: github.ref == 'refs/heads/beta'
++ run: echo "::set-env name=BETA::true"
++
++ - name: Use scripts and templates from master branch
++ run: |
++ git fetch --no-tags --prune --depth=1 origin master
++ git checkout origin/master -- .github/deploy.sh util/gh-pages/ util/*.py
++
++ - name: Deploy
++ run: |
++ eval "$(ssh-agent -s)"
++ ssh-add - <<< "${{ secrets.DEPLOY_KEY }}"
++ bash .github/deploy.sh
--- /dev/null
--- /dev/null
++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.0.0
++
++ - name: Setup Node.js
++ uses: actions/setup-node@v1.1.0
++
++ - name: Install remark
++ run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended
++
++ # 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
--- /dev/null
--- /dev/null
++# Used by CI to be able to push:
++/.github/deploy_key
++out
++
++# Compiled files
++*.o
++*.d
++*.so
++*.rlib
++*.dll
++*.pyc
++*.rmeta
++
++# Executables
++*.exe
++
++# Generated by Cargo
++*Cargo.lock
++/target
++/clippy_lints/target
++/clippy_workspace_tests/target
++/clippy_dev/target
++/rustc_tools_util/target
++
++# Generated by dogfood
++/target_recur/
++
++# gh pages docs
++util/gh-pages/lints.json
++
++# rustfmt backups
++*.rs.bk
++
++helper.txt
++*.iml
++.vscode
++.idea
--- /dev/null
--- /dev/null
++{
++ "plugins": [
++ "remark-preset-lint-recommended",
++ ["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
++ }
++}
--- /dev/null
--- /dev/null
++# Change Log
++
++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
++
++[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master)
++
++## Rust 1.44
++
++Current beta, release 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
++
++Current stable, 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
++
++<!-- lint disable no-unused-definitions -->
++<!-- begin autogenerated links to lint list -->
++[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
++[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
++[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
++[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
++[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
++[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
++[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
++[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
++[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
++[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
++[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
++[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
++[`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
++[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
++[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
++[`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
++[`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_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
++[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
++[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
++[`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_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
++[`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_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
++[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
++[`drop_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
++[`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
++[`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
++[`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_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
++[`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
++[`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_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
++[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
++[`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
++[`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_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
++[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
++[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
++[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
++[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
++[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
++[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
++[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
++[`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
++[`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_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
++[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
++[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
++[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
++[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
++[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
++[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
++[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
++[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
++[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
++[`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_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
++[`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
++[`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_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_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
++[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
++[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
++[`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_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
++[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
++[`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_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
++[`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_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_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_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_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_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_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
++[`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
++[`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_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
++[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
++[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
++[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
++[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
++[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
++[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
++[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
++[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
++[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
++[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
++[`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_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
++[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
++[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
++[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
++[`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_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_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
++[`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_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_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
++[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
++[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
++[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
++[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
++[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
++[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
++[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
++[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
++[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
++[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
++[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
++[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
++[`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_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_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
++[`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
++[`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_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_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
++[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
++[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
++[`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
++[`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
++[`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
++[`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
++[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
++[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
++[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
++[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
++[`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_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
++[`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
++[`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_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
++[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
++[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
++[`unused_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
++[`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_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
++[`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_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
++[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
++<!-- end autogenerated links to lint list -->
--- /dev/null
--- /dev/null
++# The Rust Code of Conduct
++
++A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
++
++## Conduct
++
++**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
++
++* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience,
++ gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
++ religion, nationality, or other similar characteristic.
++* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and
++ welcoming environment for all.
++* Please be kind and courteous. There's no need to be mean or rude.
++* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and
++ numerous costs. There is seldom a right answer.
++* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and
++ see how it works.
++* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We
++ interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen
++ Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their
++ definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
++* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or
++ made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation
++ team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a
++ safe place for you and we've got your back.
++* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
++
++## Moderation
++
++
++These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation,
++please contact the [Rust moderation team][mod_team].
++
++1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks,
++ are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
++2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
++3. Moderators will first respond to such remarks with a warning.
++4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
++5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
++6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended
++ party a genuine apology.
++7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a
++ different moderator, **in private**. Complaints about bans in-channel are not allowed.
++8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate
++ situation, they should expect less leeway than others.
++
++In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically
++unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly
++if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can
++drive people away from the community entirely.
++
++And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was
++they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good
++there was something you could've communicated better — remember that it's your responsibility to make your fellow
++Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about
++cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their
++trust.
++
++The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust,
++#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo);
++GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org
++(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the
++maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider
++explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
++
++*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the
++[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
++
++[mod_team]: https://www.rust-lang.org/team.html#Moderation-team
--- /dev/null
--- /dev/null
++# 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 [Discord].
++
++All contributors are expected to follow the [Rust Code of Conduct].
++
++* [Getting started](#getting-started)
++ * [Finding something to fix/improve](#finding-something-to-fiximprove)
++* [Writing code](#writing-code)
++* [How Clippy works](#how-clippy-works)
++* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
++* [Issue and PR Triage](#issue-and-pr-triage)
++* [Bors and Homu](#bors-and-homu)
++* [Contributions](#contributions)
++
++[Discord]: https://discord.gg/rust-lang
++[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
++
++## Getting started
++
++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 [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
++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 with a bug just ask
++@Manishearth, @flip1995, @phansch or @yaahc.
++
++Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
++If you want to work on an issue, please leave a comment so that we can assign it to you!
++
++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 E-easy issue first.
++They are mostly classified as [`E-medium`], since they might be somewhat involved code wise,
++but not difficult per-se.
++
++[`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%20first%20issue
++[`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
++
++## 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
++
++## Fixing build failures caused by Rust
++
++Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of
++the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures
++caused by Rust updates, can be a good way to learn about Rust internals.
++
++In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit
++history][toolstate_commit_history]. You will then have to look for the last commit that contains
++`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component.
++[Here][toolstate_commit] is an example.
++
++The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and
++if they are bigger, they likely include some discussion that may help you to fix Clippy.
++
++To check if Clippy is available for a specific target platform, you can check
++the [rustup component history][rustup_component_history].
++
++If you decide to make Clippy work again with a Rust commit that breaks it,
++you probably want to install the latest Rust from master locally and run Clippy
++using that version of Rust.
++
++You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install
++[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`.
++
++After fixing the build failure on this repository, we can submit a pull request
++to [`rust-lang/rust`] to fix the toolstate.
++
++To submit a pull request, you should follow these steps:
++
++```bash
++# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory
++git submodule update --remote src/tools/clippy
++cargo update -p clippy
++git add -u
++git commit -m "Update Clippy"
++./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway
++# Open a PR in rust-lang/rust
++```
++
++[rustup_component_history]: https://rust-lang.github.io/rustup-components-history
++[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master
++[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727
++[rtim]: https://github.com/kennytm/rustup-toolchain-install-master
++[`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]. 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.
++
++## 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%20%3Aboom%3A
++[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A
++[homu]: https://github.com/rust-lang/homu
++[homu_instructions]: https://buildbot2.rust-lang.org/homu/
++[homu_queue]: https://buildbot2.rust-lang.org/homu/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
--- /dev/null
--- /dev/null
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++option. All files in the project carrying such notice may not be
++copied, modified, or distributed except according to those terms.
--- /dev/null
--- /dev/null
++[package]
++name = "clippy"
++version = "0.0.212"
++authors = [
++ "Manish Goregaokar <manishsmail@gmail.com>",
++ "Andre Bogus <bogusandre@gmail.com>",
++ "Georg Brandl <georg@python.org>",
++ "Martin Carton <cartonmartin@gmail.com>",
++ "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>"
++]
++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.0.212", path = "clippy_lints" }
++# end automatic update
++regex = "1"
++semver = "0.9"
++rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
++tempfile = { version = "3.1.0", optional = true }
++lazy_static = "1.0"
++
++[dev-dependencies]
++cargo_metadata = "0.9.0"
++compiletest_rs = { version = "0.5.0", features = ["tmp"] }
++tester = "0.7"
++lazy_static = "1.0"
++clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
++serde = { version = "1.0", features = ["derive"] }
++derive-new = "0.5"
++
++# A noop dependency that changes in the Rust repository, it's a bit of a hack.
++# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
++# for more information.
++rustc-workspace-hack = "1.0.0"
++
++[build-dependencies]
++rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
++
++[features]
++deny-warnings = []
++integration = ["tempfile"]
--- /dev/null
--- /dev/null
++ Apache License
++ Version 2.0, January 2004
++ http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++ "License" shall mean the terms and conditions for use, reproduction,
++ and distribution as defined by Sections 1 through 9 of this document.
++
++ "Licensor" shall mean the copyright owner or entity authorized by
++ the copyright owner that is granting the License.
++
++ "Legal Entity" shall mean the union of the acting entity and all
++ other entities that control, are controlled by, or are under common
++ control with that entity. For the purposes of this definition,
++ "control" means (i) the power, direct or indirect, to cause the
++ direction or management of such entity, whether by contract or
++ otherwise, or (ii) ownership of fifty percent (50%) or more of the
++ outstanding shares, or (iii) beneficial ownership of such entity.
++
++ "You" (or "Your") shall mean an individual or Legal Entity
++ exercising permissions granted by this License.
++
++ "Source" form shall mean the preferred form for making modifications,
++ including but not limited to software source code, documentation
++ source, and configuration files.
++
++ "Object" form shall mean any form resulting from mechanical
++ transformation or translation of a Source form, including but
++ not limited to compiled object code, generated documentation,
++ and conversions to other media types.
++
++ "Work" shall mean the work of authorship, whether in Source or
++ Object form, made available under the License, as indicated by a
++ copyright notice that is included in or attached to the work
++ (an example is provided in the Appendix below).
++
++ "Derivative Works" shall mean any work, whether in Source or Object
++ form, that is based on (or derived from) the Work and for which the
++ editorial revisions, annotations, elaborations, or other modifications
++ represent, as a whole, an original work of authorship. For the purposes
++ of this License, Derivative Works shall not include works that remain
++ separable from, or merely link (or bind by name) to the interfaces of,
++ the Work and Derivative Works thereof.
++
++ "Contribution" shall mean any work of authorship, including
++ the original version of the Work and any modifications or additions
++ to that Work or Derivative Works thereof, that is intentionally
++ submitted to Licensor for inclusion in the Work by the copyright owner
++ or by an individual or Legal Entity authorized to submit on behalf of
++ the copyright owner. For the purposes of this definition, "submitted"
++ means any form of electronic, verbal, or written communication sent
++ to the Licensor or its representatives, including but not limited to
++ communication on electronic mailing lists, source code control systems,
++ and issue tracking systems that are managed by, or on behalf of, the
++ Licensor for the purpose of discussing and improving the Work, but
++ excluding communication that is conspicuously marked or otherwise
++ designated in writing by the copyright owner as "Not a Contribution."
++
++ "Contributor" shall mean Licensor and any individual or Legal Entity
++ on behalf of whom a Contribution has been received by Licensor and
++ subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++ this License, each Contributor hereby grants to You a perpetual,
++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++ copyright license to reproduce, prepare Derivative Works of,
++ publicly display, publicly perform, sublicense, and distribute the
++ Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++ this License, each Contributor hereby grants to You a perpetual,
++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++ (except as stated in this section) patent license to make, have made,
++ use, offer to sell, sell, import, and otherwise transfer the Work,
++ where such license applies only to those patent claims licensable
++ by such Contributor that are necessarily infringed by their
++ Contribution(s) alone or by combination of their Contribution(s)
++ with the Work to which such Contribution(s) was submitted. If You
++ institute patent litigation against any entity (including a
++ cross-claim or counterclaim in a lawsuit) alleging that the Work
++ or a Contribution incorporated within the Work constitutes direct
++ or contributory patent infringement, then any patent licenses
++ granted to You under this License for that Work shall terminate
++ as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++ Work or Derivative Works thereof in any medium, with or without
++ modifications, and in Source or Object form, provided that You
++ meet the following conditions:
++
++ (a) You must give any other recipients of the Work or
++ Derivative Works a copy of this License; and
++
++ (b) You must cause any modified files to carry prominent notices
++ stating that You changed the files; and
++
++ (c) You must retain, in the Source form of any Derivative Works
++ that You distribute, all copyright, patent, trademark, and
++ attribution notices from the Source form of the Work,
++ excluding those notices that do not pertain to any part of
++ the Derivative Works; and
++
++ (d) If the Work includes a "NOTICE" text file as part of its
++ distribution, then any Derivative Works that You distribute must
++ include a readable copy of the attribution notices contained
++ within such NOTICE file, excluding those notices that do not
++ pertain to any part of the Derivative Works, in at least one
++ of the following places: within a NOTICE text file distributed
++ as part of the Derivative Works; within the Source form or
++ documentation, if provided along with the Derivative Works; or,
++ within a display generated by the Derivative Works, if and
++ wherever such third-party notices normally appear. The contents
++ of the NOTICE file are for informational purposes only and
++ do not modify the License. You may add Your own attribution
++ notices within Derivative Works that You distribute, alongside
++ or as an addendum to the NOTICE text from the Work, provided
++ that such additional attribution notices cannot be construed
++ as modifying the License.
++
++ You may add Your own copyright statement to Your modifications and
++ may provide additional or different license terms and conditions
++ for use, reproduction, or distribution of Your modifications, or
++ for any such Derivative Works as a whole, provided Your use,
++ reproduction, and distribution of the Work otherwise complies with
++ the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++ any Contribution intentionally submitted for inclusion in the Work
++ by You to the Licensor shall be under the terms and conditions of
++ this License, without any additional terms or conditions.
++ Notwithstanding the above, nothing herein shall supersede or modify
++ the terms of any separate license agreement you may have executed
++ with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++ names, trademarks, service marks, or product names of the Licensor,
++ except as required for reasonable and customary use in describing the
++ origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++ agreed to in writing, Licensor provides the Work (and each
++ Contributor provides its Contributions) on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++ implied, including, without limitation, any warranties or conditions
++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++ PARTICULAR PURPOSE. You are solely responsible for determining the
++ appropriateness of using or redistributing the Work and assume any
++ risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++ whether in tort (including negligence), contract, or otherwise,
++ unless required by applicable law (such as deliberate and grossly
++ negligent acts) or agreed to in writing, shall any Contributor be
++ liable to You for damages, including any direct, indirect, special,
++ incidental, or consequential damages of any character arising as a
++ result of this License or out of the use or inability to use the
++ Work (including but not limited to damages for loss of goodwill,
++ work stoppage, computer failure or malfunction, or any and all
++ other commercial damages or losses), even if such Contributor
++ has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++ the Work or Derivative Works thereof, You may choose to offer,
++ and charge a fee for, acceptance of support, warranty, indemnity,
++ or other liability obligations and/or rights consistent with this
++ License. However, in accepting such obligations, You may act only
++ on Your own behalf and on Your sole responsibility, not on behalf
++ of any other Contributor, and only if You agree to indemnify,
++ defend, and hold each Contributor harmless for any liability
++ incurred by, or claims asserted against, such Contributor by reason
++ of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++ To apply the Apache License to your work, attach the following
++ boilerplate notice, with the fields enclosed by brackets "[]"
++ replaced with your own identifying information. (Don't include
++ the brackets!) The text should be enclosed in the appropriate
++ comment syntax for the file format. We also recommend that a
++ file or class name and description of purpose be included on the
++ same "printed page" as the copyright notice for easier
++ identification within third-party archives.
++
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file except in compliance with the License.
++You may obtain a copy of the License at
++
++ http://www.apache.org/licenses/LICENSE-2.0
++
++Unless required by applicable law or agreed to in writing, software
++distributed under the License is distributed on an "AS IS" BASIS,
++WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++See the License for the specific language governing permissions and
++limitations under the License.
--- /dev/null
--- /dev/null
++MIT License
++
++Copyright (c) 2014-2020 The Rust Project Developers
++
++Permission is hereby granted, free of charge, to any
++person obtaining a copy of this software and associated
++documentation files (the "Software"), to deal in the
++Software without restriction, including without
++limitation the rights to use, copy, modify, merge,
++publish, distribute, sublicense, and/or sell copies of
++the Software, and to permit persons to whom the Software
++is furnished to do so, subject to the following
++conditions:
++
++The above copyright notice and this permission notice
++shall be included in all copies or substantial portions
++of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
++ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
++TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
++PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
++SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
++IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++DEALINGS IN THE SOFTWARE.
--- /dev/null
--- /dev/null
++# 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 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
++
++We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
++
++* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`)
++* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default)
++* `clippy::style` (code that should be written in a more idiomatic way)
++* `clippy::complexity` (code that does something simple but in a complex way)
++* `clippy::perf` (code that can be written in a faster way)
++* `clippy::pedantic` (lints which are rather strict, off by default)
++* `clippy::nursery` (new lints that aren't quite ready yet, off by default)
++* `clippy::cargo` (checks against the cargo manifest, off by default)
++
++More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
++
++Only the following of those categories are enabled by default:
++
++* `clippy::style`
++* `clippy::correctness`
++* `clippy::complexity`
++* `clippy::perf`
++
++Other categories need to be enabled in order for their lints to be executed.
++
++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
++
++Since this is a tool for helping the developer of a library or application
++write better code, it is recommended not to include Clippy as a hard dependency.
++Options include using it as an optional dependency, as a cargo subcommand, or
++as an included feature during build. These options are detailed below.
++
++### 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
++
++Clippy can automatically apply some lint suggestions.
++Note that this is still experimental and only supported on the nightly channel:
++
++```terminal
++cargo clippy --fix -Z unstable-options
++```
++
++### Running Clippy from the command line without installing it
++
++To have cargo compile your crate with Clippy without Clippy installation
++in your code, you can use:
++
++```terminal
++cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
++```
++
++*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
++
++### 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.
++```
++
++If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
++In this case you can try to conditionally install Clippy from the Git repo.
++
++```yaml
++language: rust
++rust:
++ - nightly
++before_script:
++ - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
++ # 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
++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: `deny` produces errors instead of warnings.
++
++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: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and
++`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you
++can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic`
++If you care only about a single lint, you can allow all others and then explicitly reenable
++the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...`
++
++## 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-2020 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.
--- /dev/null
--- /dev/null
++fn main() {
++ // Forward the profile to the main compilation
++ println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap());
++ // Don't rebuild even if nothing changed
++ println!("cargo:rerun-if-changed=build.rs");
++ // forward git repo hashes we build at
++ println!(
++ "cargo:rustc-env=GIT_HASH={}",
++ rustc_tools_util::get_commit_hash().unwrap_or_default()
++ );
++ println!(
++ "cargo:rustc-env=COMMIT_DATE={}",
++ rustc_tools_util::get_commit_date().unwrap_or_default()
++ );
++ println!(
++ "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
++ rustc_tools_util::get_channel().unwrap_or_default()
++ );
++}
--- /dev/null
--- /dev/null
++[package]
++name = "clippy_dev"
++version = "0.0.1"
++authors = ["Philipp Hansch <dev@phansch.net>"]
++edition = "2018"
++
++[dependencies]
++bytecount = "0.6"
++clap = "2.33"
++itertools = "0.9"
++regex = "1"
++lazy_static = "1.0"
++shell-escape = "0.1"
++walkdir = "2"
++
++[features]
++deny-warnings = []
--- /dev/null
--- /dev/null
++use crate::clippy_project_root;
++use shell_escape::escape;
++use std::ffi::OsStr;
++use std::io;
++use std::path::Path;
++use std::process::{self, Command};
++use walkdir::WalkDir;
++
++#[derive(Debug)]
++pub enum CliError {
++ CommandFailed(String),
++ IoError(io::Error),
++ RustfmtNotInstalled,
++ WalkDirError(walkdir::Error),
++}
++
++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,
++}
++
++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();
++
++ 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"))?;
++
++ for entry in WalkDir::new(project_root.join("tests")) {
++ let entry = entry?;
++ let path = entry.path();
++
++ 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"
++ {
++ continue;
++ }
++
++ success &= rustfmt(context, &path)?;
++ }
++
++ Ok(success)
++ }
++
++ fn output_err(err: CliError) {
++ match err {
++ CliError::CommandFailed(command) => {
++ eprintln!("error: A command failed! `{}`", command);
++ },
++ CliError::IoError(err) => {
++ eprintln!("error: {}", err);
++ },
++ CliError::RustfmtNotInstalled => {
++ eprintln!("error: rustfmt nightly is not installed.");
++ },
++ CliError::WalkDirError(err) => {
++ eprintln!("error: {}", err);
++ },
++ }
++ }
++
++ 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 mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
++ let code = child.wait()?;
++ let success = code.success();
++
++ if !context.check && !success {
++ return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
++ }
++
++ 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)))
++ }
++}
++
++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)
++}
--- /dev/null
--- /dev/null
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use itertools::Itertools;
++use lazy_static::lazy_static;
++use regex::Regex;
++use std::collections::HashMap;
++use std::ffi::OsStr;
++use std::fs;
++use std::path::{Path, PathBuf};
++use walkdir::WalkDir;
++
++pub mod fmt;
++pub mod new_lint;
++pub mod stderr_length_check;
++pub mod update_lints;
++
++lazy_static! {
++ static ref DEC_CLIPPY_LINT_RE: Regex = 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 ref DEC_DEPRECATED_LINT_RE: Regex = 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 ref NL_ESCAPE_RE: Regex = 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>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++ let pre = " store.register_lints(&[".to_string();
++ let post = " ]);".to_string();
++ let mut inner = lints
++ .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
++ .sorted()
++ .collect::<Vec<String>>();
++ inner.insert(0, pre);
++ inner.push(post);
++ inner
++}
++
++/// 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.
++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);
++/// ```
++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
++#[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![]);
++ 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 _ = 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()));
++}
--- /dev/null
--- /dev/null
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use clap::{App, Arg, SubCommand};
++use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints};
++
++fn main() {
++ let matches = App::new("Clippy developer tooling")
++ .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/lib.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",
++ "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."),
++ )
++ .get_matches();
++
++ match matches.subcommand() {
++ ("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();
++ },
++ _ => {},
++ }
++}
--- /dev/null
--- /dev/null
++use crate::clippy_project_root;
++use std::fs::{File, OpenOptions};
++use std::io;
++use std::io::prelude::*;
++use std::io::ErrorKind;
++use std::path::Path;
++
++/// Creates files required to implement and test a new lint and runs `update_lints`.
++///
++/// # Errors
++///
++/// This function errors, if the files couldn't be created
++pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
++ let pass = pass.expect("`pass` argument is validated by clap");
++ let lint_name = lint_name.expect("`name` argument is validated by clap");
++ let category = category.expect("`category` argument is validated by clap");
++
++ match open_files(lint_name) {
++ Ok((mut test_file, mut lint_file)) => {
++ let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
++ "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
++ "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
++ _ => {
++ unreachable!("`pass_type` should only ever be `early` or `late`!");
++ },
++ };
++
++ let camel_case_name = to_camel_case(lint_name);
++
++ if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
++ return Err(io::Error::new(
++ ErrorKind::Other,
++ format!("Could not write to test file: {}", e),
++ ));
++ };
++
++ if let Err(e) = lint_file.write_all(
++ get_lint_file_contents(
++ pass_type,
++ pass_lifetimes,
++ lint_name,
++ &camel_case_name,
++ category,
++ pass_import,
++ context_import,
++ )
++ .as_bytes(),
++ ) {
++ return Err(io::Error::new(
++ ErrorKind::Other,
++ format!("Could not write to lint file: {}", e),
++ ));
++ }
++ Ok(())
++ },
++ Err(e) => Err(io::Error::new(
++ ErrorKind::Other,
++ format!("Unable to create lint: {}", e),
++ )),
++ }
++}
++
++fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
++ let project_root = clippy_project_root();
++
++ let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
++ let lint_file_path = project_root
++ .join("clippy_lints")
++ .join("src")
++ .join(format!("{}.rs", lint_name));
++
++ if Path::new(&test_file_path).exists() {
++ return Err(io::Error::new(
++ ErrorKind::AlreadyExists,
++ format!("test file {:?} already exists", test_file_path),
++ ));
++ }
++ if Path::new(&lint_file_path).exists() {
++ return Err(io::Error::new(
++ ErrorKind::AlreadyExists,
++ format!("lint file {:?} already exists", lint_file_path),
++ ));
++ }
++
++ let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
++ let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
++
++ Ok((test_file, lint_file))
++}
++
++fn to_camel_case(name: &str) -> String {
++ name.split('_')
++ .map(|s| {
++ if s.is_empty() {
++ String::from("")
++ } else {
++ [&s[0..1].to_uppercase(), &s[1..]].concat()
++ }
++ })
++ .collect()
++}
++
++fn get_test_file_contents(lint_name: &str) -> String {
++ format!(
++ "#![warn(clippy::{})]
++
++fn main() {{
++ // test code goes here
++}}
++",
++ lint_name
++ )
++}
++
++fn get_lint_file_contents(
++ pass_type: &str,
++ pass_lifetimes: &str,
++ lint_name: &str,
++ camel_case_name: &str,
++ category: &str,
++ pass_import: &str,
++ context_import: &str,
++) -> String {
++ format!(
++ "use rustc_lint::{{{type}, {context_import}}};
++use rustc_session::{{declare_lint_pass, declare_tool_lint}};
++{pass_import}
++
++declare_clippy_lint! {{
++ /// **What it does:**
++ ///
++ /// **Why is this bad?**
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // example code where clippy issues a warning
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// // example code which does not raise clippy warning
++ /// ```
++ pub {name_upper},
++ {category},
++ \"default lint description\"
++}}
++
++declare_lint_pass!({name_camel} => [{name_upper}]);
++
++impl {type}{lifetimes} for {name_camel} {{}}
++",
++ type=pass_type,
++ lifetimes=pass_lifetimes,
++ name_upper=lint_name.to_uppercase(),
++ name_camel=camel_case_name,
++ category=category,
++ pass_import=pass_import,
++ context_import=context_import
++ )
++}
++
++#[test]
++fn test_camel_case() {
++ let s = "a_lint";
++ let s2 = to_camel_case(s);
++ assert_eq!(s2, "ALint");
++
++ let name = "a_really_long_new_lint";
++ let name2 = to_camel_case(name);
++ assert_eq!(name2, "AReallyLongNewLint");
++
++ let name3 = "lint__name";
++ let name4 = to_camel_case(name3);
++ assert_eq!(name4, "LintName");
++}
--- /dev/null
--- /dev/null
++use crate::clippy_project_root;
++use std::ffi::OsStr;
++use std::fs;
++use std::path::{Path, PathBuf};
++use walkdir::WalkDir;
++
++// The maximum length allowed for stderr files.
++//
++// We limit this because small files are easier to deal with than bigger files.
++const LENGTH_LIMIT: usize = 200;
++
++pub fn check() {
++ let exceeding_files: Vec<_> = exceeding_stderr_files();
++
++ if !exceeding_files.is_empty() {
++ eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
++ for (path, count) in exceeding_files {
++ println!("{}: {}", path.display(), count);
++ }
++ std::process::exit(1);
++ }
++}
++
++fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> {
++ // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
++ WalkDir::new(clippy_project_root().join("tests/ui"))
++ .into_iter()
++ .filter_map(Result::ok)
++ .filter(|f| !f.file_type().is_dir())
++ .filter_map(|e| {
++ let p = e.into_path();
++ let count = count_linenumbers(&p);
++ if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT {
++ Some((p, count))
++ } else {
++ None
++ }
++ })
++ .collect()
++}
++
++#[must_use]
++fn count_linenumbers(filepath: &Path) -> usize {
++ match fs::read(filepath) {
++ Ok(content) => bytecount::count(&content, b'\n'),
++ Err(e) => {
++ eprintln!("Failed to read file: {}", e);
++ 0
++ },
++ }
++}
--- /dev/null
--- /dev/null
++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 = replace_region_in_file(
++ Path::new("src/lintlist/mod.rs"),
++ "begin lint list",
++ "end lint list",
++ false,
++ update_mode == UpdateMode::Change,
++ || {
++ format!("pub static ref ALL_LINTS: Vec<Lint> = vec!{:#?};", sorted_usable_lints)
++ .lines()
++ .map(ToString::to_string)
++ .collect::<Vec<_>>()
++ },
++ )
++ .changed;
++
++ 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(usable_lints.iter().chain(internal_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| {
++ l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "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
++}
--- /dev/null
--- /dev/null
++[package]
++name = "clippy_dummy" # rename to clippy before publishing
++version = "0.0.303"
++authors = ["Manish Goregaokar <manishsmail@gmail.com>"]
++edition = "2018"
++readme = "crates-readme.md"
++description = "A bunch of helpful lints to avoid common pitfalls in Rust."
++build = 'build.rs'
++
++repository = "https://github.com/rust-lang/rust-clippy"
++
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++categories = ["development-tools", "development-tools::cargo-plugins"]
++
++[build-dependencies]
++term = "0.6"
--- /dev/null
--- /dev/null
++This is a dummy crate to publish to crates.io. It primarily exists to ensure
++that folks trying to install clippy from crates.io get redirected to the
++`rustup` technique.
++
++Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`,
++it has a different name to avoid workspace issues.
--- /dev/null
--- /dev/null
++use term::color::{GREEN, RED, WHITE};
++use term::{Attr, Error, Result};
++
++fn main() {
++ if foo().is_err() {
++ eprintln!(
++ "error: Clippy is no longer available via crates.io\n\n\
++ help: please run `rustup component add clippy` instead"
++ );
++ }
++ std::process::exit(1);
++}
++
++fn foo() -> Result<()> {
++ let mut t = term::stderr().ok_or(Error::NotSupported)?;
++
++ t.attr(Attr::Bold)?;
++ t.fg(RED)?;
++ write!(t, "\nerror: ")?;
++
++ t.reset()?;
++ t.fg(WHITE)?;
++ writeln!(t, "Clippy is no longer available via crates.io\n")?;
++
++ t.attr(Attr::Bold)?;
++ t.fg(GREEN)?;
++ write!(t, "help: ")?;
++
++ t.reset()?;
++ t.fg(WHITE)?;
++ write!(t, "please run `")?;
++
++ t.attr(Attr::Bold)?;
++ write!(t, "rustup component add clippy")?;
++
++ t.reset()?;
++ t.fg(WHITE)?;
++ writeln!(t, "` instead")?;
++
++ t.reset()?;
++ Ok(())
++}
--- /dev/null
--- /dev/null
++Installing clippy via crates.io is deprecated. Please use the following:
++
++```terminal
++rustup component add clippy
++```
++
++on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
++
++See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information
--- /dev/null
--- /dev/null
++fn main() {
++ panic!("This shouldn't even compile")
++}
--- /dev/null
--- /dev/null
++[package]
++name = "clippy_lints"
++# begin automatic update
++version = "0.0.212"
++# end automatic update
++authors = [
++ "Manish Goregaokar <manishsmail@gmail.com>",
++ "Andre Bogus <bogusandre@gmail.com>",
++ "Georg Brandl <georg@python.org>",
++ "Martin Carton <cartonmartin@gmail.com>"
++]
++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.9.0"
++if_chain = "1.0.0"
++itertools = "0.9"
++lazy_static = "1.0.2"
++pulldown-cmark = { version = "0.7", default-features = false }
++quine-mc_cluskey = "0.2.2"
++regex-syntax = "0.6"
++serde = { version = "1.0", features = ["derive"] }
++smallvec = { version = "1", features = ["union"] }
++toml = "0.5.3"
++unicode-normalization = "0.1"
++semver = "0.9.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 = []
--- /dev/null
--- /dev/null
++This crate contains Clippy lints. For the main crate, check [GitHub](https://github.com/rust-lang/rust-clippy).
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol;
++use std::f64::consts as f64;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for floating point literals that approximate
++ /// constants which are defined in
++ /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
++ /// or
++ /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
++ /// respectively, suggesting to use the predefined constant.
++ ///
++ /// **Why is this bad?** Usually, the definition in the standard library is more
++ /// precise than what people come up with. If you find that your definition is
++ /// actually more precise, please [file a Rust
++ /// issue](https://github.com/rust-lang/rust/issues).
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 3.14;
++ /// let y = 1_f64 / x;
++ /// ```
++ /// Use predefined constants instead:
++ /// ```rust
++ /// let x = std::f32::consts::PI;
++ /// let y = std::f64::consts::FRAC_1_PI;
++ /// ```
++ pub APPROX_CONSTANT,
++ correctness,
++ "the approximate of a known float constant (in `std::fXX::consts`)"
++}
++
++// Tuples are of the form (constant, name, min_digits)
++const KNOWN_CONSTS: [(f64, &str, usize); 18] = [
++ (f64::E, "E", 4),
++ (f64::FRAC_1_PI, "FRAC_1_PI", 4),
++ (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
++ (f64::FRAC_2_PI, "FRAC_2_PI", 5),
++ (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
++ (f64::FRAC_PI_2, "FRAC_PI_2", 5),
++ (f64::FRAC_PI_3, "FRAC_PI_3", 5),
++ (f64::FRAC_PI_4, "FRAC_PI_4", 5),
++ (f64::FRAC_PI_6, "FRAC_PI_6", 5),
++ (f64::FRAC_PI_8, "FRAC_PI_8", 5),
++ (f64::LN_10, "LN_10", 5),
++ (f64::LN_2, "LN_2", 5),
++ (f64::LOG10_E, "LOG10_E", 5),
++ (f64::LOG2_E, "LOG2_E", 5),
++ (f64::LOG2_10, "LOG2_10", 5),
++ (f64::LOG10_2, "LOG10_2", 5),
++ (f64::PI, "PI", 3),
++ (f64::SQRT_2, "SQRT_2", 5),
++];
++
++declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::Lit(lit) = &e.kind {
++ check_lit(cx, &lit.node, e);
++ }
++ }
++}
++
++fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr<'_>) {
++ match *lit {
++ LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
++ FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
++ FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
++ },
++ LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
++ _ => (),
++ }
++}
++
++fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
++ let s = s.as_str();
++ if s.parse::<f64>().is_ok() {
++ for &(constant, name, min_digits) in &KNOWN_CONSTS {
++ if is_approx_const(constant, &s, min_digits) {
++ span_lint(
++ cx,
++ APPROX_CONSTANT,
++ e.span,
++ &format!(
++ "approximate value of `{}::consts::{}` found. \
++ Consider using it directly",
++ module, &name
++ ),
++ );
++ return;
++ }
++ }
++ }
++}
++
++/// Returns `false` if the number of significant figures in `value` are
++/// less than `min_digits`; otherwise, returns true if `value` is equal
++/// to `constant`, rounded to the number of digits present in `value`.
++#[must_use]
++fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
++ if value.len() <= min_digits {
++ false
++ } else if constant.to_string().starts_with(value) {
++ // The value is a truncated constant
++ true
++ } else {
++ let round_const = format!("{:.*}", value.len() - 2, constant);
++ value == round_const
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::constant_simple;
++use crate::utils::span_lint;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for integer arithmetic operations which could overflow or panic.
++ ///
++ /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
++ /// of overflowing according to the [Rust
++ /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
++ /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
++ /// attempted.
++ ///
++ /// **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in
++ /// release mode. Division by zero will cause a panic in either mode. In some applications one
++ /// wants explicitly checked, wrapping or saturating arithmetic.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let a = 0;
++ /// a + 1;
++ /// ```
++ pub INTEGER_ARITHMETIC,
++ restriction,
++ "any integer arithmetic expression which could overflow or panic"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for float arithmetic.
++ ///
++ /// **Why is this bad?** For some embedded systems or kernel development, it
++ /// can be useful to rule out floating-point numbers.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let a = 0.0;
++ /// a + 1.0;
++ /// ```
++ pub FLOAT_ARITHMETIC,
++ restriction,
++ "any floating-point arithmetic statement"
++}
++
++#[derive(Copy, Clone, Default)]
++pub struct Arithmetic {
++ expr_span: Option<Span>,
++ /// This field is used to check whether expressions are constants, such as in enum discriminants
++ /// and consts
++ const_span: Option<Span>,
++}
++
++impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if self.expr_span.is_some() {
++ return;
++ }
++
++ if let Some(span) = self.const_span {
++ if span.contains(expr.span) {
++ return;
++ }
++ }
++ match &expr.kind {
++ hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
++ match op.node {
++ hir::BinOpKind::And
++ | hir::BinOpKind::Or
++ | hir::BinOpKind::BitAnd
++ | hir::BinOpKind::BitOr
++ | hir::BinOpKind::BitXor
++ | hir::BinOpKind::Eq
++ | hir::BinOpKind::Lt
++ | hir::BinOpKind::Le
++ | hir::BinOpKind::Ne
++ | hir::BinOpKind::Ge
++ | hir::BinOpKind::Gt => return,
++ _ => (),
++ }
++
++ let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
++ if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
++ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++ self.expr_span = Some(expr.span);
++ } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
++ span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++ self.expr_span = Some(expr.span);
++ }
++ },
++ hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
++ let ty = cx.tables.expr_ty(arg);
++ if constant_simple(cx, cx.tables, expr).is_none() {
++ if ty.is_integral() {
++ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++ self.expr_span = Some(expr.span);
++ } else if ty.is_floating_point() {
++ span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++ self.expr_span = Some(expr.span);
++ }
++ }
++ },
++ _ => (),
++ }
++ }
++
++ fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if Some(expr.span) == self.expr_span {
++ self.expr_span = None;
++ }
++ }
++
++ fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
++ let body_owner = cx.tcx.hir().body_owner(body.id());
++
++ match cx.tcx.hir().body_owner_kind(body_owner) {
++ hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
++ let body_span = cx.tcx.hir().span(body_owner);
++
++ if let Some(span) = self.const_span {
++ if span.contains(body_span) {
++ return;
++ }
++ }
++ self.const_span = Some(body_span);
++ },
++ hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
++ }
++ }
++
++ fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
++ let body_owner = cx.tcx.hir().body_owner(body.id());
++ let body_span = cx.tcx.hir().span(body_owner);
++
++ if let Some(span) = self.const_span {
++ if span.contains(body_span) {
++ return;
++ }
++ }
++ self.const_span = None;
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::span_lint_and_help;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `as` conversions.
++ ///
++ /// **Why is this bad?** `as` conversions will perform many kinds of
++ /// conversions, including silently lossy conversions and dangerous coercions.
++ /// There are cases when it makes sense to use `as`, so the lint is
++ /// Allow by default.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// let a: u32;
++ /// ...
++ /// f(a as u16);
++ /// ```
++ ///
++ /// Usually better represents the semantics you expect:
++ /// ```rust,ignore
++ /// f(a.try_into()?);
++ /// ```
++ /// or
++ /// ```rust,ignore
++ /// f(a.try_into().expect("Unexpected u16 overflow in f"));
++ /// ```
++ ///
++ pub AS_CONVERSIONS,
++ restriction,
++ "using a potentially dangerous silent `as` conversion"
++}
++
++declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
++
++impl EarlyLintPass for AsConversions {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ if let ExprKind::Cast(_, _) = expr.kind {
++ span_lint_and_help(
++ cx,
++ AS_CONVERSIONS,
++ expr.span,
++ "using a potentially dangerous silent `as` conversion",
++ None,
++ "consider using a safe wrapper for this conversion",
++ );
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, Constant};
++use crate::utils::paths;
++use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, snippet_opt, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
++ ///
++ /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
++ /// `panic!()` or `unreachable!()`
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// assert!(false)
++ /// assert!(true)
++ /// const B: bool = false;
++ /// assert!(B)
++ /// ```
++ pub ASSERTIONS_ON_CONSTANTS,
++ style,
++ "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
++}
++
++declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ let lint_true = |is_debug: bool| {
++ span_lint_and_help(
++ cx,
++ ASSERTIONS_ON_CONSTANTS,
++ e.span,
++ if is_debug {
++ "`debug_assert!(true)` will be optimized out by the compiler"
++ } else {
++ "`assert!(true)` will be optimized out by the compiler"
++ },
++ None,
++ "remove it",
++ );
++ };
++ let lint_false_without_message = || {
++ span_lint_and_help(
++ cx,
++ ASSERTIONS_ON_CONSTANTS,
++ e.span,
++ "`assert!(false)` should probably be replaced",
++ None,
++ "use `panic!()` or `unreachable!()`",
++ );
++ };
++ let lint_false_with_message = |panic_message: String| {
++ span_lint_and_help(
++ cx,
++ ASSERTIONS_ON_CONSTANTS,
++ e.span,
++ &format!("`assert!(false, {})` should probably be replaced", panic_message),
++ None,
++ &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
++ )
++ };
++
++ if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
++ if debug_assert_span.from_expansion() {
++ return;
++ }
++ if_chain! {
++ if let ExprKind::Unary(_, ref lit) = e.kind;
++ if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit);
++ if is_true;
++ then {
++ lint_true(true);
++ }
++ };
++ } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
++ if assert_span.from_expansion() {
++ return;
++ }
++ if let Some(assert_match) = match_assert_with_message(&cx, e) {
++ match assert_match {
++ // matched assert but not message
++ AssertKind::WithoutMessage(false) => lint_false_without_message(),
++ AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
++ AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
++ };
++ }
++ }
++ }
++}
++
++/// Result of calling `match_assert_with_message`.
++enum AssertKind {
++ WithMessage(String, bool),
++ WithoutMessage(bool),
++}
++
++/// Check if the expression matches
++///
++/// ```rust,ignore
++/// match { let _t = !c; _t } {
++/// true => {
++/// {
++/// ::std::rt::begin_panic(message, _)
++/// }
++/// }
++/// _ => { }
++/// };
++/// ```
++///
++/// where `message` is any expression and `c` is a constant bool.
++fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
++ if_chain! {
++ if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
++ // matches { let _t = expr; _t }
++ if let ExprKind::DropTemps(ref expr) = expr.kind;
++ if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
++ // bind the first argument of the `assert!` macro
++ if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr);
++ // arm 1 pattern
++ if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
++ if let ExprKind::Lit(ref lit) = lit_expr.kind;
++ if let LitKind::Bool(true) = lit.node;
++ // arm 1 block
++ if let ExprKind::Block(ref block, _) = arms[0].body.kind;
++ if block.stmts.is_empty();
++ if let Some(block_expr) = &block.expr;
++ if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
++ if let Some(begin_panic_call) = &inner_block.expr;
++ // function call
++ if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
++ if args.len() == 1;
++ // bind the second argument of the `assert!` macro if it exists
++ if let panic_message = snippet_opt(cx, args[0].span);
++ // second argument of begin_panic is irrelevant
++ // as is the second match arm
++ then {
++ // an empty message occurs when it was generated by the macro
++ // (and not passed by the user)
++ return panic_message
++ .filter(|msg| !msg.is_empty())
++ .map(|msg| AssertKind::WithMessage(msg, is_true))
++ .or(Some(AssertKind::WithoutMessage(is_true)));
++ }
++ }
++ None
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
++};
++use crate::utils::{higher, 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;
++ /// // ...
++ /// a = 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,
++ complexity,
++ "having a variable on both sides of an assign op"
++}
++
++declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
++ #[allow(clippy::too_many_lines)]
++ fn check_expr(&mut self, cx: &LateContext<'a, '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 SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
++ lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
++ }
++ // lhs op= l commutative_op r
++ if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(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.tables.expr_ty(assignee);
++ let rty = cx.tables.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] = crate::utils::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 SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
++ lint(assignee, r);
++ }
++ // a = b commutative_op a
++ // Limited to primitive type as these ops are know to be commutative
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
++ && cx.tables.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<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
++ self.counter += 1;
++ }
++
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::def_id::DefId;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of invalid atomic
++ /// ordering in atomic loads/stores and memory fences.
++ ///
++ /// **Why is this bad?** Using an invalid atomic ordering
++ /// will cause a panic at run-time.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,no_run
++ /// # use std::sync::atomic::{self, AtomicBool, Ordering};
++ ///
++ /// let x = AtomicBool::new(true);
++ ///
++ /// let _ = x.load(Ordering::Release);
++ /// let _ = x.load(Ordering::AcqRel);
++ ///
++ /// x.store(false, Ordering::Acquire);
++ /// x.store(false, Ordering::AcqRel);
++ ///
++ /// atomic::fence(Ordering::Relaxed);
++ /// atomic::compiler_fence(Ordering::Relaxed);
++ /// ```
++ pub INVALID_ATOMIC_ORDERING,
++ correctness,
++ "usage of invalid atomic ordering in atomic loads/stores and memory fences"
++}
++
++declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
++
++const ATOMIC_TYPES: [&str; 12] = [
++ "AtomicBool",
++ "AtomicI8",
++ "AtomicI16",
++ "AtomicI32",
++ "AtomicI64",
++ "AtomicIsize",
++ "AtomicPtr",
++ "AtomicU8",
++ "AtomicU16",
++ "AtomicU32",
++ "AtomicU64",
++ "AtomicUsize",
++];
++
++fn type_is_atomic(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.tables.expr_ty(expr).kind {
++ ATOMIC_TYPES
++ .iter()
++ .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
++ } else {
++ false
++ }
++}
++
++fn match_ordering_def_path(cx: &LateContext<'_, '_>, did: DefId, orderings: &[&str]) -> bool {
++ orderings
++ .iter()
++ .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
++}
++
++fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind;
++ let method = method_path.ident.name.as_str();
++ if type_is_atomic(cx, &args[0]);
++ if method == "load" || method == "store";
++ let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
++ if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
++ if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
++ then {
++ if method == "load" &&
++ match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
++ span_lint_and_help(
++ cx,
++ INVALID_ATOMIC_ORDERING,
++ ordering_arg.span,
++ "atomic loads cannot have `Release` and `AcqRel` ordering",
++ None,
++ "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
++ );
++ } else if method == "store" &&
++ match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
++ span_lint_and_help(
++ cx,
++ INVALID_ATOMIC_ORDERING,
++ ordering_arg.span,
++ "atomic stores cannot have `Acquire` and `AcqRel` ordering",
++ None,
++ "consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
++ );
++ }
++ }
++ }
++}
++
++fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref func, ref args) = expr.kind;
++ if let ExprKind::Path(ref func_qpath) = func.kind;
++ if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++ if ["fence", "compiler_fence"]
++ .iter()
++ .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
++ if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
++ if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
++ if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
++ then {
++ span_lint_and_help(
++ cx,
++ INVALID_ATOMIC_ORDERING,
++ args[0].span,
++ "memory fences cannot have `Relaxed` ordering",
++ None,
++ "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
++ );
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ check_atomic_load_store(cx, expr);
++ check_memory_fence(cx, expr);
++ }
++}
--- /dev/null
--- /dev/null
++//! checks for attributes
++
++use crate::reexport::Name;
++use crate::utils::{
++ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg,
++ span_lint_and_then, without_block_comments,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
++use rustc_ast::util::lev_distance::find_best_match_for_name;
++use rustc_errors::Applicability;
++use rustc_hir::{
++ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
++};
++use rustc_lint::{CheckLintNameResult, 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::symbol::Symbol;
++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] = &["cloudabi", "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 whitelists `#[allow(unused_imports)]`, `#[allow(deprecated)]` and
++ /// `#[allow(unreachable_pub)]` 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)
++ /// #![inline(always)]
++ ///
++ /// fn this_is_fine() { }
++ ///
++ /// // Bad
++ /// #[inline(always)]
++ ///
++ /// fn not_quite_good_code() { }
++ ///
++ /// // Good (as outer attribute)
++ /// #[inline(always)]
++ /// 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 `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
++ /// lints and if those lints exist in clippy. If there is an uppercase letter in the lint name
++ /// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
++ /// the lint name.
++ ///
++ /// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust
++ /// #![warn(if_not_els)]
++ /// #![deny(clippy::All)]
++ /// ```
++ ///
++ /// Good:
++ /// ```rust
++ /// #![warn(if_not_else)]
++ /// #![deny(clippy::all)]
++ /// ```
++ pub UNKNOWN_CLIPPY_LINTS,
++ style,
++ "unknown_lints for scoped Clippy lints"
++}
++
++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,
++ EMPTY_LINE_AFTER_OUTER_ATTR,
++ UNKNOWN_CLIPPY_LINTS,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes {
++ fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
++ if let Some(items) = &attr.meta_item_list() {
++ if let Some(ident) = attr.ident() {
++ match &*ident.as_str() {
++ "allow" | "warn" | "deny" | "forbid" => {
++ check_clippy_lint_names(cx, items);
++ },
++ _ => {},
++ }
++ if items.is_empty() || !attr.check_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.check_name(sym!(since));
++ then {
++ check_semver(cx, item.span(), lit);
++ }
++ }
++ }
++ }
++ }
++ }
++
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if is_relevant_item(cx, item) {
++ check_attrs(cx, item.span, item.ident.name, &item.attrs)
++ }
++ match item.kind {
++ ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
++ let skip_unused_imports = item.attrs.iter().any(|attr| attr.check_name(sym!(macro_use)));
++
++ for attr in item.attrs {
++ if in_external_macro(cx.sess(), attr.span) {
++ return;
++ }
++ if let Some(lint_list) = &attr.meta_item_list() {
++ if let Some(ident) = attr.ident() {
++ match &*ident.as_str() {
++ "allow" | "warn" | "deny" | "forbid" => {
++ // whitelist `unused_imports`, `deprecated` and `unreachable_pub` 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))
++ {
++ 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<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++ if is_relevant_impl(cx, item) {
++ check_attrs(cx, item.span, item.ident.name, &item.attrs)
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++ if is_relevant_trait(cx, item) {
++ check_attrs(cx, item.span, item.ident.name, &item.attrs)
++ }
++ }
++}
++
++#[allow(clippy::single_match_else)]
++fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) {
++ let lint_store = cx.lints();
++ for lint in items {
++ 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.as_str() == "clippy";
++ let name = meta_item.path.segments.last().unwrap().ident.name;
++ if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
++ &name.as_str(),
++ Some(tool_name.name),
++ );
++ then {
++ span_lint_and_then(
++ cx,
++ UNKNOWN_CLIPPY_LINTS,
++ lint.span(),
++ &format!("unknown clippy lint: clippy::{}", name),
++ |diag| {
++ let name_lower = name.as_str().to_lowercase();
++ let symbols = lint_store.get_lints().iter().map(
++ |l| Symbol::intern(&l.name_lower())
++ ).collect::<Vec<_>>();
++ let sugg = find_best_match_for_name(
++ symbols.iter(),
++ &format!("clippy::{}", name_lower),
++ None,
++ );
++ if name.as_str().chars().any(char::is_uppercase)
++ && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
++ diag.span_suggestion(
++ lint.span(),
++ "lowercase the lint name",
++ format!("clippy::{}", name_lower),
++ Applicability::MachineApplicable,
++ );
++ } else if let Some(sugg) = sugg {
++ diag.span_suggestion(
++ lint.span(),
++ "did you mean",
++ sugg.to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ );
++ }
++ };
++ }
++}
++
++fn is_relevant_item(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool {
++ if let ItemKind::Fn(_, _, eid) = item.kind {
++ is_relevant_expr(cx, cx.tcx.body_tables(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.body_tables(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.body_tables(eid), &cx.tcx.hir().body(eid).value)
++ },
++ _ => false,
++ }
++}
++
++fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
++ if let Some(stmt) = block.stmts.first() {
++ match &stmt.kind {
++ StmtKind::Local(_) => true,
++ StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
++ _ => false,
++ }
++ } else {
++ block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
++ }
++}
++
++fn is_relevant_expr(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
++ match &expr.kind {
++ ExprKind::Block(block, _) => is_relevant_block(cx, tables, block),
++ ExprKind::Ret(Some(e)) => is_relevant_expr(cx, tables, e),
++ ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
++ ExprKind::Call(path_expr, _) => {
++ if let ExprKind::Path(qpath) = &path_expr.kind {
++ if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
++ !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
++ } else {
++ true
++ }
++ } else {
++ true
++ }
++ },
++ _ => true,
++ }
++}
++
++fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attribute]) {
++ if span.from_expansion() {
++ return;
++ }
++
++ for attr in attrs {
++ let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
++ attr
++ } else {
++ continue;
++ };
++
++ 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(), span.lo(), span.ctxt());
++ let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), 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?",
++ );
++ }
++ }
++ }
++
++ if let Some(values) = attr.meta_item_list() {
++ if values.len() != 1 || !attr.check_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.check_name(expected)
++ } else {
++ false
++ }
++}
++
++declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
++
++impl EarlyLintPass for EarlyAttributes {
++ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
++ check_deprecated_cfg_attr(cx, attr);
++ check_mismatched_target_os(cx, attr);
++ }
++}
++
++fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
++ if_chain! {
++ // check cfg_attr
++ if attr.check_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.check_name(sym!(rustfmt));
++ // check for `rustfmt_skip` and `rustfmt::skip`
++ if let Some(skip_item) = &items[1].meta_item();
++ if skip_item.check_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));
++ }
++ }
++ },
++ _ => {},
++ }
++ }
++ }
++
++ mismatched
++ }
++
++ if_chain! {
++ if attr.check_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;
++ }
++ }
++ });
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, span_lint_and_note};
++use rustc_hir::def_id::DefId;
++use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::GeneratorInteriorTypeCause;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to await while holding a
++ /// non-async-aware MutexGuard.
++ ///
++ /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
++ /// are not designed to operator in an async context across await points.
++ ///
++ /// There are two potential solutions. One is to use an asynx-aware Mutex
++ /// type. Many asynchronous foundation crates provide such a Mutex type. The
++ /// other solution is to ensure the mutex is unlocked before calling await,
++ /// either by introducing a scope or an explicit call to Drop::drop.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust,ignore
++ /// use std::sync::Mutex;
++ ///
++ /// async fn foo(x: &Mutex<u32>) {
++ /// let guard = x.lock().unwrap();
++ /// *guard += 1;
++ /// bar.await;
++ /// }
++ /// ```
++ ///
++ /// Use instead:
++ /// ```rust,ignore
++ /// use std::sync::Mutex;
++ ///
++ /// async fn foo(x: &Mutex<u32>) {
++ /// {
++ /// let guard = x.lock().unwrap();
++ /// *guard += 1;
++ /// }
++ /// bar.await;
++ /// }
++ /// ```
++ pub AWAIT_HOLDING_LOCK,
++ pedantic,
++ "Inside an async function, holding a MutexGuard while calling await"
++}
++
++declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
++
++impl LateLintPass<'_, '_> for AwaitHoldingLock {
++ fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) {
++ use AsyncGeneratorKind::{Block, Closure, Fn};
++ match body.generator_kind {
++ Some(GeneratorKind::Async(Block))
++ | Some(GeneratorKind::Async(Closure))
++ | Some(GeneratorKind::Async(Fn)) => {
++ let body_id = BodyId {
++ hir_id: body.value.hir_id,
++ };
++ let def_id = cx.tcx.hir().body_owner_def_id(body_id);
++ let tables = cx.tcx.typeck_tables_of(def_id);
++ check_interior_types(cx, &tables.generator_interior_types, body.value.span);
++ },
++ _ => {},
++ }
++ }
++}
++
++fn check_interior_types(cx: &LateContext<'_, '_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
++ for ty_cause in ty_causes {
++ if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind {
++ if is_mutex_guard(cx, adt.did) {
++ span_lint_and_note(
++ cx,
++ AWAIT_HOLDING_LOCK,
++ ty_cause.span,
++ "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.",
++ ty_cause.scope_span.or(Some(span)),
++ "these are all the await points this lock is held through",
++ );
++ }
++ }
++ }
++}
++
++fn is_mutex_guard(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
++ match_def_path(cx, def_id, &paths::MUTEX_GUARD)
++ || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
++ || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
++ || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
++ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
++ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, Constant};
++use crate::utils::sugg::Sugg;
++use crate::utils::{span_lint, span_lint_and_then};
++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_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for incompatible bit masks in comparisons.
++ ///
++ /// The formula for detecting if an expression of the type `_ <bit_op> m
++ /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
++ /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
++ /// table:
++ ///
++ /// |Comparison |Bit Op|Example |is always|Formula |
++ /// |------------|------|------------|---------|----------------------|
++ /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
++ /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
++ /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
++ /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
++ /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
++ /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
++ ///
++ /// **Why is this bad?** If the bits that the comparison cares about are always
++ /// set to zero or one by the bit mask, the comparison is constant `true` or
++ /// `false` (depending on mask, compared value, and operators).
++ ///
++ /// So the code is actively misleading, and the only reason someone would write
++ /// this intentionally is to win an underhanded Rust contest or create a
++ /// test-case for this lint.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// if (x & 1 == 2) { }
++ /// ```
++ pub BAD_BIT_MASK,
++ correctness,
++ "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for bit masks in comparisons which can be removed
++ /// without changing the outcome. The basic structure can be seen in the
++ /// following table:
++ ///
++ /// |Comparison| Bit Op |Example |equals |
++ /// |----------|---------|-----------|-------|
++ /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
++ /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
++ ///
++ /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
++ /// but still a bit misleading, because the bit mask is ineffective.
++ ///
++ /// **Known problems:** False negatives: This lint will only match instances
++ /// where we have figured out the math (which is for a power-of-two compared
++ /// value). This means things like `x | 1 >= 7` (which would be better written
++ /// as `x >= 6`) will not be reported (but bit masks like this are fairly
++ /// uncommon).
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// if (x | 1 > 3) { }
++ /// ```
++ pub INEFFECTIVE_BIT_MASK,
++ correctness,
++ "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for bit masks that can be replaced by a call
++ /// to `trailing_zeros`
++ ///
++ /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
++ /// == 0`
++ ///
++ /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// if x & 0b1111 == 0 { }
++ /// ```
++ pub VERBOSE_BIT_MASK,
++ style,
++ "expressions where a bit mask is less readable than the corresponding method call"
++}
++
++#[derive(Copy, Clone)]
++pub struct BitMask {
++ verbose_bit_mask_threshold: u64,
++}
++
++impl BitMask {
++ #[must_use]
++ pub fn new(verbose_bit_mask_threshold: u64) -> Self {
++ Self {
++ verbose_bit_mask_threshold,
++ }
++ }
++}
++
++impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(cmp, left, right) = &e.kind {
++ if cmp.node.is_comparison() {
++ if let Some(cmp_opt) = fetch_int_literal(cx, right) {
++ check_compare(cx, left, cmp.node, cmp_opt, e.span)
++ } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
++ check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span)
++ }
++ }
++ }
++ if_chain! {
++ if let ExprKind::Binary(op, left, right) = &e.kind;
++ if BinOpKind::Eq == op.node;
++ if let ExprKind::Binary(op1, left1, right1) = &left.kind;
++ if BinOpKind::BitAnd == op1.node;
++ if let ExprKind::Lit(lit) = &right1.kind;
++ if let LitKind::Int(n, _) = lit.node;
++ if let ExprKind::Lit(lit1) = &right.kind;
++ if let LitKind::Int(0, _) = lit1.node;
++ if n.leading_zeros() == n.count_zeros();
++ if n > u128::from(self.verbose_bit_mask_threshold);
++ then {
++ span_lint_and_then(cx,
++ VERBOSE_BIT_MASK,
++ e.span,
++ "bit mask could be simplified with a call to `trailing_zeros`",
++ |diag| {
++ let sugg = Sugg::hir(cx, left1, "...").maybe_par();
++ diag.span_suggestion(
++ e.span,
++ "try",
++ format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
++ Applicability::MaybeIncorrect,
++ );
++ });
++ }
++ }
++ }
++}
++
++#[must_use]
++fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
++ match cmp {
++ BinOpKind::Eq => BinOpKind::Eq,
++ BinOpKind::Ne => BinOpKind::Ne,
++ BinOpKind::Lt => BinOpKind::Gt,
++ BinOpKind::Gt => BinOpKind::Lt,
++ BinOpKind::Le => BinOpKind::Ge,
++ BinOpKind::Ge => BinOpKind::Le,
++ _ => BinOpKind::Or, // Dummy
++ }
++}
++
++fn check_compare(cx: &LateContext<'_, '_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
++ if let ExprKind::Binary(op, left, right) = &bit_op.kind {
++ if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
++ return;
++ }
++ fetch_int_literal(cx, right)
++ .or_else(|| fetch_int_literal(cx, left))
++ .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span))
++ }
++}
++
++#[allow(clippy::too_many_lines)]
++fn check_bit_mask(
++ cx: &LateContext<'_, '_>,
++ bit_op: BinOpKind,
++ cmp_op: BinOpKind,
++ mask_value: u128,
++ cmp_value: u128,
++ span: Span,
++) {
++ match cmp_op {
++ BinOpKind::Eq | BinOpKind::Ne => match bit_op {
++ BinOpKind::BitAnd => {
++ if mask_value & cmp_value != cmp_value {
++ if cmp_value != 0 {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ & {}` can never be equal to `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ }
++ } else if mask_value == 0 {
++ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++ }
++ },
++ BinOpKind::BitOr => {
++ if mask_value | cmp_value != cmp_value {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ | {}` can never be equal to `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ }
++ },
++ _ => (),
++ },
++ BinOpKind::Lt | BinOpKind::Ge => match bit_op {
++ BinOpKind::BitAnd => {
++ if mask_value < cmp_value {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ & {}` will always be lower than `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ } else if mask_value == 0 {
++ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++ }
++ },
++ BinOpKind::BitOr => {
++ if mask_value >= cmp_value {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ | {}` will never be lower than `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ } else {
++ check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
++ }
++ },
++ BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
++ _ => (),
++ },
++ BinOpKind::Le | BinOpKind::Gt => match bit_op {
++ BinOpKind::BitAnd => {
++ if mask_value <= cmp_value {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ & {}` will never be higher than `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ } else if mask_value == 0 {
++ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++ }
++ },
++ BinOpKind::BitOr => {
++ if mask_value > cmp_value {
++ span_lint(
++ cx,
++ BAD_BIT_MASK,
++ span,
++ &format!(
++ "incompatible bit mask: `_ | {}` will always be higher than `{}`",
++ mask_value, cmp_value
++ ),
++ );
++ } else {
++ check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
++ }
++ },
++ BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
++ _ => (),
++ },
++ _ => (),
++ }
++}
++
++fn check_ineffective_lt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
++ if c.is_power_of_two() && m < c {
++ span_lint(
++ cx,
++ INEFFECTIVE_BIT_MASK,
++ span,
++ &format!(
++ "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
++ op, m, c
++ ),
++ );
++ }
++}
++
++fn check_ineffective_gt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
++ if (c + 1).is_power_of_two() && m <= c {
++ span_lint(
++ cx,
++ INEFFECTIVE_BIT_MASK,
++ span,
++ &format!(
++ "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
++ op, m, c
++ ),
++ );
++ }
++}
++
++fn fetch_int_literal(cx: &LateContext<'_, '_>, lit: &Expr<'_>) -> Option<u128> {
++ match constant(cx, cx.tables, lit)?.0 {
++ Constant::Int(n) => Some(n),
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::{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>,
++}
++
++impl BlacklistedName {
++ pub fn new(blacklist: FxHashSet<String>) -> Self {
++ Self { blacklist }
++ }
++}
++
++impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlacklistedName {
++ fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++ 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),
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BlockCheckMode, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `if` conditions that use blocks to contain an
++ /// expression.
++ ///
++ /// **Why is this bad?** It isn't really Rust style, same as using parentheses
++ /// to contain expressions.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// if { true } { /* ... */ }
++ /// ```
++ pub BLOCK_IN_IF_CONDITION_EXPR,
++ style,
++ "braces that can be eliminated in conditions, e.g., `if { true } ...`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `if` conditions that use blocks containing
++ /// statements, or conditions that use closures with blocks.
++ ///
++ /// **Why is this bad?** Using blocks in the condition makes it hard to read.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// if { let x = somefunc(); x } {}
++ /// // or
++ /// if somefunc(|x| { x == 47 }) {}
++ /// ```
++ pub BLOCK_IN_IF_CONDITION_STMT,
++ style,
++ "complex blocks in conditions, e.g., `if { let x = true; x } ...`"
++}
++
++declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]);
++
++struct ExVisitor<'a, 'tcx> {
++ found_block: Option<&'tcx Expr<'tcx>>,
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++ if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
++ let body = self.cx.tcx.hir().body(eid);
++ let ex = &body.value;
++ if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
++ self.found_block = Some(ex);
++ return;
++ }
++ }
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
++const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
++ instead, move the block or closure higher and bind it with a `let`";
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++ if let Some((cond, _, _)) = higher::if_block(&expr) {
++ if let ExprKind::Block(block, _) = &cond.kind {
++ if block.rules == BlockCheckMode::DefaultBlock {
++ if block.stmts.is_empty() {
++ if let Some(ex) = &block.expr {
++ // don't dig into the expression here, just suggest that they remove
++ // the block
++ if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
++ return;
++ }
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ BLOCK_IN_IF_CONDITION_EXPR,
++ cond.span,
++ BRACED_EXPR_MESSAGE,
++ "try",
++ format!(
++ "{}",
++ snippet_block_with_applicability(
++ cx,
++ ex.span,
++ "..",
++ Some(expr.span),
++ &mut applicability
++ )
++ ),
++ applicability,
++ );
++ }
++ } else {
++ let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
++ if span.from_expansion() || differing_macro_contexts(expr.span, span) {
++ return;
++ }
++ // move block higher
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ BLOCK_IN_IF_CONDITION_STMT,
++ expr.span.with_hi(cond.span.hi()),
++ COMPLEX_BLOCK_MESSAGE,
++ "try",
++ format!(
++ "let res = {}; if res",
++ snippet_block_with_applicability(
++ cx,
++ block.span,
++ "..",
++ Some(expr.span),
++ &mut applicability
++ ),
++ ),
++ applicability,
++ );
++ }
++ }
++ } else {
++ let mut visitor = ExVisitor { found_block: None, cx };
++ walk_expr(&mut visitor, cond);
++ if let Some(block) = visitor.found_block {
++ span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE);
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg,
++ span_lint_and_then, SpanlessEq,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for boolean expressions that can be written more
++ /// concisely.
++ ///
++ /// **Why is this bad?** Readability of boolean expressions suffers from
++ /// unnecessary duplication.
++ ///
++ /// **Known problems:** Ignores short circuiting behavior of `||` and
++ /// `&&`. Ignores `|`, `&` and `^`.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if a && true // should be: if a
++ /// if !(a == b) // should be: if a != b
++ /// ```
++ pub NONMINIMAL_BOOL,
++ complexity,
++ "boolean expressions that can be written more concisely"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for boolean expressions that contain terminals that
++ /// can be eliminated.
++ ///
++ /// **Why is this bad?** This is most likely a logic bug.
++ ///
++ /// **Known problems:** Ignores short circuiting behavior.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if a && b || a { ... }
++ /// ```
++ /// The `b` is unnecessary, the expression is equivalent to `if a`.
++ pub LOGIC_BUG,
++ correctness,
++ "boolean expressions that contain terminals which can be eliminated"
++}
++
++// For each pairs, both orders are considered.
++const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
++
++declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, LOGIC_BUG]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ _: FnKind<'tcx>,
++ _: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ _: Span,
++ _: HirId,
++ ) {
++ NonminimalBoolVisitor { cx }.visit_body(body)
++ }
++}
++
++struct NonminimalBoolVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++use quine_mc_cluskey::Bool;
++struct Hir2Qmm<'a, 'tcx, 'v> {
++ terminals: Vec<&'v Expr<'v>>,
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
++ fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
++ for a in a {
++ if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
++ if binop.node == op {
++ v = self.extract(op, &[lhs, rhs], v)?;
++ continue;
++ }
++ }
++ v.push(self.run(a)?);
++ }
++ Ok(v)
++ }
++
++ fn run(&mut self, e: &'v Expr<'_>) -> Result<Bool, String> {
++ fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
++ match bin_op_kind {
++ BinOpKind::Eq => Some(BinOpKind::Ne),
++ BinOpKind::Ne => Some(BinOpKind::Eq),
++ BinOpKind::Gt => Some(BinOpKind::Le),
++ BinOpKind::Ge => Some(BinOpKind::Lt),
++ BinOpKind::Lt => Some(BinOpKind::Ge),
++ BinOpKind::Le => Some(BinOpKind::Gt),
++ _ => None,
++ }
++ }
++
++ // prevent folding of `cfg!` macros and the like
++ if !e.span.from_expansion() {
++ match &e.kind {
++ ExprKind::Unary(UnOp::UnNot, inner) => return Ok(Bool::Not(box self.run(inner)?)),
++ ExprKind::Binary(binop, lhs, rhs) => match &binop.node {
++ BinOpKind::Or => return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?)),
++ BinOpKind::And => return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?)),
++ _ => (),
++ },
++ ExprKind::Lit(lit) => match lit.node {
++ LitKind::Bool(true) => return Ok(Bool::True),
++ LitKind::Bool(false) => return Ok(Bool::False),
++ _ => (),
++ },
++ _ => (),
++ }
++ }
++ for (n, expr) in self.terminals.iter().enumerate() {
++ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
++ #[allow(clippy::cast_possible_truncation)]
++ return Ok(Bool::Term(n as u8));
++ }
++
++ if_chain! {
++ if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
++ if implements_ord(self.cx, e_lhs);
++ if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
++ if negate(e_binop.node) == Some(expr_binop.node);
++ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs);
++ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs);
++ then {
++ #[allow(clippy::cast_possible_truncation)]
++ return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
++ }
++ }
++ }
++ let n = self.terminals.len();
++ self.terminals.push(e);
++ if n < 32 {
++ #[allow(clippy::cast_possible_truncation)]
++ Ok(Bool::Term(n as u8))
++ } else {
++ Err("too many literals".to_owned())
++ }
++ }
++}
++
++struct SuggestContext<'a, 'tcx, 'v> {
++ terminals: &'v [&'v Expr<'v>],
++ cx: &'a LateContext<'a, 'tcx>,
++ output: String,
++}
++
++impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
++ fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
++ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++ match suggestion {
++ True => {
++ self.output.push_str("true");
++ },
++ False => {
++ self.output.push_str("false");
++ },
++ Not(inner) => match **inner {
++ And(_) | Or(_) => {
++ self.output.push('!');
++ self.output.push('(');
++ self.recurse(inner);
++ self.output.push(')');
++ },
++ Term(n) => {
++ let terminal = self.terminals[n as usize];
++ if let Some(str) = simplify_not(self.cx, terminal) {
++ self.output.push_str(&str)
++ } else {
++ self.output.push('!');
++ let snip = snippet_opt(self.cx, terminal.span)?;
++ self.output.push_str(&snip);
++ }
++ },
++ True | False | Not(_) => {
++ self.output.push('!');
++ self.recurse(inner)?;
++ },
++ },
++ And(v) => {
++ for (index, inner) in v.iter().enumerate() {
++ if index > 0 {
++ self.output.push_str(" && ");
++ }
++ if let Or(_) = *inner {
++ self.output.push('(');
++ self.recurse(inner);
++ self.output.push(')');
++ } else {
++ self.recurse(inner);
++ }
++ }
++ },
++ Or(v) => {
++ for (index, inner) in v.iter().rev().enumerate() {
++ if index > 0 {
++ self.output.push_str(" || ");
++ }
++ self.recurse(inner);
++ }
++ },
++ &Term(n) => {
++ let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?;
++ self.output.push_str(&snip);
++ },
++ }
++ Some(())
++ }
++}
++
++fn simplify_not(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<String> {
++ match &expr.kind {
++ ExprKind::Binary(binop, lhs, rhs) => {
++ if !implements_ord(cx, lhs) {
++ return None;
++ }
++
++ match binop.node {
++ BinOpKind::Eq => Some(" != "),
++ BinOpKind::Ne => Some(" == "),
++ BinOpKind::Lt => Some(" >= "),
++ BinOpKind::Gt => Some(" <= "),
++ BinOpKind::Le => Some(" > "),
++ BinOpKind::Ge => Some(" < "),
++ _ => None,
++ }
++ .and_then(|op| {
++ Some(format!(
++ "{}{}{}",
++ snippet_opt(cx, lhs.span)?,
++ op,
++ snippet_opt(cx, rhs.span)?
++ ))
++ })
++ },
++ ExprKind::MethodCall(path, _, args) if args.len() == 1 => {
++ let type_of_receiver = cx.tables.expr_ty(&args[0]);
++ if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type))
++ && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type))
++ {
++ return None;
++ }
++ METHODS_WITH_NEGATION
++ .iter()
++ .cloned()
++ .flat_map(|(a, b)| vec![(a, b), (b, a)])
++ .find(|&(a, _)| {
++ let path: &str = &path.ident.name.as_str();
++ a == path
++ })
++ .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method)))
++ },
++ _ => None,
++ }
++}
++
++fn suggest(cx: &LateContext<'_, '_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
++ let mut suggest_context = SuggestContext {
++ terminals,
++ cx,
++ output: String::new(),
++ };
++ suggest_context.recurse(suggestion);
++ suggest_context.output
++}
++
++fn simple_negate(b: Bool) -> Bool {
++ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++ match b {
++ True => False,
++ False => True,
++ t @ Term(_) => Not(Box::new(t)),
++ And(mut v) => {
++ for el in &mut v {
++ *el = simple_negate(::std::mem::replace(el, True));
++ }
++ Or(v)
++ },
++ Or(mut v) => {
++ for el in &mut v {
++ *el = simple_negate(::std::mem::replace(el, True));
++ }
++ And(v)
++ },
++ Not(inner) => *inner,
++ }
++}
++
++#[derive(Default)]
++struct Stats {
++ terminals: [usize; 32],
++ negations: usize,
++ ops: usize,
++}
++
++fn terminal_stats(b: &Bool) -> Stats {
++ fn recurse(b: &Bool, stats: &mut Stats) {
++ match b {
++ True | False => stats.ops += 1,
++ Not(inner) => {
++ match **inner {
++ And(_) | Or(_) => stats.ops += 1, // brackets are also operations
++ _ => stats.negations += 1,
++ }
++ recurse(inner, stats);
++ },
++ And(v) | Or(v) => {
++ stats.ops += v.len() - 1;
++ for inner in v {
++ recurse(inner, stats);
++ }
++ },
++ &Term(n) => stats.terminals[n as usize] += 1,
++ }
++ }
++ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++ let mut stats = Stats::default();
++ recurse(b, &mut stats);
++ stats
++}
++
++impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
++ fn bool_expr(&self, e: &'tcx Expr<'_>) {
++ let mut h2q = Hir2Qmm {
++ terminals: Vec::new(),
++ cx: self.cx,
++ };
++ if let Ok(expr) = h2q.run(e) {
++ if h2q.terminals.len() > 8 {
++ // QMC has exponentially slow behavior as the number of terminals increases
++ // 8 is reasonable, it takes approximately 0.2 seconds.
++ // See #825
++ return;
++ }
++
++ let stats = terminal_stats(&expr);
++ let mut simplified = expr.simplify();
++ for simple in Bool::Not(Box::new(expr)).simplify() {
++ match simple {
++ Bool::Not(_) | Bool::True | Bool::False => {},
++ _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
++ }
++ let simple_negated = simple_negate(simple);
++ if simplified.iter().any(|s| *s == simple_negated) {
++ continue;
++ }
++ simplified.push(simple_negated);
++ }
++ let mut improvements = Vec::with_capacity(simplified.len());
++ 'simplified: for suggestion in &simplified {
++ let simplified_stats = terminal_stats(suggestion);
++ let mut improvement = false;
++ for i in 0..32 {
++ // ignore any "simplifications" that end up requiring a terminal more often
++ // than in the original expression
++ if stats.terminals[i] < simplified_stats.terminals[i] {
++ continue 'simplified;
++ }
++ if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
++ span_lint_and_then(
++ self.cx,
++ LOGIC_BUG,
++ e.span,
++ "this boolean expression contains a logic bug",
++ |diag| {
++ diag.span_help(
++ h2q.terminals[i].span,
++ "this expression can be optimized out by applying boolean operations to the \
++ outer expression",
++ );
++ diag.span_suggestion(
++ e.span,
++ "it would look like the following",
++ suggest(self.cx, suggestion, &h2q.terminals),
++ // nonminimal_bool can produce minimal but
++ // not human readable expressions (#3141)
++ Applicability::Unspecified,
++ );
++ },
++ );
++ // don't also lint `NONMINIMAL_BOOL`
++ return;
++ }
++ // if the number of occurrences of a terminal decreases or any of the stats
++ // decreases while none increases
++ improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
++ || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
++ || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
++ }
++ if improvement {
++ improvements.push(suggestion);
++ }
++ }
++ let nonminimal_bool_lint = |suggestions: Vec<_>| {
++ span_lint_and_then(
++ self.cx,
++ NONMINIMAL_BOOL,
++ e.span,
++ "this boolean expression can be simplified",
++ |diag| {
++ diag.span_suggestions(
++ e.span,
++ "try",
++ suggestions.into_iter(),
++ // nonminimal_bool can produce minimal but
++ // not human readable expressions (#3141)
++ Applicability::Unspecified,
++ );
++ },
++ );
++ };
++ if improvements.is_empty() {
++ let mut visitor = NotSimplificationVisitor { cx: self.cx };
++ visitor.visit_expr(e);
++ } else {
++ nonminimal_bool_lint(
++ improvements
++ .into_iter()
++ .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
++ .collect(),
++ );
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++ if in_macro(e.span) {
++ return;
++ }
++ match &e.kind {
++ ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
++ self.bool_expr(e)
++ },
++ ExprKind::Unary(UnOp::UnNot, inner) => {
++ if self.cx.tables.node_types()[inner.hir_id].is_bool() {
++ self.bool_expr(e);
++ } else {
++ walk_expr(self, e);
++ }
++ },
++ _ => walk_expr(self, e),
++ }
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn implements_ord<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++ let ty = cx.tables.expr_ty(expr);
++ get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]))
++}
++
++struct NotSimplificationVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Unary(UnOp::UnNot, inner) = &expr.kind {
++ if let Some(suggestion) = simplify_not(self.cx, inner) {
++ span_lint_and_sugg(
++ self.cx,
++ NONMINIMAL_BOOL,
++ expr.span,
++ "this boolean expression can be simplified",
++ "try",
++ suggestion,
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
++ span_lint_and_sugg, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{Name, UintTy};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for 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,
++ perf,
++ "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
++}
++
++declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.kind;
++ if count.ident.name == sym!(count);
++ if count_args.len() == 1;
++ if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].kind;
++ if filter.ident.name == sym!(filter);
++ if filter_args.len() == 2;
++ if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
++ then {
++ let body = cx.tcx.hir().body(body_id);
++ if_chain! {
++ if body.params.len() == 1;
++ if let Some(argname) = get_pat_name(&body.params[0].pat);
++ if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
++ if op.node == BinOpKind::Eq;
++ if match_type(cx,
++ walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
++ &paths::SLICE_ITER);
++ then {
++ 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) != walk_ptrs_ty(cx.tables.expr_ty(needle)).kind {
++ return;
++ }
++ let haystack = if let ExprKind::MethodCall(ref path, _, ref args) =
++ filter_args[0].kind {
++ let p = path.ident.name;
++ if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 {
++ &args[0]
++ } else {
++ &filter_args[0]
++ }
++ } else {
++ &filter_args[0]
++ };
++ 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,
++ );
++ }
++ };
++ }
++ };
++ }
++}
++
++fn check_arg(name: Name, arg: Name, needle: &Expr<'_>) -> bool {
++ name == arg && !contains_name(name, needle)
++}
++
++fn get_path_name(expr: &Expr<'_>) -> Option<Name> {
++ match expr.kind {
++ ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => {
++ get_path_name(e)
++ },
++ ExprKind::Block(ref 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,
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on missing cargo common metadata
++
++use std::path::PathBuf;
++
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks to see if all common metadata is defined in
++ /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
++ ///
++ /// **Why is this bad?** It will be more difficult for users to discover the
++ /// purpose of the crate, and key information related to it.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```toml
++ /// # This `Cargo.toml` is missing an authors field:
++ /// [package]
++ /// name = "clippy"
++ /// version = "0.0.212"
++ /// 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"]
++ /// ```
++ pub CARGO_COMMON_METADATA,
++ cargo,
++ "common metadata is defined in `Cargo.toml`"
++}
++
++fn warning(cx: &LateContext<'_, '_>, message: &str) {
++ span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
++}
++
++fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) {
++ let message = format!("package `{}` is missing `{}` metadata", package.name, field);
++ warning(cx, &message);
++}
++
++fn is_empty_str(value: &Option<String>) -> bool {
++ value.as_ref().map_or(true, String::is_empty)
++}
++
++fn is_empty_path(value: &Option<PathBuf>) -> bool {
++ value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty)
++}
++
++fn is_empty_vec(value: &[String]) -> bool {
++ // This works because empty iterators return true
++ value.iter().all(String::is_empty)
++}
++
++declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
++
++impl LateLintPass<'_, '_> for CargoCommonMetadata {
++ fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++ if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
++ return;
++ }
++
++ let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
++ metadata
++ } else {
++ warning(cx, "could not read cargo metadata");
++ return;
++ };
++
++ for package in metadata.packages {
++ if is_empty_vec(&package.authors) {
++ missing_warning(cx, &package, "package.authors");
++ }
++
++ if is_empty_str(&package.description) {
++ missing_warning(cx, &package, "package.description");
++ }
++
++ if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
++ missing_warning(cx, &package, "either package.license or package.license_file");
++ }
++
++ if is_empty_str(&package.repository) {
++ missing_warning(cx, &package, "package.repository");
++ }
++
++ if is_empty_path(&package.readme) {
++ missing_warning(cx, &package, "package.readme");
++ }
++
++ if is_empty_vec(&package.keywords) {
++ missing_warning(cx, &package, "package.keywords");
++ }
++
++ if is_empty_vec(&package.categories) {
++ missing_warning(cx, &package, "package.categories");
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on manually implemented checked conversions that could be transformed into `try_from`
++
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for explicit bounds checking when casting.
++ ///
++ /// **Why is this bad?** Reduces the readability of statements & is error prone.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let foo: u32 = 5;
++ /// # let _ =
++ /// foo <= i32::MAX as u32
++ /// # ;
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// # use std::convert::TryFrom;
++ /// # let foo = 1;
++ /// # let _ =
++ /// i32::try_from(foo).is_ok()
++ /// # ;
++ /// ```
++ pub CHECKED_CONVERSIONS,
++ pedantic,
++ "`try_from` could replace manual bounds checking when casting"
++}
++
++declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, item: &Expr<'_>) {
++ let result = if_chain! {
++ if !in_external_macro(cx.sess(), item.span);
++ if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
++
++ then {
++ match op.node {
++ BinOpKind::Ge | BinOpKind::Le => single_check(item),
++ BinOpKind::And => double_check(cx, left, right),
++ _ => None,
++ }
++ } else {
++ None
++ }
++ };
++
++ if_chain! {
++ if let Some(cv) = result;
++ if let Some(to_type) = cv.to_type;
++
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut
++ applicability);
++ span_lint_and_sugg(
++ cx,
++ CHECKED_CONVERSIONS,
++ item.span,
++ "Checked cast can be simplified.",
++ "try",
++ format!("{}::try_from({}).is_ok()",
++ to_type,
++ snippet),
++ applicability
++ );
++ }
++ }
++ }
++}
++
++/// Searches for a single check from unsigned to _ is done
++/// todo: check for case signed -> larger unsigned == only x >= 0
++fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++ check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
++}
++
++/// Searches for a combination of upper & lower bound checks
++fn double_check<'a>(cx: &LateContext<'_, '_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> {
++ let upper_lower = |l, r| {
++ let upper = check_upper_bound(l);
++ let lower = check_lower_bound(r);
++
++ transpose(upper, lower).and_then(|(l, r)| l.combine(r, cx))
++ };
++
++ upper_lower(left, right).or_else(|| upper_lower(right, left))
++}
++
++/// Contains the result of a tried conversion check
++#[derive(Clone, Debug)]
++struct Conversion<'a> {
++ cvt: ConversionType,
++ expr_to_cast: &'a Expr<'a>,
++ to_type: Option<&'a str>,
++}
++
++/// The kind of conversion that is checked
++#[derive(Copy, Clone, Debug, PartialEq)]
++enum ConversionType {
++ SignedToUnsigned,
++ SignedToSigned,
++ FromUnsigned,
++}
++
++impl<'a> Conversion<'a> {
++ /// Combine multiple conversions if the are compatible
++ pub fn combine(self, other: Self, cx: &LateContext<'_, '_>) -> Option<Conversion<'a>> {
++ if self.is_compatible(&other, cx) {
++ // Prefer a Conversion that contains a type-constraint
++ Some(if self.to_type.is_some() { self } else { other })
++ } else {
++ None
++ }
++ }
++
++ /// Checks if two conversions are compatible
++ /// same type of conversion, same 'castee' and same 'to type'
++ pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_, '_>) -> bool {
++ (self.cvt == other.cvt)
++ && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
++ && (self.has_compatible_to_type(other))
++ }
++
++ /// Checks if the to-type is the same (if there is a type constraint)
++ fn has_compatible_to_type(&self, other: &Self) -> bool {
++ transpose(self.to_type.as_ref(), other.to_type.as_ref()).map_or(true, |(l, r)| l == r)
++ }
++
++ /// Try to construct a new conversion if the conversion type is valid
++ fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
++ ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
++ cvt,
++ expr_to_cast,
++ to_type: Some(to_type),
++ })
++ }
++
++ /// Construct a new conversion without type constraint
++ fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> {
++ Conversion {
++ cvt: ConversionType::SignedToUnsigned,
++ expr_to_cast,
++ to_type: None,
++ }
++ }
++}
++
++impl ConversionType {
++ /// Creates a conversion type if the type is allowed & conversion is valid
++ #[must_use]
++ fn try_new(from: &str, to: &str) -> Option<Self> {
++ if UINTS.contains(&from) {
++ Some(Self::FromUnsigned)
++ } else if SINTS.contains(&from) {
++ if UINTS.contains(&to) {
++ Some(Self::SignedToUnsigned)
++ } else if SINTS.contains(&to) {
++ Some(Self::SignedToSigned)
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++ }
++}
++
++/// Check for `expr <= (to_type::MAX as from_type)`
++fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++ if_chain! {
++ if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind;
++ if let Some((candidate, check)) = normalize_le_ge(op, left, right);
++ if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS);
++
++ then {
++ Conversion::try_new(candidate, from, to)
++ } else {
++ None
++ }
++ }
++}
++
++/// Check for `expr >= 0|(to_type::MIN as from_type)`
++fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++ fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> {
++ (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
++ }
++
++ // First of we need a binary containing the expression & the cast
++ if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind {
++ normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
++ } else {
++ None
++ }
++}
++
++/// Check for `expr >= 0`
++fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
++ if_chain! {
++ if let ExprKind::Lit(ref lit) = &check.kind;
++ if let LitKind::Int(0, _) = &lit.node;
++
++ then {
++ Some(Conversion::new_any(candidate))
++ } else {
++ None
++ }
++ }
++}
++
++/// Check for `expr >= (to_type::MIN as from_type)`
++fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
++ if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) {
++ Conversion::try_new(candidate, from, to)
++ } else {
++ None
++ }
++}
++
++/// Tries to extract the from- and to-type from a cast expression
++fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> {
++ // `to_type::maxmin_value() as from_type`
++ let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
++ // to_type::maxmin_value(), from_type
++ if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind;
++ if let TyKind::Path(ref from_type_path) = &from_type.kind;
++ if let Some(from_sym) = int_ty_to_sym(from_type_path);
++
++ then {
++ Some((limit, from_sym))
++ } else {
++ None
++ }
++ };
++
++ // `from_type::from(to_type::maxmin_value())`
++ let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
++ if_chain! {
++ // `from_type::from, to_type::maxmin_value()`
++ if let ExprKind::Call(ref from_func, ref args) = &expr.kind;
++ // `to_type::maxmin_value()`
++ if args.len() == 1;
++ if let limit = &args[0];
++ // `from_type::from`
++ if let ExprKind::Path(ref path) = &from_func.kind;
++ if let Some(from_sym) = get_implementing_type(path, INTS, FROM);
++
++ then {
++ Some((limit, from_sym))
++ } else {
++ None
++ }
++ }
++ });
++
++ if let Some((limit, from_type)) = limit_from {
++ if_chain! {
++ if let ExprKind::Call(ref fun_name, _) = &limit.kind;
++ // `to_type, maxmin_value`
++ if let ExprKind::Path(ref path) = &fun_name.kind;
++ // `to_type`
++ if let Some(to_type) = get_implementing_type(path, types, func);
++
++ then {
++ Some((from_type, to_type))
++ } else {
++ None
++ }
++ }
++ } else {
++ None
++ }
++}
++
++/// Gets the type which implements the called function
++fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
++ if_chain! {
++ if let QPath::TypeRelative(ref ty, ref path) = &path;
++ if path.ident.name.as_str() == function;
++ if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind;
++ if let [int] = &*tp.segments;
++ let name = &int.ident.name.as_str();
++
++ then {
++ candidates.iter().find(|c| name == *c).cloned()
++ } else {
++ None
++ }
++ }
++}
++
++/// Gets the type as a string, if it is a supported integer
++fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
++ if_chain! {
++ if let QPath::Resolved(_, ref path) = *path;
++ if let [ty] = &*path.segments;
++ let name = &ty.ident.name.as_str();
++
++ then {
++ INTS.iter().find(|c| name == *c).cloned()
++ } else {
++ None
++ }
++ }
++}
++
++/// (Option<T>, Option<U>) -> Option<(T, U)>
++fn transpose<T, U>(lhs: Option<T>, rhs: Option<U>) -> Option<(T, U)> {
++ match (lhs, rhs) {
++ (Some(l), Some(r)) => Some((l, r)),
++ _ => None,
++ }
++}
++
++/// Will return the expressions as if they were expr1 <= expr2
++fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
++ match op.node {
++ BinOpKind::Le => Some((left, right)),
++ BinOpKind::Ge => Some((right, left)),
++ _ => None,
++ }
++}
++
++// Constants
++const FROM: &str = "from";
++const MAX_VALUE: &str = "max_value";
++const MIN_VALUE: &str = "min_value";
++
++const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
++const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
++const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
--- /dev/null
--- /dev/null
++//! calculate cognitive complexity and warn about overly complex functions
++
++use rustc_ast::ast::Attribute;
++use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::BytePos;
++
++use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for methods with high cognitive complexity.
++ ///
++ /// **Why is this bad?** Methods of high cognitive complexity tend to be hard to
++ /// both read and maintain. Also LLVM will tend to optimize small methods better.
++ ///
++ /// **Known problems:** Sometimes it's hard to find a way to reduce the
++ /// complexity.
++ ///
++ /// **Example:** No. You'll see it when you get the warning.
++ pub COGNITIVE_COMPLEXITY,
++ nursery,
++ "functions that should be split up into multiple functions"
++}
++
++pub struct CognitiveComplexity {
++ limit: LimitStack,
++}
++
++impl CognitiveComplexity {
++ #[must_use]
++ pub fn new(limit: u64) -> Self {
++ Self {
++ limit: LimitStack::new(limit),
++ }
++ }
++}
++
++impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
++
++impl CognitiveComplexity {
++ #[allow(clippy::cast_possible_truncation)]
++ fn check<'a, 'tcx>(
++ &mut self,
++ cx: &'a LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ body_span: Span,
++ ) {
++ if body_span.from_expansion() {
++ return;
++ }
++
++ let expr = &body.value;
++
++ let mut helper = CCHelper { cc: 1, returns: 0 };
++ helper.visit_expr(expr);
++ let CCHelper { cc, returns } = helper;
++ let ret_ty = cx.tables.node_type(expr.hir_id);
++ let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) {
++ returns
++ } else {
++ #[allow(clippy::integer_division)]
++ (returns / 2)
++ };
++
++ let mut rust_cc = cc;
++ // prevent degenerate cases where unreachable code contains `return` statements
++ if rust_cc >= ret_adjust {
++ rust_cc -= ret_adjust;
++ }
++
++ if rust_cc > self.limit.limit() {
++ let fn_span = match kind {
++ FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span,
++ FnKind::Closure(_) => {
++ let header_span = body_span.with_hi(decl.output.span().lo());
++ let pos = snippet_opt(cx, header_span).and_then(|snip| {
++ let low_offset = snip.find('|')?;
++ let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
++ let low = header_span.lo() + BytePos(low_offset as u32);
++ let high = low + BytePos(high_offset as u32 + 1);
++
++ Some((low, high))
++ });
++
++ if let Some((low, high)) = pos {
++ Span::new(low, high, header_span.ctxt())
++ } else {
++ return;
++ }
++ },
++ };
++
++ span_lint_and_help(
++ cx,
++ COGNITIVE_COMPLEXITY,
++ fn_span,
++ &format!(
++ "the function has a cognitive complexity of ({}/{})",
++ rust_cc,
++ self.limit.limit()
++ ),
++ None,
++ "you could split it up into multiple smaller functions",
++ );
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ span: Span,
++ hir_id: HirId,
++ ) {
++ let def_id = cx.tcx.hir().local_def_id(hir_id);
++ if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) {
++ self.check(cx, kind, decl, body, span);
++ }
++ }
++
++ fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
++ self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity");
++ }
++ fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
++ self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
++ }
++}
++
++struct CCHelper {
++ cc: u64,
++ returns: u64,
++}
++
++impl<'tcx> Visitor<'tcx> for CCHelper {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++ walk_expr(self, e);
++ match e.kind {
++ ExprKind::Match(_, ref arms, _) => {
++ if arms.len() > 1 {
++ self.cc += 1;
++ }
++ self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
++ },
++ ExprKind::Ret(_) => self.returns += 1,
++ _ => {},
++ }
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for if expressions that contain only an if expression.
++//!
++//! For example, the lint would catch:
++//!
++//! ```rust,ignore
++//! if x {
++//! if y {
++//! println!("Hello world");
++//! }
++//! }
++//! ```
++//!
++//! This lint is **warn** by default
++
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then};
++use rustc_errors::Applicability;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for nested `if` statements which can be collapsed
++ /// by `&&`-combining their conditions and for `else { if ... }` expressions
++ /// that
++ /// can be collapsed to `else if ...`.
++ ///
++ /// **Why is this bad?** Each `if`-statement adds one level of nesting, which
++ /// makes code look more complex than it really is.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// if x {
++ /// if y {
++ /// …
++ /// }
++ /// }
++ ///
++ /// // or
++ ///
++ /// if x {
++ /// …
++ /// } else {
++ /// if y {
++ /// …
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Should be written:
++ ///
++ /// ```rust.ignore
++ /// if x && y {
++ /// …
++ /// }
++ ///
++ /// // or
++ ///
++ /// if x {
++ /// …
++ /// } else if y {
++ /// …
++ /// }
++ /// ```
++ pub COLLAPSIBLE_IF,
++ style,
++ "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
++}
++
++declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]);
++
++impl EarlyLintPass for CollapsibleIf {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
++ if !expr.span.from_expansion() {
++ check_if(cx, expr)
++ }
++ }
++}
++
++fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
++ if let ast::ExprKind::If(check, then, else_) = &expr.kind {
++ if let Some(else_) = else_ {
++ check_collapsible_maybe_if_let(cx, else_);
++ } else if let ast::ExprKind::Let(..) = check.kind {
++ // Prevent triggering on `if let a = b { if c { .. } }`.
++ } else {
++ check_collapsible_no_if_let(cx, expr, check, then);
++ }
++ }
++}
++
++fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
++ // We trim all opening braces and whitespaces and then check if the next string is a comment.
++ let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
++ .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
++ .to_owned();
++ trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
++}
++
++fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
++ if_chain! {
++ if let ast::ExprKind::Block(ref block, _) = else_.kind;
++ if !block_starts_with_comment(cx, block);
++ if let Some(else_) = expr_block(block);
++ if !else_.span.from_expansion();
++ if let ast::ExprKind::If(..) = else_.kind;
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ COLLAPSIBLE_IF,
++ block.span,
++ "this `else { if .. }` block can be collapsed",
++ "try",
++ snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
++ applicability,
++ );
++ }
++ }
++}
++
++fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
++ if_chain! {
++ if !block_starts_with_comment(cx, then);
++ if let Some(inner) = expr_block(then);
++ if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
++ then {
++ if let ast::ExprKind::Let(..) = check_inner.kind {
++ // Prevent triggering on `if c { if let a = b { .. } }`.
++ return;
++ }
++
++ if expr.span.ctxt() != inner.span.ctxt() {
++ return;
++ }
++ span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
++ let lhs = Sugg::ast(cx, check, "..");
++ let rhs = Sugg::ast(cx, check_inner, "..");
++ diag.span_suggestion(
++ expr.span,
++ "try",
++ format!(
++ "if {} {}",
++ lhs.and(&rhs),
++ snippet_block(cx, content.span, "..", Some(expr.span)),
++ ),
++ Applicability::MachineApplicable, // snippet
++ );
++ });
++ }
++ }
++}
++
++/// If the block contains only one expression, return it.
++fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
++ let mut it = block.stmts.iter();
++
++ if let (Some(stmt), None) = (it.next(), it.next()) {
++ match stmt.kind {
++ ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
++ _ => None,
++ }
++ } else {
++ None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq,
++};
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks comparison chains written with `if` that can be
++ /// rewritten with `match` and `cmp`.
++ ///
++ /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
++ /// repetitive
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// # fn a() {}
++ /// # fn b() {}
++ /// # fn c() {}
++ /// fn f(x: u8, y: u8) {
++ /// if x > y {
++ /// a()
++ /// } else if x < y {
++ /// b()
++ /// } else {
++ /// c()
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust,ignore
++ /// use std::cmp::Ordering;
++ /// # fn a() {}
++ /// # fn b() {}
++ /// # fn c() {}
++ /// fn f(x: u8, y: u8) {
++ /// match x.cmp(&y) {
++ /// Ordering::Greater => a(),
++ /// Ordering::Less => b(),
++ /// Ordering::Equal => c()
++ /// }
++ /// }
++ /// ```
++ pub COMPARISON_CHAIN,
++ style,
++ "`if`s that can be rewritten with `match` and `cmp`"
++}
++
++declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ // We only care about the top-most `if` in the chain
++ if parent_node_is_if_expr(expr, cx) {
++ return;
++ }
++
++ // Check that there exists at least one explicit else condition
++ let (conds, _) = if_sequence(expr);
++ if conds.len() < 2 {
++ return;
++ }
++
++ for cond in conds.windows(2) {
++ if let (
++ &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1),
++ &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2),
++ ) = (&cond[0].kind, &cond[1].kind)
++ {
++ if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
++ return;
++ }
++
++ // Check that both sets of operands are equal
++ let mut spanless_eq = SpanlessEq::new(cx);
++ if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2))
++ && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2))
++ {
++ return;
++ }
++
++ // Check that the type being compared implements `core::cmp::Ord`
++ let ty = cx.tables.expr_ty(lhs1);
++ let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
++
++ if !is_ord {
++ return;
++ }
++ } else {
++ // We only care about comparison chains
++ return;
++ }
++ }
++ span_lint_and_help(
++ cx,
++ COMPARISON_CHAIN,
++ expr.span,
++ "`if` chain can be rewritten with `match`",
++ None,
++ "Consider rewriting the `if` chain to use `cmp` and `match`.",
++ )
++ }
++}
++
++fn kind_is_cmp(kind: BinOpKind) -> bool {
++ match kind {
++ BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
++ _ => false,
++ }
++}
--- /dev/null
--- /dev/null
++#![allow(clippy::float_cmp)]
++
++use crate::utils::{clip, higher, sext, unsext};
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, 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::ty::subst::{Subst, SubstsRef};
++use rustc_middle::ty::{self, 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};
++
++/// 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<Vec<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 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,
++ // 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::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)) => l
++ .iter()
++ .zip(r.iter())
++ .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,
++ }
++ },
++ // 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 {
++ FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
++ 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<'c, 'cc>(
++ lcx: &LateContext<'c, 'cc>,
++ tables: &'c ty::TypeckTables<'cc>,
++ e: &Expr<'_>,
++) -> Option<(Constant, bool)> {
++ let mut cx = ConstEvalLateContext {
++ lcx,
++ tables,
++ 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<'c, 'cc>(
++ lcx: &LateContext<'c, 'cc>,
++ tables: &'c ty::TypeckTables<'cc>,
++ e: &Expr<'_>,
++) -> Option<Constant> {
++ constant(lcx, tables, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
++}
++
++/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`.
++pub fn constant_context<'c, 'cc>(
++ lcx: &'c LateContext<'c, 'cc>,
++ tables: &'c ty::TypeckTables<'cc>,
++) -> ConstEvalLateContext<'c, 'cc> {
++ ConstEvalLateContext {
++ lcx,
++ tables,
++ param_env: lcx.param_env,
++ needed_resolution: false,
++ substs: lcx.tcx.intern_substs(&[]),
++ }
++}
++
++pub struct ConstEvalLateContext<'a, 'tcx> {
++ lcx: &'a LateContext<'a, 'tcx>,
++ tables: &'a ty::TypeckTables<'tcx>,
++ param_env: ty::ParamEnv<'tcx>,
++ needed_resolution: bool,
++ substs: SubstsRef<'tcx>,
++}
++
++impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
++ /// Simple constant folding: Insert an expression, get a constant or none.
++ pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
++ if let Some((ref cond, ref then, otherwise)) = higher::if_block(&e) {
++ return self.ifthenelse(cond, then, otherwise);
++ }
++ match e.kind {
++ ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.tables.expr_ty(e)),
++ ExprKind::Block(ref block, _) => self.block(block),
++ ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.tables.expr_ty_opt(e))),
++ ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
++ ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
++ ExprKind::Repeat(ref value, _) => {
++ let n = match self.tables.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, ref operand) => self.expr(operand).and_then(|o| match op {
++ UnOp::UnNot => self.constant_not(&o, self.tables.expr_ty(e)),
++ UnOp::UnNeg => self.constant_negate(&o, self.tables.expr_ty(e)),
++ UnOp::UnDeref => Some(o),
++ }),
++ ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
++ ExprKind::Call(ref callee, ref 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.tables.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_value() as u128,
++ "<impl i16>" => i16::max_value() as u128,
++ "<impl i32>" => i32::max_value() as u128,
++ "<impl i64>" => i64::max_value() as u128,
++ "<impl i128>" => i128::max_value() as u128,
++ _ => return None,
++ };
++ Some(Constant::Int(value))
++ }
++ else {
++ None
++ }
++ }
++ },
++ ExprKind::Index(ref arr, ref index) => self.index(arr, index),
++ // 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<'cc>) -> Option<Constant> {
++ let res = self.tables.qpath_res(qpath, id);
++ match res {
++ Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
++ let substs = self.tables.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, def_id, substs, 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.tables.expr_ty(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, Scalar};
++ match result.val {
++ ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => match result.ty.kind {
++ ty::Bool => Some(Constant::Bool(d == 1)),
++ ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)),
++ ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
++ d.try_into().expect("invalid f32 bit representation"),
++ ))),
++ ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
++ d.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(d));
++ }
++ 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_undef_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_undef_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_undef_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,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then};
++use crate::utils::{SpanlessEq, SpanlessHash};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++use std::collections::hash_map::Entry;
++use std::hash::BuildHasherDefault;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for consecutive `if`s with the same condition.
++ ///
++ /// **Why is this bad?** This is probably a copy & paste error.
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if a == b {
++ /// …
++ /// } else if a == b {
++ /// …
++ /// }
++ /// ```
++ ///
++ /// Note that this lint ignores all conditions with a function call as it could
++ /// have side effects:
++ ///
++ /// ```ignore
++ /// if foo() {
++ /// …
++ /// } else if foo() { // not linted
++ /// …
++ /// }
++ /// ```
++ pub IFS_SAME_COND,
++ correctness,
++ "consecutive `if`s with the same condition"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for consecutive `if`s with the same function call.
++ ///
++ /// **Why is this bad?** This is probably a copy & paste error.
++ /// Despite the fact that function can have side effects and `if` works as
++ /// intended, such an approach is implicit and can be considered a "code smell".
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if foo() == bar {
++ /// …
++ /// } else if foo() == bar {
++ /// …
++ /// }
++ /// ```
++ ///
++ /// This probably should be:
++ /// ```ignore
++ /// if foo() == bar {
++ /// …
++ /// } else if foo() == baz {
++ /// …
++ /// }
++ /// ```
++ ///
++ /// or if the original code was not a typo and called function mutates a state,
++ /// consider move the mutation out of the `if` condition to avoid similarity to
++ /// a copy & paste error:
++ ///
++ /// ```ignore
++ /// let first = foo();
++ /// if first == bar {
++ /// …
++ /// } else {
++ /// let second = foo();
++ /// if second == bar {
++ /// …
++ /// }
++ /// }
++ /// ```
++ pub SAME_FUNCTIONS_IN_IF_CONDITION,
++ pedantic,
++ "consecutive `if`s with the same function call"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `if/else` with the same body as the *then* part
++ /// and the *else* part.
++ ///
++ /// **Why is this bad?** This is probably a copy & paste error.
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let foo = if … {
++ /// 42
++ /// } else {
++ /// 42
++ /// };
++ /// ```
++ pub IF_SAME_THEN_ELSE,
++ correctness,
++ "`if` with the same `then` and `else` blocks"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks 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"
++}
++
++declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if !expr.span.from_expansion() {
++ // skip ifs directly in else, it will be checked in the parent if
++ if let Some(expr) = get_parent_expr(cx, expr) {
++ if let Some((_, _, Some(ref else_expr))) = higher::if_block(&expr) {
++ if else_expr.hir_id == expr.hir_id {
++ return;
++ }
++ }
++ }
++
++ let (conds, blocks) = if_sequence(expr);
++ lint_same_then_else(cx, &blocks);
++ lint_same_cond(cx, &conds);
++ lint_same_fns_in_if_cond(cx, &conds);
++ lint_match_arms(cx, expr);
++ }
++ }
++}
++
++/// Implementation of `IF_SAME_THEN_ELSE`.
++fn lint_same_then_else(cx: &LateContext<'_, '_>, blocks: &[&Block<'_>]) {
++ let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool =
++ &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) };
++
++ if let Some((i, j)) = search_same_sequenced(blocks, eq) {
++ span_lint_and_note(
++ cx,
++ IF_SAME_THEN_ELSE,
++ j.span,
++ "this `if` has identical blocks",
++ Some(i.span),
++ "same as this",
++ );
++ }
++}
++
++/// Implementation of `IFS_SAME_COND`.
++fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
++ let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
++ let mut h = SpanlessHash::new(cx, cx.tables);
++ h.hash_expr(expr);
++ h.finish()
++ };
++
++ let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool =
++ &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
++
++ for (i, j) in search_same(conds, hash, eq) {
++ span_lint_and_note(
++ cx,
++ IFS_SAME_COND,
++ j.span,
++ "this `if` has the same condition as a previous `if`",
++ Some(i.span),
++ "same as this",
++ );
++ }
++}
++
++/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
++fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
++ let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
++ let mut h = SpanlessHash::new(cx, cx.tables);
++ h.hash_expr(expr);
++ h.finish()
++ };
++
++ let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
++ // Do not spawn warning if `IFS_SAME_COND` already produced it.
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
++ return false;
++ }
++ SpanlessEq::new(cx).eq_expr(lhs, rhs)
++ };
++
++ for (i, j) in search_same(conds, hash, eq) {
++ span_lint_and_note(
++ cx,
++ SAME_FUNCTIONS_IN_IF_CONDITION,
++ j.span,
++ "this `if` has the same function call as a previous `if`",
++ Some(i.span),
++ "same as this",
++ );
++ }
++}
++
++/// Implementation of `MATCH_SAME_ARMS`.
++fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) {
++ fn same_bindings<'tcx>(
++ cx: &LateContext<'_, 'tcx>,
++ lhs: &FxHashMap<Symbol, Ty<'tcx>>,
++ rhs: &FxHashMap<Symbol, Ty<'tcx>>,
++ ) -> bool {
++ lhs.len() == rhs.len()
++ && lhs
++ .iter()
++ .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty)))
++ }
++
++ if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
++ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
++ let mut h = SpanlessHash::new(cx, cx.tables);
++ 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);
++
++ // 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).eq_expr(&lhs.body, &rhs.body) &&
++ // all patterns should have the same bindings
++ same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
++ };
++
++ 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));
++ }
++ },
++ );
++ }
++ }
++}
++
++/// Returns the list of bindings in a pattern.
++fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
++ fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
++ match pat.kind {
++ PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
++ PatKind::TupleStruct(_, pats, _) => {
++ for pat in pats {
++ bindings_impl(cx, pat, map);
++ }
++ },
++ PatKind::Binding(.., ident, ref as_pat) => {
++ if let Entry::Vacant(v) = map.entry(ident.name) {
++ v.insert(cx.tables.pat_ty(pat));
++ }
++ if let Some(ref as_pat) = *as_pat {
++ bindings_impl(cx, as_pat, map);
++ }
++ },
++ PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
++ for pat in fields {
++ bindings_impl(cx, pat, map);
++ }
++ },
++ PatKind::Struct(_, fields, _) => {
++ for pat in fields {
++ bindings_impl(cx, &pat.pat, map);
++ }
++ },
++ PatKind::Slice(lhs, ref mid, rhs) => {
++ for pat in lhs {
++ bindings_impl(cx, pat, map);
++ }
++ if let Some(ref mid) = *mid {
++ bindings_impl(cx, mid, map);
++ }
++ for pat in rhs {
++ bindings_impl(cx, pat, map);
++ }
++ },
++ PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
++ }
++ }
++
++ let mut result = FxHashMap::default();
++ bindings_impl(cx, pat, &mut result);
++ result
++}
++
++fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)>
++where
++ Eq: Fn(&T, &T) -> bool,
++{
++ for win in exprs.windows(2) {
++ if eq(&win[0], &win[1]) {
++ return Some((&win[0], &win[1]));
++ }
++ }
++ None
++}
++
++fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
++where
++ Eq: Fn(&T, &T) -> bool,
++{
++ if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
++ Some((&exprs[0], &exprs[1]))
++ } else {
++ None
++ }
++}
++
++fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
++where
++ Hash: Fn(&T) -> u64,
++ Eq: Fn(&T, &T) -> bool,
++{
++ if let Some(expr) = search_common_cases(&exprs, &eq) {
++ return vec![expr];
++ }
++
++ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
++
++ let mut map: FxHashMap<_, Vec<&_>> =
++ FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
++
++ for expr in exprs {
++ match map.entry(hash(expr)) {
++ Entry::Occupied(mut o) => {
++ for o in o.get() {
++ if eq(o, expr) {
++ match_expr_list.push((o, expr));
++ }
++ }
++ o.get_mut().push(expr);
++ },
++ Entry::Vacant(v) => {
++ v.insert(vec![expr]);
++ },
++ }
++ }
++
++ match_expr_list
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for types that implement `Copy` as well as
++ /// `Iterator`.
++ ///
++ /// **Why is this bad?** Implicit copies can be confusing when working with
++ /// iterator combinators.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// #[derive(Copy, Clone)]
++ /// struct Countdown(u8);
++ ///
++ /// impl Iterator for Countdown {
++ /// // ...
++ /// }
++ ///
++ /// let a: Vec<_> = my_iterator.take(1).collect();
++ /// let b: Vec<_> = my_iterator.collect();
++ /// ```
++ pub COPY_ITERATOR,
++ pedantic,
++ "implementing `Iterator` on a `Copy` type"
++}
++
++declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyIterator {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Impl {
++ of_trait: Some(ref trait_ref),
++ ..
++ } = item.kind
++ {
++ let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
++
++ if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
++ span_lint_and_note(
++ cx,
++ COPY_ITERATOR,
++ item.span,
++ "you are implementing `Iterator` on a `Copy` type",
++ None,
++ "consider implementing `IntoIterator` instead",
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
++use rustc_ast::ast;
++use rustc_ast::tokenstream::TokenStream;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of dbg!() macro.
++ ///
++ /// **Why is this bad?** `dbg!` macro is intended as a debugging tool. It
++ /// should not be in version control.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// // Bad
++ /// dbg!(true)
++ ///
++ /// // Good
++ /// true
++ /// ```
++ pub DBG_MACRO,
++ restriction,
++ "`dbg!` macro is intended as a debugging tool"
++}
++
++declare_lint_pass!(DbgMacro => [DBG_MACRO]);
++
++impl EarlyLintPass for DbgMacro {
++ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
++ if mac.path == sym!(dbg) {
++ if let Some(sugg) = tts_span(mac.args.inner_tokens()).and_then(|span| snippet_opt(cx, span)) {
++ span_lint_and_sugg(
++ cx,
++ DBG_MACRO,
++ mac.span(),
++ "`dbg!` macro is intended as a debugging tool",
++ "ensure to avoid having uses of it in version control",
++ sugg,
++ Applicability::MaybeIncorrect,
++ );
++ } else {
++ span_lint_and_help(
++ cx,
++ DBG_MACRO,
++ mac.span(),
++ "`dbg!` macro is intended as a debugging tool",
++ None,
++ "ensure to avoid having uses of it in version control",
++ );
++ }
++ }
++ }
++}
++
++// Get span enclosing entire the token stream.
++fn tts_span(tts: TokenStream) -> Option<Span> {
++ let mut cursor = tts.into_trees();
++ let first = cursor.next()?.span();
++ let span = cursor.last().map_or(first, |tree| first.to(tree.span()));
++ Some(span)
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg};
++
++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_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref 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.tables.qpath_res(qpath, path.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
++ then {
++ match qpath {
++ QPath::Resolved(..) => {
++ if_chain! {
++ // Detect and ignore <Foo as Default>::default() because these calls do
++ // explicitly name the type.
++ if let ExprKind::Call(ref method, ref _args) = expr.kind;
++ if let ExprKind::Path(ref p) = method.kind;
++ if let QPath::Resolved(Some(_ty), _path) = p;
++ then {
++ return;
++ }
++ }
++
++ // 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 expr_ty = cx.tables.expr_ty(expr);
++ if let ty::Adt(..) = expr_ty.kind {
++ let replacement = format!("{}::default()", expr_ty);
++ 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
++ );
++ }
++ },
++ QPath::TypeRelative(..) => {},
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++macro_rules! declare_deprecated_lint {
++ (pub $name: ident, $_reason: expr) => {
++ declare_lint!(pub $name, Allow, "deprecated lint")
++ }
++}
++
++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 used to check for `.to_string()` method calls on values
++ /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be
++ /// specialized to be as efficient as `to_owned`.
++ pub STR_TO_STRING,
++ "using `str::to_string` is common even today and specialization will likely happen soon"
++}
++
++declare_deprecated_lint! {
++ /// **What it does:** Nothing. This lint has been deprecated.
++ ///
++ /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
++ /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be
++ /// specialized to be as efficient as `clone`.
++ pub STRING_TO_STRING,
++ "using `string::to_string` is common even today and specialization will likely happen soon"
++}
++
++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 the warn-by-default
++ /// `invalid_value` rustc lint.
++ pub INVALID_REF,
++ "superseded by rustc lint `invalid_value`"
++}
++
++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:** This lint has been uplifted to rustc and is now called
++ /// `array_into_iter`.
++ pub INTO_ITER_ON_ARRAY,
++ "this lint has been uplifted to rustc and is now called `array_into_iter`"
++}
++
++declare_deprecated_lint! {
++ /// **What it does:** Nothing. This lint has been deprecated.
++ ///
++ /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
++ /// `unused_labels`.
++ pub UNUSED_LABEL,
++ "this lint has been uplifted to rustc and is now called `unused_labels`"
++}
++
++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 prefer to `{min,max}_value()` and module constants"
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
++ ///
++ /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise,
++ /// when not part of a method chain.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::ops::Deref;
++ /// let a: &mut String = &mut String::from("foo");
++ /// let b: &str = a.deref();
++ /// ```
++ /// Could be written as:
++ /// ```rust
++ /// let a: &mut String = &mut String::from("foo");
++ /// let b = &*a;
++ /// ```
++ ///
++ /// This lint excludes
++ /// ```rust,ignore
++ /// let _ = d.unwrap().deref();
++ /// ```
++ pub EXPLICIT_DEREF_METHODS,
++ pedantic,
++ "Explicit use of deref or deref_mut method while not in a method chain."
++}
++
++declare_lint_pass!(Dereferencing => [
++ EXPLICIT_DEREF_METHODS
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if !expr.span.from_expansion();
++ if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.kind;
++ if args.len() == 1;
++
++ then {
++ if let Some(parent_expr) = get_parent_expr(cx, expr) {
++ // Check if we have the whole call chain here
++ if let ExprKind::MethodCall(..) = parent_expr.kind {
++ return;
++ }
++ // Check for Expr that we don't want to be linted
++ let precedence = parent_expr.precedence();
++ match precedence {
++ // Lint a Call is ok though
++ ExprPrecedence::Call | ExprPrecedence::AddrOf => (),
++ _ => {
++ if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX {
++ return;
++ }
++ }
++ }
++ }
++ let name = method_name.ident.as_str();
++ lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
++ }
++ }
++ }
++}
++
++fn lint_deref(cx: &LateContext<'_, '_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
++ match method_name {
++ "deref" => {
++ if cx
++ .tcx
++ .lang_items()
++ .deref_trait()
++ .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
++ {
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_DEREF_METHODS,
++ expr_span,
++ "explicit deref method call",
++ "try this",
++ format!("&*{}", &snippet(cx, var_span, "..")),
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ "deref_mut" => {
++ if cx
++ .tcx
++ .lang_items()
++ .deref_mut_trait()
++ .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
++ {
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_DEREF_METHODS,
++ expr_span,
++ "explicit deref_mut method call",
++ "try this",
++ format!("&mut *{}", &snippet(cx, var_span, "..")),
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ _ => (),
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths;
++use crate::utils::{
++ is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
++};
++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, 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 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, UNSAFE_DERIVE_DESERIALIZE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Impl {
++ of_trait: Some(ref trait_ref),
++ ..
++ } = item.kind
++ {
++ let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
++ let is_automatically_derived = is_automatically_derived(&*item.attrs);
++
++ check_hash_peq(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<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ span: Span,
++ trait_ref: &TraitRef<'_>,
++ ty: Ty<'tcx>,
++ hash_is_automatically_derived: bool,
++) {
++ if_chain! {
++ if match_path(&trait_ref.path, &paths::HASH);
++ if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
++ if let Some(def_id) = &trait_ref.trait_def_id();
++ if !def_id.is_local();
++ 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().as_local_hir_id(local_def_id);
++ diag.span_note(
++ cx.tcx.hir().span(hir_id),
++ "`PartialEq` implemented here"
++ );
++ }
++ }
++ );
++ }
++ });
++ }
++ }
++}
++
++/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
++fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
++ if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
++ if !is_copy(cx, ty) {
++ return;
++ }
++
++ match ty.kind {
++ ty::Adt(def, _) if def.is_union() => return,
++
++ // Some types are not Clone by default but could be cloned “by hand” if necessary
++ ty::Adt(def, substs) => {
++ for variant in &def.variants {
++ for field in &variant.fields {
++ if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind {
++ return;
++ }
++ }
++ for subst in substs {
++ if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() {
++ if let ty::Param(_) = subst.kind {
++ 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<'a, 'tcx>(
++ cx: &LateContext<'a, '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().as_local_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 match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
++ if let ty::Adt(def, _) = ty.kind;
++ if def.did.is_local();
++ 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<'a, '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 {
++ match block.rules {
++ BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
++ | BlockCheckMode::PushUnsafeBlock(UnsafeSource::UserProvided)
++ | BlockCheckMode::PopUnsafeBlock(UnsafeSource::UserProvided) => {
++ self.has_unsafe = true;
++ },
++ _ => {},
++ }
++ }
++
++ walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::All(self.cx.tcx.hir())
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint};
++use if_chain::if_chain;
++use itertools::Itertools;
++use rustc_ast::ast::{AttrKind, Attribute};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{BytePos, MultiSpan, Span};
++use rustc_span::Pos;
++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.
++ ///
++ /// **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) {}
++ /// ```
++ 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 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, NEEDLESS_DOCTEST_MAIN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown {
++ fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
++ check_attrs(cx, &self.valid_idents, &krate.item.attrs);
++ }
++
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
++ match item.kind {
++ hir::ItemKind::Fn(ref sig, _, body_id) => {
++ if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id).to_def_id())
++ || in_external_macro(cx.tcx.sess, item.span))
++ {
++ lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id));
++ }
++ },
++ hir::ItemKind::Impl {
++ of_trait: ref trait_ref,
++ ..
++ } => {
++ self.in_trait_impl = trait_ref.is_some();
++ },
++ _ => {},
++ }
++ }
++
++ fn check_item_post(&mut self, _cx: &LateContext<'a, '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<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++ let headers = check_attrs(cx, &self.valid_idents, &item.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);
++ }
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++ let headers = check_attrs(cx, &self.valid_idents, &item.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 {
++ lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id));
++ }
++ }
++}
++
++fn lint_for_missing_headers<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ hir_id: hir::HirId,
++ span: impl Into<MultiSpan> + Copy,
++ sig: &hir::FnSig<'_>,
++ headers: DocHeaders,
++ body_id: Option<hir::BodyId>,
++) {
++ 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.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 def_id = cx.tcx.hir().body_owner_def_id(body_id);
++ let mir = cx.tcx.optimized_mir(def_id.to_def_id());
++ let ret_ty = mir.return_ty();
++ 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 (`///` and such).
++///
++/// 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(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
++ // one-line comments lose their prefix
++ const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
++ for prefix in ONELINERS {
++ if comment.starts_with(*prefix) {
++ let doc = &comment[prefix.len()..];
++ let mut doc = doc.to_owned();
++ doc.push('\n');
++ return (
++ doc.to_owned(),
++ vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))],
++ );
++ }
++ }
++
++ if comment.starts_with("/*") {
++ let doc = &comment[3..comment.len() - 2];
++ let mut sizes = vec![];
++ let mut contains_initial_stars = false;
++ for line in doc.lines() {
++ let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
++ debug_assert_eq!(offset as u32 as usize, offset);
++ contains_initial_stars |= line.trim_start().starts_with('*');
++ // +1 for the newline
++ sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(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();
++ while let Some(c) = chars.next() {
++ 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');
++ }
++ return (no_stars, sizes);
++ }
++
++ panic!("not a doc-comment: {}", comment);
++}
++
++#[derive(Copy, Clone)]
++struct DocHeaders {
++ safety: bool,
++ errors: 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(ref comment) = attr.kind {
++ let comment = comment.to_string();
++ let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span);
++ spans.extend_from_slice(¤t_spans);
++ doc.push_str(&comment);
++ } else if attr.check_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,
++ };
++ }
++ }
++
++ 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,
++ };
++ }
++
++ 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(¤t);
++ 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", "edition2018"];
++
++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};
++
++ let mut headers = DocHeaders {
++ safety: false,
++ errors: false,
++ };
++ let mut in_code = false;
++ let mut in_link = None;
++ let mut in_heading = false;
++ let mut is_rust = false;
++ for (event, range) in events {
++ match event {
++ Start(CodeBlock(ref kind)) => {
++ in_code = true;
++ if let CodeBlockKind::Fenced(lang) = kind {
++ is_rust =
++ lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i));
++ }
++ },
++ 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(_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() {
++ // 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";
++ 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 {
++ check_code(cx, &text, 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);
++ }
++ },
++ }
++ }
++ headers
++}
++
++static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"];
++
++fn check_code(cx: &LateContext<'_, '_>, text: &str, span: Span) {
++ if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) {
++ 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 = if s.ends_with('s') { &s[..s.len() - 1] } else { 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),
++ );
++ }
++}
--- /dev/null
--- /dev/null
++//! Lint on unnecessary double comparisons. Some examples:
++
++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::Span;
++
++use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for double comparisons that could be simplified to a single expression.
++ ///
++ ///
++ /// **Why is this bad?** Readability.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// # let y = 2;
++ /// if x == y || x < y {}
++ /// ```
++ ///
++ /// Could be written as:
++ ///
++ /// ```rust
++ /// # let x = 1;
++ /// # let y = 2;
++ /// if x <= y {}
++ /// ```
++ pub DOUBLE_COMPARISONS,
++ complexity,
++ "unnecessary double comparisons that can be simplified"
++}
++
++declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
++
++impl<'a, 'tcx> DoubleComparisons {
++ #[allow(clippy::similar_names)]
++ fn check_binop(cx: &LateContext<'a, 'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
++ let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
++ (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
++ (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
++ },
++ _ => return,
++ };
++ let mut spanless_eq = SpanlessEq::new(cx).ignore_fn();
++ if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) {
++ return;
++ }
++ macro_rules! lint_double_comparison {
++ ($op:tt) => {{
++ let mut applicability = Applicability::MachineApplicable;
++ let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
++ let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
++ let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
++ span_lint_and_sugg(
++ cx,
++ DOUBLE_COMPARISONS,
++ span,
++ "This binary expression can be simplified",
++ "try",
++ sugg,
++ applicability,
++ );
++ }};
++ }
++ #[rustfmt::skip]
++ match (op, lkind, rkind) {
++ (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
++ lint_double_comparison!(<=)
++ },
++ (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
++ lint_double_comparison!(>=)
++ },
++ (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
++ lint_double_comparison!(!=)
++ },
++ (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
++ lint_double_comparison!(==)
++ },
++ _ => (),
++ };
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DoubleComparisons {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind {
++ Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for unnecessary double parentheses.
++ ///
++ /// **Why is this bad?** This makes code harder to read and might indicate a
++ /// mistake.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # fn foo(bar: usize) {}
++ /// ((0));
++ /// foo((0));
++ /// ((1, 2));
++ /// ```
++ pub DOUBLE_PARENS,
++ complexity,
++ "Warn on unnecessary double parentheses"
++}
++
++declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]);
++
++impl EarlyLintPass for DoubleParens {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ match expr.kind {
++ ExprKind::Paren(ref in_paren) => match in_paren.kind {
++ ExprKind::Paren(_) | ExprKind::Tup(_) => {
++ span_lint(
++ cx,
++ DOUBLE_PARENS,
++ expr.span,
++ "Consider removing unnecessary double parentheses",
++ );
++ },
++ _ => {},
++ },
++ ExprKind::Call(_, ref params) => {
++ if params.len() == 1 {
++ let param = ¶ms[0];
++ if let ExprKind::Paren(_) = param.kind {
++ span_lint(
++ cx,
++ DOUBLE_PARENS,
++ param.span,
++ "Consider removing unnecessary double parentheses",
++ );
++ }
++ }
++ },
++ ExprKind::MethodCall(_, ref params) => {
++ if params.len() == 2 {
++ let param = ¶ms[1];
++ if let ExprKind::Paren(_) = param.kind {
++ span_lint(
++ cx,
++ DOUBLE_PARENS,
++ param.span,
++ "Consider removing unnecessary double parentheses",
++ );
++ }
++ }
++ },
++ _ => {},
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, span_lint};
++use if_chain::if_chain;
++use rustc_hir::{GenericBound, GenericParam, WhereBoundPredicate, WherePredicate};
++use rustc_lint::LateLintPass;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for generics with `std::ops::Drop` as bounds.
++ ///
++ /// **Why is this bad?** `Drop` bounds do not really accomplish anything.
++ /// A type may have compiler-generated drop glue without implementing the
++ /// `Drop` trait itself. The `Drop` trait also only has one method,
++ /// `Drop::drop`, and that function is by fiat not callable in user code.
++ /// So there is really no use case for using `Drop` in trait bounds.
++ ///
++ /// The most likely use case of a drop bound is to distinguish between types
++ /// that have destructors and types that don't. Combined with specialization,
++ /// a naive coder would write an implementation that assumed a type could be
++ /// trivially dropped, then write a specialization for `T: Drop` that actually
++ /// calls the destructor. Except that doing so is not correct; String, for
++ /// example, doesn't actually implement Drop, but because String contains a
++ /// Vec, assuming it can be trivially dropped will leak memory.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo<T: Drop>() {}
++ /// ```
++ pub DROP_BOUNDS,
++ correctness,
++ "Bounds of the form `T: Drop` are useless"
++}
++
++const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
++ Use `std::mem::needs_drop` to detect if a type has drop glue.";
++
++declare_lint_pass!(DropBounds => [DROP_BOUNDS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropBounds {
++ fn check_generic_param(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam<'_>) {
++ for bound in p.bounds.iter() {
++ lint_bound(cx, bound);
++ }
++ }
++ fn check_where_predicate(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate<'_>) {
++ if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p {
++ for bound in *bounds {
++ lint_bound(cx, bound);
++ }
++ }
++ }
++}
++
++fn lint_bound<'a, 'tcx>(cx: &rustc_lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound<'_>) {
++ if_chain! {
++ if let GenericBound::Trait(t, _) = bound;
++ if let Some(def_id) = t.trait_ref.path.res.opt_def_id();
++ if match_def_path(cx, def_id, &paths::DROP_TRAIT);
++ then {
++ span_lint(
++ cx,
++ DROP_BOUNDS,
++ t.span,
++ DROP_BOUNDS_SUMMARY
++ );
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `std::mem::drop` with a reference
++ /// instead of an owned value.
++ ///
++ /// **Why is this bad?** Calling `drop` on a reference will only drop the
++ /// reference itself, which is a no-op. It will not call the `drop` method (from
++ /// the `Drop` trait implementation) on the underlying referenced value, which
++ /// is likely what was intended.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let mut lock_guard = mutex.lock();
++ /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
++ /// // still locked
++ /// operation_that_requires_mutex_to_be_unlocked();
++ /// ```
++ pub DROP_REF,
++ correctness,
++ "calls to `std::mem::drop` with a reference instead of an owned value"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `std::mem::forget` with a reference
++ /// instead of an owned value.
++ ///
++ /// **Why is this bad?** Calling `forget` on a reference will only forget the
++ /// reference itself, which is a no-op. It will not forget the underlying
++ /// referenced
++ /// value, which is likely what was intended.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = Box::new(1);
++ /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
++ /// ```
++ pub FORGET_REF,
++ correctness,
++ "calls to `std::mem::forget` with a reference instead of an owned value"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `std::mem::drop` with a value
++ /// that derives the Copy trait
++ ///
++ /// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
++ /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
++ /// value will be copied and moved into the function on invocation.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x: i32 = 42; // i32 implements Copy
++ /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
++ /// // original unaffected
++ /// ```
++ pub DROP_COPY,
++ correctness,
++ "calls to `std::mem::drop` with a value that implements Copy"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `std::mem::forget` with a value that
++ /// derives the Copy trait
++ ///
++ /// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
++ /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
++ /// value will be copied and moved into the function on invocation.
++ ///
++ /// An alternative, but also valid, explanation is that Copy types do not
++ /// implement
++ /// the Drop trait, which means they have no destructors. Without a destructor,
++ /// there
++ /// is nothing for `std::mem::forget` to ignore.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x: i32 = 42; // i32 implements Copy
++ /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
++ /// // original unaffected
++ /// ```
++ pub FORGET_COPY,
++ correctness,
++ "calls to `std::mem::forget` with a value that implements Copy"
++}
++
++const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
++ Dropping a reference does nothing.";
++const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
++ Forgetting a reference does nothing.";
++const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
++ Dropping a copy leaves the original intact.";
++const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
++ Forgetting a copy leaves the original intact.";
++
++declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropForgetRef {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref path, ref args) = expr.kind;
++ if let ExprKind::Path(ref qpath) = path.kind;
++ if args.len() == 1;
++ if let Some(def_id) = qpath_res(cx, qpath, path.hir_id).opt_def_id();
++ then {
++ let lint;
++ let msg;
++ let arg = &args[0];
++ let arg_ty = cx.tables.expr_ty(arg);
++
++ if let ty::Ref(..) = arg_ty.kind {
++ if match_def_path(cx, def_id, &paths::DROP) {
++ lint = DROP_REF;
++ msg = DROP_REF_SUMMARY.to_string();
++ } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++ lint = FORGET_REF;
++ msg = FORGET_REF_SUMMARY.to_string();
++ } else {
++ return;
++ }
++ span_lint_and_note(cx,
++ lint,
++ expr.span,
++ &msg,
++ Some(arg.span),
++ &format!("argument has type `{}`", arg_ty));
++ } else if is_copy(cx, arg_ty) {
++ if match_def_path(cx, def_id, &paths::DROP) {
++ lint = DROP_COPY;
++ msg = DROP_COPY_SUMMARY.to_string();
++ } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++ lint = FORGET_COPY;
++ msg = FORGET_COPY_SUMMARY.to_string();
++ } else {
++ return;
++ }
++ span_lint_and_note(cx,
++ lint,
++ expr.span,
++ &msg,
++ Some(arg.span),
++ &format!("argument has type {}", arg_ty));
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++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 crate::consts::{constant, Constant};
++use crate::utils::paths;
++use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
++ /// from other `Duration` methods.
++ ///
++ /// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or
++ /// `Duration::subsec_millis()` than to calculate them.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::time::Duration;
++ /// let dur = Duration::new(5, 0);
++ /// let _micros = dur.subsec_nanos() / 1_000;
++ /// let _millis = dur.subsec_nanos() / 1_000_000;
++ /// ```
++ pub DURATION_SUBSEC,
++ complexity,
++ "checks for calculation of subsecond microseconds or milliseconds"
++}
++
++declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DurationSubsec {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind;
++ if let ExprKind::MethodCall(ref method_path, _ , ref args) = left.kind;
++ if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::DURATION);
++ if let Some((Constant::Int(divisor), _)) = constant(cx, cx.tables, right);
++ then {
++ let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) {
++ ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
++ ("subsec_nanos", 1_000) => "subsec_micros",
++ _ => return,
++ };
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ DURATION_SUBSEC,
++ expr.span,
++ &format!("Calling `{}()` is more concise than this calculation", suggested_fn),
++ "try",
++ format!(
++ "{}.{}()",
++ snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
++ suggested_fn
++ ),
++ applicability,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! Lint on if expressions with an else if, but without a final else branch.
++
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::span_lint_and_help;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of if expressions with an `else if` branch,
++ /// but without a final `else` branch.
++ ///
++ /// **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # fn a() {}
++ /// # fn b() {}
++ /// # let x: i32 = 1;
++ /// if x.is_positive() {
++ /// a();
++ /// } else if x.is_negative() {
++ /// b();
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// # fn a() {}
++ /// # fn b() {}
++ /// # let x: i32 = 1;
++ /// if x.is_positive() {
++ /// a();
++ /// } else if x.is_negative() {
++ /// b();
++ /// } else {
++ /// // We don't care about zero.
++ /// }
++ /// ```
++ pub ELSE_IF_WITHOUT_ELSE,
++ restriction,
++ "`if` expression with an `else if`, but without a final `else` branch"
++}
++
++declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]);
++
++impl EarlyLintPass for ElseIfWithoutElse {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) {
++ if in_external_macro(cx.sess(), item.span) {
++ return;
++ }
++
++ while let ExprKind::If(_, _, Some(ref els)) = item.kind {
++ if let ExprKind::If(_, _, None) = els.kind {
++ span_lint_and_help(
++ cx,
++ ELSE_IF_WITHOUT_ELSE,
++ els.span,
++ "`if` expression with an `else if`, but without a final `else`",
++ None,
++ "add an `else` block here",
++ );
++ }
++
++ item = els;
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint when there is an enum with no variants
++
++use crate::utils::span_lint_and_help;
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `enum`s with no variants.
++ ///
++ /// **Why is this bad?** If you want to introduce a type which
++ /// can't be instantiated, you should use `!` (the never type),
++ /// or a wrapper around it, because `!` has more extensive
++ /// compiler support (type inference, etc...) and wrappers
++ /// around it are the conventional way to define an uninhabited type.
++ /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
++ ///
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// Bad:
++ /// ```rust
++ /// enum Test {}
++ /// ```
++ ///
++ /// Good:
++ /// ```rust
++ /// #![feature(never_type)]
++ ///
++ /// struct Test(!);
++ /// ```
++ pub EMPTY_ENUM,
++ pedantic,
++ "enum with no variants"
++}
++
++declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
++ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++ let did = cx.tcx.hir().local_def_id(item.hir_id);
++ if let ItemKind::Enum(..) = item.kind {
++ let ty = cx.tcx.type_of(did);
++ let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
++ if adt.variants.is_empty() {
++ span_lint_and_help(
++ cx,
++ EMPTY_ENUM,
++ item.span,
++ "enum with no variants",
++ None,
++ "consider using the uninhabited type `!` (never type) or a wrapper \
++ around it to introduce a type which can't be instantiated",
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::SpanlessEq;
++use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
++use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
++ /// or `BTreeMap`.
++ ///
++ /// **Why is this bad?** Using `entry` is more efficient.
++ ///
++ /// **Known problems:** Some false negatives, eg.:
++ /// ```rust
++ /// # use std::collections::HashMap;
++ /// # let mut map = HashMap::new();
++ /// # let v = 1;
++ /// # let k = 1;
++ /// if !map.contains_key(&k) {
++ /// map.insert(k.clone(), v);
++ /// }
++ /// ```
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::collections::HashMap;
++ /// # let mut map = HashMap::new();
++ /// # let k = 1;
++ /// # let v = 1;
++ /// if !map.contains_key(&k) {
++ /// map.insert(k, v);
++ /// }
++ /// ```
++ /// can both be rewritten as:
++ /// ```rust
++ /// # use std::collections::HashMap;
++ /// # let mut map = HashMap::new();
++ /// # let k = 1;
++ /// # let v = 1;
++ /// map.entry(k).or_insert(v);
++ /// ```
++ pub MAP_ENTRY,
++ perf,
++ "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
++}
++
++declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapPass {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let Some((ref check, ref then_block, ref else_block)) = higher::if_block(&expr) {
++ if let ExprKind::Unary(UnOp::UnNot, ref check) = check.kind {
++ if let Some((ty, map, key)) = check_cond(cx, check) {
++ // in case of `if !m.contains_key(&k) { m.insert(k, v); }`
++ // we can give a better error message
++ let sole_expr = {
++ else_block.is_none()
++ && if let ExprKind::Block(ref then_block, _) = then_block.kind {
++ (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
++ } else {
++ true
++ }
++ // XXXManishearth we can also check for if/else blocks containing `None`.
++ };
++
++ let mut visitor = InsertVisitor {
++ cx,
++ span: expr.span,
++ ty,
++ map,
++ key,
++ sole_expr,
++ };
++
++ walk_expr(&mut visitor, &**then_block);
++ }
++ } else if let Some(ref else_block) = *else_block {
++ if let Some((ty, map, key)) = check_cond(cx, check) {
++ let mut visitor = InsertVisitor {
++ cx,
++ span: expr.span,
++ ty,
++ map,
++ key,
++ sole_expr: false,
++ };
++
++ walk_expr(&mut visitor, else_block);
++ }
++ }
++ }
++ }
++}
++
++fn check_cond<'a, 'tcx, 'b>(
++ cx: &'a LateContext<'a, 'tcx>,
++ check: &'b Expr<'b>,
++) -> Option<(&'static str, &'b Expr<'b>, &'b Expr<'b>)> {
++ if_chain! {
++ if let ExprKind::MethodCall(ref path, _, ref params) = check.kind;
++ if params.len() >= 2;
++ if path.ident.name == sym!(contains_key);
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind;
++ then {
++ let map = ¶ms[0];
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(map));
++
++ return if match_type(cx, obj_ty, &paths::BTREEMAP) {
++ Some(("BTreeMap", map, key))
++ }
++ else if is_type_diagnostic_item(cx, obj_ty, sym!(hashmap_type)) {
++ Some(("HashMap", map, key))
++ }
++ else {
++ None
++ };
++ }
++ }
++
++ None
++}
++
++struct InsertVisitor<'a, 'tcx, 'b> {
++ cx: &'a LateContext<'a, 'tcx>,
++ span: Span,
++ ty: &'static str,
++ map: &'b Expr<'b>,
++ key: &'b Expr<'b>,
++ sole_expr: bool,
++}
++
++impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::MethodCall(ref path, _, ref params) = expr.kind;
++ if params.len() == 3;
++ if path.ident.name == sym!(insert);
++ if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]);
++ if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]);
++ if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
++ then {
++ span_lint_and_then(self.cx, MAP_ENTRY, self.span,
++ &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
++ if self.sole_expr {
++ let mut app = Applicability::MachineApplicable;
++ let help = format!("{}.entry({}).or_insert({});",
++ snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
++ snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
++ snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
++
++ diag.span_suggestion(
++ self.span,
++ "consider using",
++ help,
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++ else {
++ let help = format!("consider using `{}.entry({})`",
++ snippet(self.cx, self.map.span, "map"),
++ snippet(self.cx, params[1].span, ".."));
++
++ diag.span_label(
++ self.span,
++ &help,
++ );
++ }
++ });
++ }
++ }
++
++ if !self.sole_expr {
++ walk_expr(self, expr);
++ }
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on C-like enums that are `repr(isize/usize)` and have values that
++//! don't fit into an `i32`
++
++use crate::consts::{miri_to_const, Constant};
++use crate::utils::span_lint;
++use rustc_ast::ast::{IntTy, UintTy};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_middle::ty::util::IntTypeExt;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for C-like enumerations that are
++ /// `repr(isize/usize)` and have values that don't fit into an `i32`.
++ ///
++ /// **Why is this bad?** This will truncate the variant value on 32 bit
++ /// architectures, but works fine on 64 bit.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # #[cfg(target_pointer_width = "64")]
++ /// #[repr(usize)]
++ /// enum NonPortable {
++ /// X = 0x1_0000_0000,
++ /// Y = 0,
++ /// }
++ /// ```
++ pub ENUM_CLIKE_UNPORTABLE_VARIANT,
++ correctness,
++ "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
++}
++
++declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
++ #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)]
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if cx.tcx.data_layout.pointer_size.bits() != 64 {
++ return;
++ }
++ if let ItemKind::Enum(def, _) = &item.kind {
++ for var in def.variants {
++ if let Some(anon_const) = &var.disr_expr {
++ let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body);
++ let mut ty = cx.tcx.type_of(def_id.to_def_id());
++ let constant = cx
++ .tcx
++ .const_eval_poly(def_id.to_def_id())
++ .ok()
++ .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty));
++ if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) {
++ if let ty::Adt(adt, _) = ty.kind {
++ if adt.is_enum() {
++ ty = adt.repr.discr_type().to_ty(cx.tcx);
++ }
++ }
++ match ty.kind {
++ ty::Int(IntTy::Isize) => {
++ let val = ((val as i128) << 64) >> 64;
++ if i32::try_from(val).is_ok() {
++ continue;
++ }
++ },
++ ty::Uint(UintTy::Usize) if val > u128::from(u32::max_value()) => {},
++ _ => continue,
++ }
++ span_lint(
++ cx,
++ ENUM_CLIKE_UNPORTABLE_VARIANT,
++ var.span,
++ "Clike enum variant discriminant is not portable to 32-bit targets",
++ );
++ };
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on enum variants that are prefixed or suffixed by the same characters
++
++use crate::utils::{camel_case, is_present_in_source};
++use crate::utils::{span_lint, span_lint_and_help};
++use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::Symbol;
++
++declare_clippy_lint! {
++ /// **What it does:** Detects enumeration variants that are prefixed or suffixed
++ /// by the same characters.
++ ///
++ /// **Why is this bad?** Enumeration variant names should specify their variant,
++ /// not repeat the enumeration name.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// enum Cake {
++ /// BlackForestCake,
++ /// HummingbirdCake,
++ /// BattenbergCake,
++ /// }
++ /// ```
++ pub ENUM_VARIANT_NAMES,
++ style,
++ "enums where all variants share a prefix/postfix"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Detects enumeration variants that are prefixed or suffixed
++ /// by the same characters.
++ ///
++ /// **Why is this bad?** Enumeration variant names should specify their variant,
++ /// not repeat the enumeration name.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// enum Cake {
++ /// BlackForestCake,
++ /// HummingbirdCake,
++ /// BattenbergCake,
++ /// }
++ /// ```
++ pub PUB_ENUM_VARIANT_NAMES,
++ pedantic,
++ "enums where all variants share a prefix/postfix"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Detects type names that are prefixed or suffixed by the
++ /// containing module's name.
++ ///
++ /// **Why is this bad?** It requires the user to type the module name twice.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// mod cake {
++ /// struct BlackForestCake;
++ /// }
++ /// ```
++ pub MODULE_NAME_REPETITIONS,
++ pedantic,
++ "type names prefixed/postfixed with their containing module's name"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for modules that have the same name as their
++ /// parent module
++ ///
++ /// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and
++ /// again `mod foo { ..
++ /// }` in `foo.rs`.
++ /// The expectation is that items inside the inner `mod foo { .. }` are then
++ /// available
++ /// through `foo::x`, but they are only available through
++ /// `foo::foo::x`.
++ /// If this is done on purpose, it would be better to choose a more
++ /// representative module name.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// // lib.rs
++ /// mod foo;
++ /// // foo.rs
++ /// mod foo {
++ /// ...
++ /// }
++ /// ```
++ pub MODULE_INCEPTION,
++ style,
++ "modules that have the same name as their parent module"
++}
++
++pub struct EnumVariantNames {
++ modules: Vec<(Symbol, String)>,
++ threshold: u64,
++}
++
++impl EnumVariantNames {
++ #[must_use]
++ pub fn new(threshold: u64) -> Self {
++ Self {
++ modules: Vec::new(),
++ threshold,
++ }
++ }
++}
++
++impl_lint_pass!(EnumVariantNames => [
++ ENUM_VARIANT_NAMES,
++ PUB_ENUM_VARIANT_NAMES,
++ MODULE_NAME_REPETITIONS,
++ MODULE_INCEPTION
++]);
++
++/// Returns the number of chars that match from the start
++#[must_use]
++fn partial_match(pre: &str, name: &str) -> usize {
++ let mut name_iter = name.chars();
++ let _ = name_iter.next_back(); // make sure the name is never fully matched
++ pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
++}
++
++/// Returns the number of chars that match from the end
++#[must_use]
++fn partial_rmatch(post: &str, name: &str) -> usize {
++ let mut name_iter = name.chars();
++ let _ = name_iter.next(); // make sure the name is never fully matched
++ post.chars()
++ .rev()
++ .zip(name_iter.rev())
++ .take_while(|&(l, r)| l == r)
++ .count()
++}
++
++fn check_variant(
++ cx: &EarlyContext<'_>,
++ threshold: u64,
++ def: &EnumDef,
++ item_name: &str,
++ item_name_chars: usize,
++ span: Span,
++ lint: &'static Lint,
++) {
++ if (def.variants.len() as u64) < threshold {
++ return;
++ }
++ for var in &def.variants {
++ let name = var.ident.name.as_str();
++ if partial_match(item_name, &name) == item_name_chars
++ && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
++ && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
++ {
++ span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
++ }
++ if partial_rmatch(item_name, &name) == item_name_chars {
++ span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
++ }
++ }
++ let first = &def.variants[0].ident.name.as_str();
++ let mut pre = &first[..camel_case::until(&*first)];
++ let mut post = &first[camel_case::from(&*first)..];
++ for var in &def.variants {
++ let name = var.ident.name.as_str();
++
++ let pre_match = partial_match(pre, &name);
++ pre = &pre[..pre_match];
++ let pre_camel = camel_case::until(pre);
++ pre = &pre[..pre_camel];
++ while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
++ if next.is_numeric() {
++ return;
++ }
++ if next.is_lowercase() {
++ let last = pre.len() - last.len_utf8();
++ let last_camel = camel_case::until(&pre[..last]);
++ pre = &pre[..last_camel];
++ } else {
++ break;
++ }
++ }
++
++ let post_match = partial_rmatch(post, &name);
++ let post_end = post.len() - post_match;
++ post = &post[post_end..];
++ let post_camel = camel_case::from(post);
++ post = &post[post_camel..];
++ }
++ let (what, value) = match (pre.is_empty(), post.is_empty()) {
++ (true, true) => return,
++ (false, _) => ("pre", pre),
++ (true, false) => ("post", post),
++ };
++ span_lint_and_help(
++ cx,
++ lint,
++ span,
++ &format!("All variants have the same {}fix: `{}`", what, value),
++ None,
++ &format!(
++ "remove the {}fixes and use full paths to \
++ the variants instead of glob imports",
++ what
++ ),
++ );
++}
++
++#[must_use]
++fn to_camel_case(item_name: &str) -> String {
++ let mut s = String::new();
++ let mut up = true;
++ for c in item_name.chars() {
++ if c.is_uppercase() {
++ // we only turn snake case text into CamelCase
++ return item_name.to_string();
++ }
++ if c == '_' {
++ up = true;
++ continue;
++ }
++ if up {
++ up = false;
++ s.extend(c.to_uppercase());
++ } else {
++ s.push(c);
++ }
++ }
++ s
++}
++
++impl EarlyLintPass for EnumVariantNames {
++ fn check_item_post(&mut self, _cx: &EarlyContext<'_>, _item: &Item) {
++ let last = self.modules.pop();
++ assert!(last.is_some());
++ }
++
++ #[allow(clippy::similar_names)]
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ let item_name = item.ident.name.as_str();
++ let item_name_chars = item_name.chars().count();
++ let item_camel = to_camel_case(&item_name);
++ if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
++ if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
++ // constants don't have surrounding modules
++ if !mod_camel.is_empty() {
++ if mod_name == &item.ident.name {
++ if let ItemKind::Mod(..) = item.kind {
++ span_lint(
++ cx,
++ MODULE_INCEPTION,
++ item.span,
++ "module has the same name as its containing module",
++ );
++ }
++ }
++ if item.vis.node.is_pub() {
++ let matching = partial_match(mod_camel, &item_camel);
++ let rmatching = partial_rmatch(mod_camel, &item_camel);
++ let nchars = mod_camel.chars().count();
++
++ let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
++
++ if matching == nchars {
++ match item_camel.chars().nth(nchars) {
++ Some(c) if is_word_beginning(c) => span_lint(
++ cx,
++ MODULE_NAME_REPETITIONS,
++ item.span,
++ "item name starts with its containing module's name",
++ ),
++ _ => (),
++ }
++ }
++ if rmatching == nchars {
++ span_lint(
++ cx,
++ MODULE_NAME_REPETITIONS,
++ item.span,
++ "item name ends with its containing module's name",
++ );
++ }
++ }
++ }
++ }
++ }
++ if let ItemKind::Enum(ref def, _) = item.kind {
++ let lint = match item.vis.node {
++ VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES,
++ _ => ENUM_VARIANT_NAMES,
++ };
++ check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint);
++ }
++ self.modules.push((item.ident.name, item_camel));
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq,
++};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for equal operands to comparison, logical and
++ /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
++ /// `||`, `&`, `|`, `^`, `-` and `/`).
++ ///
++ /// **Why is this bad?** This is usually just a typo or a copy and paste error.
++ ///
++ /// **Known problems:** False negatives: We had some false positives regarding
++ /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
++ /// of `x.pop() && x.pop()`), so we removed matching any function or method
++ /// calls. We may introduce a whitelist of known pure functions in the future.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// if x + 1 == x + 1 {}
++ /// ```
++ pub EQ_OP,
++ correctness,
++ "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for arguments to `==` which have their address
++ /// taken to satisfy a bound
++ /// and suggests to dereference the other argument instead
++ ///
++ /// **Why is this bad?** It is more idiomatic to dereference the other argument.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// &x == y
++ /// ```
++ pub OP_REF,
++ style,
++ "taking a reference to satisfy the type constraints on `==`"
++}
++
++declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
++ #[allow(clippy::similar_names, clippy::too_many_lines)]
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(op, ref left, ref right) = e.kind {
++ if e.span.from_expansion() {
++ return;
++ }
++ let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
++ if let ExprKind::Unary(_, ref expr) = *expr_kind {
++ in_macro(expr.span)
++ } else {
++ false
++ }
++ };
++ if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
++ return;
++ }
++ if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
++ span_lint(
++ cx,
++ EQ_OP,
++ e.span,
++ &format!("equal expressions as operands to `{}`", op.node.as_str()),
++ );
++ return;
++ }
++ let (trait_id, requires_ref) = match op.node {
++ BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
++ BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
++ BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
++ BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
++ BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
++ // don't lint short circuiting ops
++ BinOpKind::And | BinOpKind::Or => return,
++ BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
++ BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
++ BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
++ BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
++ BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
++ BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
++ BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
++ (cx.tcx.lang_items().partial_ord_trait(), true)
++ },
++ };
++ if let Some(trait_id) = trait_id {
++ #[allow(clippy::match_same_arms)]
++ match (&left.kind, &right.kind) {
++ // do not suggest to dereference literals
++ (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
++ // &foo == &bar
++ (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
++ let lty = cx.tables.expr_ty(l);
++ let rty = cx.tables.expr_ty(r);
++ let lcpy = is_copy(cx, lty);
++ let rcpy = is_copy(cx, rty);
++ // either operator autorefs or both args are copyable
++ if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
++ span_lint_and_then(
++ cx,
++ OP_REF,
++ e.span,
++ "needlessly taken reference of both operands",
++ |diag| {
++ let lsnip = snippet(cx, l.span, "...").to_string();
++ let rsnip = snippet(cx, r.span, "...").to_string();
++ multispan_sugg(
++ diag,
++ "use the values directly".to_string(),
++ vec![(left.span, lsnip), (right.span, rsnip)],
++ );
++ },
++ )
++ } else if lcpy
++ && !rcpy
++ && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
++ {
++ span_lint_and_then(
++ cx,
++ OP_REF,
++ e.span,
++ "needlessly taken reference of left operand",
++ |diag| {
++ let lsnip = snippet(cx, l.span, "...").to_string();
++ diag.span_suggestion(
++ left.span,
++ "use the left value directly",
++ lsnip,
++ Applicability::MaybeIncorrect, // FIXME #2597
++ );
++ },
++ )
++ } else if !lcpy
++ && rcpy
++ && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
++ {
++ span_lint_and_then(
++ cx,
++ OP_REF,
++ e.span,
++ "needlessly taken reference of right operand",
++ |diag| {
++ let rsnip = snippet(cx, r.span, "...").to_string();
++ diag.span_suggestion(
++ right.span,
++ "use the right value directly",
++ rsnip,
++ Applicability::MaybeIncorrect, // FIXME #2597
++ );
++ },
++ )
++ }
++ },
++ // &foo == bar
++ (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => {
++ let lty = cx.tables.expr_ty(l);
++ let lcpy = is_copy(cx, lty);
++ if (requires_ref || lcpy)
++ && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
++ {
++ span_lint_and_then(
++ cx,
++ OP_REF,
++ e.span,
++ "needlessly taken reference of left operand",
++ |diag| {
++ let lsnip = snippet(cx, l.span, "...").to_string();
++ diag.span_suggestion(
++ left.span,
++ "use the left value directly",
++ lsnip,
++ Applicability::MaybeIncorrect, // FIXME #2597
++ );
++ },
++ )
++ }
++ },
++ // foo == &bar
++ (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
++ let rty = cx.tables.expr_ty(r);
++ let rcpy = is_copy(cx, rty);
++ if (requires_ref || rcpy)
++ && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
++ {
++ span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
++ let rsnip = snippet(cx, r.span, "...").to_string();
++ diag.span_suggestion(
++ right.span,
++ "use the right value directly",
++ rsnip,
++ Applicability::MaybeIncorrect, // FIXME #2597
++ );
++ })
++ }
++ },
++ _ => {},
++ }
++ }
++ }
++ }
++}
++
++fn is_valid_operator(op: BinOp) -> bool {
++ match op.node {
++ BinOpKind::Sub
++ | BinOpKind::Div
++ | BinOpKind::Eq
++ | BinOpKind::Lt
++ | BinOpKind::Le
++ | BinOpKind::Gt
++ | BinOpKind::Ge
++ | BinOpKind::Ne
++ | BinOpKind::And
++ | BinOpKind::Or
++ | BinOpKind::BitXor
++ | BinOpKind::BitAnd
++ | BinOpKind::BitOr => true,
++ _ => false,
++ }
++}
--- /dev/null
--- /dev/null
++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::Span;
++
++use crate::consts::{constant_simple, Constant};
++use crate::utils::span_lint;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for erasing operations, e.g., `x * 0`.
++ ///
++ /// **Why is this bad?** The whole expression can be replaced by zero.
++ /// This is most likely not the intended outcome and should probably be
++ /// corrected
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 1;
++ /// 0 / x;
++ /// 0 * x;
++ /// x & 0;
++ /// ```
++ pub ERASING_OP,
++ correctness,
++ "using erasing operations, e.g., `x * 0` or `y & 0`"
++}
++
++declare_lint_pass!(ErasingOp => [ERASING_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ErasingOp {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if e.span.from_expansion() {
++ return;
++ }
++ if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind {
++ match cmp.node {
++ BinOpKind::Mul | BinOpKind::BitAnd => {
++ check(cx, left, e.span);
++ check(cx, right, e.span);
++ },
++ BinOpKind::Div => check(cx, left, e.span),
++ _ => (),
++ }
++ }
++ }
++}
++
++fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, span: Span) {
++ if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, e) {
++ span_lint(
++ cx,
++ ERASING_OP,
++ span,
++ "this operation will always return zero. This is likely not the intended outcome",
++ );
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_hir::intravisit;
++use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_target::abi::LayoutOf;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++
++use crate::utils::span_lint;
++
++#[derive(Copy, Clone)]
++pub struct BoxedLocal {
++ pub too_large_for_stack: u64,
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
++ /// work fine.
++ ///
++ /// **Why is this bad?** This is an unnecessary allocation, and bad for
++ /// performance. It is only necessary to allocate if you wish to move the box
++ /// into something.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # fn foo(bar: usize) {}
++ /// let x = Box::new(1);
++ /// foo(*x);
++ /// println!("{}", *x);
++ /// ```
++ pub BOXED_LOCAL,
++ perf,
++ "using `Box<T>` where unnecessary"
++}
++
++fn is_non_trait_box(ty: Ty<'_>) -> bool {
++ ty.is_box() && !ty.boxed_ty().is_trait()
++}
++
++struct EscapeDelegate<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ set: HirIdSet,
++ too_large_for_stack: u64,
++}
++
++impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ _: intravisit::FnKind<'tcx>,
++ _: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ _: Span,
++ hir_id: HirId,
++ ) {
++ // If the method is an impl for a trait, don't warn.
++ let parent_id = cx.tcx.hir().get_parent_item(hir_id);
++ let parent_node = cx.tcx.hir().find(parent_id);
++
++ if let Some(Node::Item(item)) = parent_node {
++ if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
++ return;
++ }
++ }
++
++ let mut v = EscapeDelegate {
++ cx,
++ set: HirIdSet::default(),
++ too_large_for_stack: self.too_large_for_stack,
++ };
++
++ let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
++ });
++
++ for node in v.set {
++ span_lint(
++ cx,
++ BOXED_LOCAL,
++ cx.tcx.hir().span(node),
++ "local variable doesn't need to be boxed here",
++ );
++ }
++ }
++}
++
++// TODO: Replace with Map::is_argument(..) when it's fixed
++fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
++ match map.find(id) {
++ Some(Node::Binding(_)) => (),
++ _ => return false,
++ }
++
++ match map.find(map.get_parent_node(id)) {
++ Some(Node::Param(_)) => true,
++ _ => false,
++ }
++}
++
++impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
++ fn consume(&mut self, cmt: &Place<'tcx>, mode: ConsumeMode) {
++ if cmt.projections.is_empty() {
++ if let PlaceBase::Local(lid) = cmt.base {
++ if let ConsumeMode::Move = mode {
++ // moved out or in. clearly can't be localized
++ self.set.remove(&lid);
++ }
++ let map = &self.cx.tcx.hir();
++ if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
++ if self.set.contains(&lid) {
++ // let y = x where x is known
++ // remove x, insert y
++ self.set.insert(cmt.hir_id);
++ self.set.remove(&lid);
++ }
++ }
++ }
++ }
++ }
++
++ fn borrow(&mut self, cmt: &Place<'tcx>, _: ty::BorrowKind) {
++ if cmt.projections.is_empty() {
++ if let PlaceBase::Local(lid) = cmt.base {
++ self.set.remove(&lid);
++ }
++ }
++ }
++
++ fn mutate(&mut self, cmt: &Place<'tcx>) {
++ if cmt.projections.is_empty() {
++ let map = &self.cx.tcx.hir();
++ if is_argument(*map, cmt.hir_id) {
++ // Skip closure arguments
++ let parent_id = map.get_parent_node(cmt.hir_id);
++ if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
++ return;
++ }
++
++ if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
++ self.set.insert(cmt.hir_id);
++ }
++ return;
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
++ fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
++ // Large types need to be boxed to avoid stack overflows.
++ if ty.is_box() {
++ self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
++ } else {
++ false
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{
++ implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then,
++ type_is_unsafe_function,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for closures which just call another function where
++ /// the function can be called directly. `unsafe` functions or calls where types
++ /// get adjusted are ignored.
++ ///
++ /// **Why is this bad?** Needlessly creating a closure adds code for no benefit
++ /// and gives the optimizer more work.
++ ///
++ /// **Known problems:** If creating the closure inside the closure has a side-
++ /// effect then moving the closure creation out will change when that side-
++ /// effect runs.
++ /// See rust-lang/rust-clippy#1439 for more details.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// xs.map(|x| foo(x))
++ /// ```
++ /// where `foo(_)` is a plain function that takes the exact argument type of
++ /// `x`.
++ pub REDUNDANT_CLOSURE,
++ style,
++ "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for closures which only invoke a method on the closure
++ /// argument and can be replaced by referencing the method directly.
++ ///
++ /// **Why is this bad?** It's unnecessary to create the closure.
++ ///
++ /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002,
++ /// rust-lang/rust-clippy#3942
++ ///
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// Some('a').map(|s| s.to_uppercase());
++ /// ```
++ /// may be rewritten as
++ /// ```rust,ignore
++ /// Some('a').map(char::to_uppercase);
++ /// ```
++ pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++ pedantic,
++ "redundant closures for method calls"
++}
++
++declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaReduction {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ match expr.kind {
++ ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => {
++ for arg in args {
++ check_closure(cx, arg)
++ }
++ },
++ _ => (),
++ }
++ }
++}
++
++fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind {
++ let body = cx.tcx.hir().body(eid);
++ let ex = &body.value;
++
++ if_chain!(
++ if let ExprKind::Call(ref caller, ref args) = ex.kind;
++
++ if let ExprKind::Path(_) = caller.kind;
++
++ // Not the same number of arguments, there is no way the closure is the same as the function return;
++ if args.len() == decl.inputs.len();
++
++ // Are the expression or the arguments type-adjusted? Then we need the closure
++ if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
++
++ let fn_ty = cx.tables.expr_ty(caller);
++
++ if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
++
++ if !type_is_unsafe_function(cx, fn_ty);
++
++ if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
++
++ then {
++ span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| {
++ if let Some(snippet) = snippet_opt(cx, caller.span) {
++ diag.span_suggestion(
++ expr.span,
++ "remove closure as shown",
++ snippet,
++ Applicability::MachineApplicable,
++ );
++ }
++ });
++ }
++ );
++
++ if_chain!(
++ if let ExprKind::MethodCall(ref path, _, ref args) = ex.kind;
++
++ // Not the same number of arguments, there is no way the closure is the same as the function return;
++ if args.len() == decl.inputs.len();
++
++ // Are the expression or the arguments type-adjusted? Then we need the closure
++ if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
++
++ let method_def_id = cx.tables.type_dependent_def_id(ex.hir_id).unwrap();
++ if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
++
++ if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
++
++ if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
++
++ then {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++ expr.span,
++ "redundant closure found",
++ "remove closure as shown",
++ format!("{}::{}", name, path.ident.name),
++ Applicability::MachineApplicable,
++ );
++ }
++ );
++ }
++}
++
++/// Tries to determine the type for universal function call to be used instead of the closure
++fn get_ufcs_type_name(cx: &LateContext<'_, '_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
++ let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
++ let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id);
++
++ if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
++ if match_borrow_depth(expected_type_of_self, &actual_type_of_self)
++ && implements_trait(cx, actual_type_of_self, trait_id, &[])
++ {
++ return Some(cx.tcx.def_path_str(trait_id));
++ }
++ }
++
++ cx.tcx.impl_of_method(method_def_id).and_then(|_| {
++ //a type may implicitly implement other type's methods (e.g. Deref)
++ if match_types(expected_type_of_self, &actual_type_of_self) {
++ return Some(get_type_name(cx, &actual_type_of_self));
++ }
++ None
++ })
++}
++
++fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
++ match (&lhs.kind, &rhs.kind) {
++ (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
++ (l, r) => match (l, r) {
++ (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
++ (_, _) => true,
++ },
++ }
++}
++
++fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
++ match (&lhs.kind, &rhs.kind) {
++ (ty::Bool, ty::Bool)
++ | (ty::Char, ty::Char)
++ | (ty::Int(_), ty::Int(_))
++ | (ty::Uint(_), ty::Uint(_))
++ | (ty::Str, ty::Str) => true,
++ (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
++ (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
++ (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
++ (_, _) => false,
++ }
++}
++
++fn get_type_name(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> String {
++ match ty.kind {
++ ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
++ ty::Ref(_, r, _) => get_type_name(cx, &r),
++ _ => ty.to_string(),
++ }
++}
++
++fn compare_inputs(
++ closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
++ call_args: &mut dyn Iterator<Item = &Expr<'_>>,
++) -> bool {
++ for (closure_input, function_arg) in closure_inputs.zip(call_args) {
++ if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
++ // XXXManishearth Should I be checking the binding mode here?
++ if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind {
++ if p.segments.len() != 1 {
++ // If it's a proper path, it can't be a local variable
++ return false;
++ }
++ if p.segments[0].ident.name != ident.name {
++ // The two idents should be the same
++ return false;
++ }
++ } else {
++ return false;
++ }
++ } else {
++ return false;
++ }
++ }
++ true
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_parent_expr, span_lint, span_lint_and_note};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, 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;
++ /// let a = {
++ /// x = 1;
++ /// 1
++ /// } + x;
++ /// // Unclear whether a is 1 or 2.
++ /// ```
++ pub EVAL_ORDER_DEPENDENCE,
++ complexity,
++ "whether a variable read occurs before a write depends on sub-expression evaluation order"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for diverging calls that are not match arms or
++ /// statements.
++ ///
++ /// **Why is this bad?** It is often confusing to read. In addition, the
++ /// sub-expression evaluation order for Rust is not well documented.
++ ///
++ /// **Known problems:** Someone might want to use `some_bool || panic!()` as a
++ /// shorthand.
++ ///
++ /// **Example:**
++ /// ```rust,no_run
++ /// # fn b() -> bool { true }
++ /// # fn c() -> bool { true }
++ /// let a = b() || panic!() || c();
++ /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
++ /// let x = (a, b, c, panic!());
++ /// // can simply be replaced by `panic!()`
++ /// ```
++ pub DIVERGING_SUB_EXPRESSION,
++ complexity,
++ "whether an expression contains a diverging sub expression"
++}
++
++declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // Find a write to a local variable.
++ match expr.kind {
++ ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => {
++ if let ExprKind::Path(ref qpath) = lhs.kind {
++ if let QPath::Resolved(_, ref path) = *qpath {
++ if path.segments.len() == 1 {
++ if let def::Res::Local(var) = cx.tables.qpath_res(qpath, lhs.hir_id) {
++ 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<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++ match stmt.kind {
++ StmtKind::Local(ref local) => {
++ if let Local { init: Some(ref e), .. } = **local {
++ DivergenceVisitor { cx }.visit_expr(e);
++ }
++ },
++ StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
++ StmtKind::Item(..) => {},
++ }
++ }
++}
++
++struct DivergenceVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
++ fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
++ match e.kind {
++ ExprKind::Closure(..) => {},
++ ExprKind::Match(ref 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(ref func, _) => {
++ let typ = self.cx.tables.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.tables;
++ 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(ref expr) | StmtKind::Semi(ref 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(ref local) => local
++ .init
++ .as_ref()
++ .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
++ _ => StopEarly::KeepGoing,
++ }
++}
++
++/// A visitor that looks for reads from a variable.
++struct ReadVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, '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;
++ }
++
++ match expr.kind {
++ ExprKind::Path(ref qpath) => {
++ if_chain! {
++ if let QPath::Resolved(None, ref path) = *qpath;
++ if path.segments.len() == 1;
++ if let def::Res::Local(local_id) = self.cx.tables.qpath_res(qpath, expr.hir_id);
++ if local_id == self.var;
++ // Check that this is a read, not a write.
++ if !is_in_assignment_position(self.cx, expr);
++ then {
++ span_lint_and_note(
++ self.cx,
++ EVAL_ORDER_DEPENDENCE,
++ expr.span,
++ "unsequenced read of a variable",
++ Some(self.write_expr.span),
++ "whether read occurs before this write depends on evaluation order"
++ );
++ }
++ }
++ }
++ // 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(ref lhs, ..) = parent.kind {
++ return lhs.hir_id == expr.hir_id;
++ }
++ }
++ false
++}
--- /dev/null
--- /dev/null
++use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
++use rustc_ast::ast::{AssocItemKind, Extern, FnSig, Item, ItemKind, Ty, TyKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++
++use std::convert::TryInto;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for excessive
++ /// use of bools in structs.
++ ///
++ /// **Why is this bad?** Excessive bools in a struct
++ /// is often a sign that it's used as a state machine,
++ /// which is much better implemented as an enum.
++ /// If it's not the case, excessive bools usually benefit
++ /// from refactoring into two-variant enums for better
++ /// readability and API.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust
++ /// struct S {
++ /// is_pending: bool,
++ /// is_processing: bool,
++ /// is_finished: bool,
++ /// }
++ /// ```
++ ///
++ /// Good:
++ /// ```rust
++ /// enum S {
++ /// Pending,
++ /// Processing,
++ /// Finished,
++ /// }
++ /// ```
++ pub STRUCT_EXCESSIVE_BOOLS,
++ pedantic,
++ "using too many bools in a struct"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for excessive use of
++ /// bools in function definitions.
++ ///
++ /// **Why is this bad?** Calls to such functions
++ /// are confusing and error prone, because it's
++ /// hard to remember argument order and you have
++ /// no type system support to back you up. Using
++ /// two-variant enums instead of bools often makes
++ /// API easier to use.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// fn f(is_round: bool, is_hot: bool) { ... }
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// enum Shape {
++ /// Round,
++ /// Spiky,
++ /// }
++ ///
++ /// enum Temperature {
++ /// Hot,
++ /// IceCold,
++ /// }
++ ///
++ /// fn f(shape: Shape, temperature: Temperature) { ... }
++ /// ```
++ pub FN_PARAMS_EXCESSIVE_BOOLS,
++ pedantic,
++ "using too many bools in function parameters"
++}
++
++pub struct ExcessiveBools {
++ max_struct_bools: u64,
++ max_fn_params_bools: u64,
++}
++
++impl ExcessiveBools {
++ #[must_use]
++ pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
++ Self {
++ max_struct_bools,
++ max_fn_params_bools,
++ }
++ }
++
++ fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
++ match fn_sig.header.ext {
++ Extern::Implicit | Extern::Explicit(_) => return,
++ Extern::None => (),
++ }
++
++ let fn_sig_bools = fn_sig
++ .decl
++ .inputs
++ .iter()
++ .filter(|param| is_bool_ty(¶m.ty))
++ .count()
++ .try_into()
++ .unwrap();
++ if self.max_fn_params_bools < fn_sig_bools {
++ span_lint_and_help(
++ cx,
++ FN_PARAMS_EXCESSIVE_BOOLS,
++ span,
++ &format!("more than {} bools in function parameters", self.max_fn_params_bools),
++ None,
++ "consider refactoring bools into two-variant enums",
++ );
++ }
++ }
++}
++
++impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
++
++fn is_bool_ty(ty: &Ty) -> bool {
++ if let TyKind::Path(None, path) = &ty.kind {
++ return match_path_ast(path, &["bool"]);
++ }
++ false
++}
++
++impl EarlyLintPass for ExcessiveBools {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ if in_macro(item.span) {
++ return;
++ }
++ match &item.kind {
++ ItemKind::Struct(variant_data, _) => {
++ if attr_by_name(&item.attrs, "repr").is_some() {
++ return;
++ }
++
++ let struct_bools = variant_data
++ .fields()
++ .iter()
++ .filter(|field| is_bool_ty(&field.ty))
++ .count()
++ .try_into()
++ .unwrap();
++ if self.max_struct_bools < struct_bools {
++ span_lint_and_help(
++ cx,
++ STRUCT_EXCESSIVE_BOOLS,
++ item.span,
++ &format!("more than {} bools in a struct", self.max_struct_bools),
++ None,
++ "consider using a state machine or refactoring bools into two-variant enums",
++ );
++ }
++ },
++ ItemKind::Impl {
++ of_trait: None, items, ..
++ }
++ | ItemKind::Trait(_, _, _, _, items) => {
++ for item in items {
++ if let AssocItemKind::Fn(_, fn_sig, _, _) = &item.kind {
++ self.check_fn_sig(cx, fn_sig, item.span);
++ }
++ }
++ },
++ ItemKind::Fn(_, fn_sig, _, _) => self.check_fn_sig(cx, fn_sig, item.span),
++ _ => (),
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** `exit()` terminates the program and doesn't provide a
++ /// stack trace.
++ ///
++ /// **Why is this bad?** Ideally a program is terminated by finishing
++ /// the main function.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// std::process::exit(0)
++ /// ```
++ pub EXIT,
++ restriction,
++ "`std::process::exit` is called, terminating the program"
++}
++
++declare_lint_pass!(Exit => [EXIT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref path_expr, ref _args) = e.kind;
++ if let ExprKind::Path(ref path) = path_expr.kind;
++ if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::EXIT);
++ then {
++ let parent = cx.tcx.hir().get_parent_item(e.hir_id);
++ if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) {
++ // If the next item up is a function we check if it is an entry point
++ // and only then emit a linter warning
++ let def_id = cx.tcx.hir().local_def_id(parent);
++ if !is_entrypoint_fn(cx, def_id.to_def_id()) {
++ span_lint(cx, EXIT, e.span, "usage of `process::exit`");
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
++ /// replaced with `(e)print!()` / `(e)println!()`
++ ///
++ /// **Why is this bad?** Using `(e)println! is clearer and more concise
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::io::Write;
++ /// # let bar = "furchtbar";
++ /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
++ /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
++ /// ```
++ pub EXPLICIT_WRITE,
++ complexity,
++ "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
++}
++
++declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitWrite {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ // match call to unwrap
++ if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.kind;
++ if unwrap_fun.ident.name == sym!(unwrap);
++ // match call to write_fmt
++ if !unwrap_args.is_empty();
++ if let ExprKind::MethodCall(ref write_fun, _, write_args) =
++ unwrap_args[0].kind;
++ if write_fun.ident.name == sym!(write_fmt);
++ // match calls to std::io::stdout() / std::io::stderr ()
++ if !write_args.is_empty();
++ if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
++ Some("stdout")
++ } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
++ Some("stderr")
++ } else {
++ None
++ };
++ then {
++ let write_span = unwrap_args[0].span;
++ let calling_macro =
++ // ordering is important here, since `writeln!` uses `write!` internally
++ if is_expn_of(write_span, "writeln").is_some() {
++ Some("writeln")
++ } else if is_expn_of(write_span, "write").is_some() {
++ Some("write")
++ } else {
++ None
++ };
++ let prefix = if dest_name == "stderr" {
++ "e"
++ } else {
++ ""
++ };
++
++ // We need to remove the last trailing newline from the string because the
++ // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
++ // used.
++ if let Some(mut write_output) = write_output_string(write_args) {
++ if write_output.ends_with('\n') {
++ write_output.pop();
++ }
++
++ if let Some(macro_name) = calling_macro {
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_WRITE,
++ expr.span,
++ &format!(
++ "use of `{}!({}(), ...).unwrap()`",
++ macro_name,
++ dest_name
++ ),
++ "try this",
++ format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
++ Applicability::MachineApplicable
++ );
++ } else {
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_WRITE,
++ expr.span,
++ &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
++ "try this",
++ format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
++ Applicability::MachineApplicable
++ );
++ }
++ } else {
++ // We don't have a proper suggestion
++ if let Some(macro_name) = calling_macro {
++ span_lint(
++ cx,
++ EXPLICIT_WRITE,
++ expr.span,
++ &format!(
++ "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
++ macro_name,
++ dest_name,
++ prefix,
++ macro_name.replace("write", "print")
++ )
++ );
++ } else {
++ span_lint(
++ cx,
++ EXPLICIT_WRITE,
++ expr.span,
++ &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
++ );
++ }
++ }
++
++ }
++ }
++ }
++}
++
++// Extract the output string from the given `write_args`.
++fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
++ if_chain! {
++ // Obtain the string that should be printed
++ if write_args.len() > 1;
++ if let ExprKind::Call(_, ref output_args) = write_args[1].kind;
++ if !output_args.is_empty();
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind;
++ if let ExprKind::Array(ref string_exprs) = output_string_expr.kind;
++ // we only want to provide an automatic suggestion for simple (non-format) strings
++ if string_exprs.len() == 1;
++ if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
++ if let LitKind::Str(ref write_output, _) = lit.node;
++ then {
++ return Some(write_output.to_string())
++ }
++ }
++ None
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT};
++use crate::utils::{
++ is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
++ ///
++ /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// struct Foo(i32);
++ /// impl From<String> for Foo {
++ /// fn from(s: String) -> Self {
++ /// Foo(s.parse().unwrap())
++ /// }
++ /// }
++ /// ```
++ pub FALLIBLE_IMPL_FROM,
++ nursery,
++ "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
++}
++
++declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ // check for `impl From<???> for ..`
++ let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ if_chain! {
++ if let hir::ItemKind::Impl{ items: impl_items, .. } = item.kind;
++ if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
++ if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT);
++ then {
++ lint_impl_body(cx, item.span, impl_items);
++ }
++ }
++ }
++}
++
++fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef<'_>]) {
++ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
++ use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
++
++ struct FindPanicUnwrap<'a, 'tcx> {
++ lcx: &'a LateContext<'a, 'tcx>,
++ tables: &'tcx ty::TypeckTables<'tcx>,
++ result: Vec<Span>,
++ }
++
++ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ // check for `begin_panic`
++ if_chain! {
++ if let ExprKind::Call(ref func_expr, _) = expr.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind;
++ if let Some(path_def_id) = path.res.opt_def_id();
++ if match_def_path(self.lcx, path_def_id, &BEGIN_PANIC) ||
++ match_def_path(self.lcx, path_def_id, &BEGIN_PANIC_FMT);
++ if is_expn_of(expr.span, "unreachable").is_none();
++ then {
++ self.result.push(expr.span);
++ }
++ }
++
++ // check for `unwrap`
++ if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
++ let reciever_ty = walk_ptrs_ty(self.tables.expr_ty(&arglists[0][0]));
++ if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
++ || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
++ {
++ 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
++ }
++ }
++
++ for impl_item in impl_items {
++ if_chain! {
++ if impl_item.ident.name == sym!(from);
++ if let ImplItemKind::Fn(_, body_id) =
++ cx.tcx.hir().impl_item(impl_item.id).kind;
++ then {
++ // check the body for `begin_panic` or `unwrap`
++ let body = cx.tcx.hir().body(body_id);
++ let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.id.hir_id);
++ let mut fpu = FindPanicUnwrap {
++ lcx: cx,
++ tables: cx.tcx.typeck_tables_of(impl_item_def_id),
++ result: Vec::new(),
++ };
++ fpu.visit_expr(&body.value);
++
++ // if we've found one, lint
++ if !fpu.result.is_empty() {
++ span_lint_and_then(
++ cx,
++ FALLIBLE_IMPL_FROM,
++ impl_span,
++ "consider implementing `TryFrom` instead",
++ move |diag| {
++ diag.help(
++ "`From` is intended for infallible conversions only. \
++ Use `TryFrom` if there's a possibility for the conversion to fail.");
++ diag.span_note(fpu.result, "potential failure(s)");
++ });
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{numeric_literal, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for float literals with a precision greater
++ /// than that supported by the underlying type.
++ ///
++ /// **Why is this bad?** Rust will truncate the literal silently.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// let v: f32 = 0.123_456_789_9;
++ /// println!("{}", v); // 0.123_456_789
++ ///
++ /// // Good
++ /// let v: f64 = 0.123_456_789_9;
++ /// println!("{}", v); // 0.123_456_789_9
++ /// ```
++ pub EXCESSIVE_PRECISION,
++ style,
++ "excessive precision for float literal"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for whole number float literals that
++ /// cannot be represented as the underlying type without loss.
++ ///
++ /// **Why is this bad?** Rust will silently lose precision during
++ /// conversion to a float.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// let _: f32 = 16_777_217.0; // 16_777_216.0
++ ///
++ /// // Good
++ /// let _: f32 = 16_777_216.0;
++ /// let _: f64 = 16_777_217.0;
++ /// ```
++ pub LOSSY_FLOAT_LITERAL,
++ restriction,
++ "lossy whole number float literals"
++}
++
++declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if_chain! {
++ let ty = cx.tables.expr_ty(expr);
++ if let ty::Float(fty) = ty.kind;
++ if let hir::ExprKind::Lit(ref lit) = expr.kind;
++ if let LitKind::Float(sym, lit_float_ty) = lit.node;
++ then {
++ let sym_str = sym.as_str();
++ let formatter = FloatFormat::new(&sym_str);
++ // Try to bail out if the float is for sure fine.
++ // If its within the 2 decimal digits of being out of precision we
++ // check if the parsed representation is the same as the string
++ // since we'll need the truncated string anyway.
++ let digits = count_digits(&sym_str);
++ let max = max_digits(fty);
++ let type_suffix = match lit_float_ty {
++ LitFloatType::Suffixed(FloatTy::F32) => Some("f32"),
++ LitFloatType::Suffixed(FloatTy::F64) => Some("f64"),
++ _ => None
++ };
++ let (is_whole, mut float_str) = match fty {
++ FloatTy::F32 => {
++ let value = sym_str.parse::<f32>().unwrap();
++
++ (value.fract() == 0.0, formatter.format(value))
++ },
++ FloatTy::F64 => {
++ let value = sym_str.parse::<f64>().unwrap();
++
++ (value.fract() == 0.0, formatter.format(value))
++ },
++ };
++
++ if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
++ // Normalize the literal by stripping the fractional portion
++ if sym_str.split('.').next().unwrap() != float_str {
++ // If the type suffix is missing the suggestion would be
++ // incorrectly interpreted as an integer so adding a `.0`
++ // suffix to prevent that.
++ if type_suffix.is_none() {
++ float_str.push_str(".0");
++ }
++
++ span_lint_and_sugg(
++ cx,
++ LOSSY_FLOAT_LITERAL,
++ expr.span,
++ "literal cannot be represented as the underlying type without loss of precision",
++ "consider changing the type or replacing it with",
++ numeric_literal::format(&float_str, type_suffix, true),
++ Applicability::MachineApplicable,
++ );
++ }
++ } else if digits > max as usize && sym_str != float_str {
++ span_lint_and_sugg(
++ cx,
++ EXCESSIVE_PRECISION,
++ expr.span,
++ "float has excessive precision",
++ "consider changing the type or truncating it to",
++ numeric_literal::format(&float_str, type_suffix, true),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++ }
++}
++
++#[must_use]
++fn max_digits(fty: FloatTy) -> u32 {
++ match fty {
++ FloatTy::F32 => f32::DIGITS,
++ FloatTy::F64 => f64::DIGITS,
++ }
++}
++
++/// Counts the digits excluding leading zeros
++#[must_use]
++fn count_digits(s: &str) -> usize {
++ // Note that s does not contain the f32/64 suffix, and underscores have been stripped
++ s.chars()
++ .filter(|c| *c != '-' && *c != '.')
++ .take_while(|c| *c != 'e' && *c != 'E')
++ .fold(0, |count, c| {
++ // leading zeros
++ if c == '0' && count == 0 {
++ count
++ } else {
++ count + 1
++ }
++ })
++}
++
++enum FloatFormat {
++ LowerExp,
++ UpperExp,
++ Normal,
++}
++impl FloatFormat {
++ #[must_use]
++ fn new(s: &str) -> Self {
++ s.chars()
++ .find_map(|x| match x {
++ 'e' => Some(Self::LowerExp),
++ 'E' => Some(Self::UpperExp),
++ _ => None,
++ })
++ .unwrap_or(Self::Normal)
++ }
++ fn format<T>(&self, f: T) -> String
++ where
++ T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
++ {
++ match self {
++ Self::LowerExp => format!("{:e}", f),
++ Self::UpperExp => format!("{:E}", f),
++ Self::Normal => format!("{}", f),
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{
++ constant, constant_simple, Constant,
++ Constant::{F32, F64},
++};
++use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use rustc_ast::ast;
++use std::f32::consts as f32_consts;
++use std::f64::consts as f64_consts;
++use sugg::Sugg;
++
++declare_clippy_lint! {
++ /// **What it does:** Looks for floating-point expressions that
++ /// can be expressed using built-in methods to improve accuracy
++ /// at the cost of performance.
++ ///
++ /// **Why is this bad?** Negatively impacts accuracy.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ ///
++ /// let a = 3f32;
++ /// let _ = a.powf(1.0 / 3.0);
++ /// let _ = (1.0 + a).ln();
++ /// let _ = a.exp() - 1.0;
++ /// ```
++ ///
++ /// is better expressed as
++ ///
++ /// ```rust
++ ///
++ /// let a = 3f32;
++ /// let _ = a.cbrt();
++ /// let _ = a.ln_1p();
++ /// let _ = a.exp_m1();
++ /// ```
++ pub IMPRECISE_FLOPS,
++ nursery,
++ "usage of imprecise floating point operations"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Looks for floating-point expressions that
++ /// can be expressed using built-in methods to improve both
++ /// accuracy and performance.
++ ///
++ /// **Why is this bad?** Negatively impacts accuracy and performance.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// use std::f32::consts::E;
++ ///
++ /// let a = 3f32;
++ /// let _ = (2f32).powf(a);
++ /// let _ = E.powf(a);
++ /// let _ = a.powf(1.0 / 2.0);
++ /// let _ = a.log(2.0);
++ /// let _ = a.log(10.0);
++ /// let _ = a.log(E);
++ /// let _ = a.powf(2.0);
++ /// let _ = a * 2.0 + 4.0;
++ /// let _ = if a < 0.0 {
++ /// -a
++ /// } else {
++ /// a
++ /// };
++ /// let _ = if a < 0.0 {
++ /// a
++ /// } else {
++ /// -a
++ /// };
++ /// ```
++ ///
++ /// is better expressed as
++ ///
++ /// ```rust
++ /// use std::f32::consts::E;
++ ///
++ /// let a = 3f32;
++ /// let _ = a.exp2();
++ /// let _ = a.exp();
++ /// let _ = a.sqrt();
++ /// let _ = a.log2();
++ /// let _ = a.log10();
++ /// let _ = a.ln();
++ /// let _ = a.powi(2);
++ /// let _ = a.mul_add(2.0, 4.0);
++ /// let _ = a.abs();
++ /// let _ = -a.abs();
++ /// ```
++ pub SUBOPTIMAL_FLOPS,
++ nursery,
++ "usage of sub-optimal floating point operations"
++}
++
++declare_lint_pass!(FloatingPointArithmetic => [
++ IMPRECISE_FLOPS,
++ SUBOPTIMAL_FLOPS
++]);
++
++// Returns the specialized log method for a given base if base is constant
++// and is one of 2, 10 and e
++fn get_specialized_log_method(cx: &LateContext<'_, '_>, base: &Expr<'_>) -> Option<&'static str> {
++ if let Some((value, _)) = constant(cx, cx.tables, base) {
++ if F32(2.0) == value || F64(2.0) == value {
++ return Some("log2");
++ } else if F32(10.0) == value || F64(10.0) == value {
++ return Some("log10");
++ } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
++ return Some("ln");
++ }
++ }
++
++ None
++}
++
++// Adds type suffixes and parenthesis to method receivers if necessary
++fn prepare_receiver_sugg<'a>(cx: &LateContext<'_, '_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
++ let mut suggestion = Sugg::hir(cx, expr, "..");
++
++ if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
++ expr = &inner_expr;
++ }
++
++ if_chain! {
++ // if the expression is a float literal and it is unsuffixed then
++ // add a suffix so the suggestion is valid and unambiguous
++ if let ty::Float(float_ty) = cx.tables.expr_ty(expr).kind;
++ if let ExprKind::Lit(lit) = &expr.kind;
++ if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
++ then {
++ let op = format!(
++ "{}{}{}",
++ suggestion,
++ // Check for float literals without numbers following the decimal
++ // separator such as `2.` and adds a trailing zero
++ if sym.as_str().ends_with('.') {
++ "0"
++ } else {
++ ""
++ },
++ float_ty.name_str()
++ ).into();
++
++ suggestion = match suggestion {
++ Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
++ _ => Sugg::NonParen(op)
++ };
++ }
++ }
++
++ suggestion.maybe_par()
++}
++
++fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++ if let Some(method) = get_specialized_log_method(cx, &args[1]) {
++ span_lint_and_sugg(
++ cx,
++ SUBOPTIMAL_FLOPS,
++ expr.span,
++ "logarithm for bases 2, 10 and e can be computed more accurately",
++ "consider using",
++ format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
++// suggest usage of `(x + (y - 1)).ln_1p()` instead
++fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++ if let ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ lhs,
++ rhs,
++ ) = &args[0].kind
++ {
++ let recv = match (constant(cx, cx.tables, lhs), constant(cx, cx.tables, rhs)) {
++ (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
++ (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
++ _ => return,
++ };
++
++ span_lint_and_sugg(
++ cx,
++ IMPRECISE_FLOPS,
++ expr.span,
++ "ln(1 + x) can be computed more accurately",
++ "consider using",
++ format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++// Returns an integer if the float constant is a whole number and it can be
++// converted to an integer without loss of precision. For now we only check
++// ranges [-16777215, 16777216) for type f32 as whole number floats outside
++// this range are lossy and ambiguous.
++#[allow(clippy::cast_possible_truncation)]
++fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
++ match value {
++ F32(num) if num.fract() == 0.0 => {
++ if (-16_777_215.0..16_777_216.0).contains(num) {
++ Some(num.round() as i32)
++ } else {
++ None
++ }
++ },
++ F64(num) if num.fract() == 0.0 => {
++ if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
++ Some(num.round() as i32)
++ } else {
++ None
++ }
++ },
++ _ => None,
++ }
++}
++
++fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++ // Check receiver
++ if let Some((value, _)) = constant(cx, cx.tables, &args[0]) {
++ let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
++ "exp"
++ } else if F32(2.0) == value || F64(2.0) == value {
++ "exp2"
++ } else {
++ return;
++ };
++
++ span_lint_and_sugg(
++ cx,
++ SUBOPTIMAL_FLOPS,
++ expr.span,
++ "exponent for bases 2 and e can be computed more accurately",
++ "consider using",
++ format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
++ Applicability::MachineApplicable,
++ );
++ }
++
++ // Check argument
++ if let Some((value, _)) = constant(cx, cx.tables, &args[1]) {
++ let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
++ (
++ SUBOPTIMAL_FLOPS,
++ "square-root of a number can be computed more efficiently and accurately",
++ format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
++ )
++ } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
++ (
++ IMPRECISE_FLOPS,
++ "cube-root of a number can be computed more accurately",
++ format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
++ )
++ } else if let Some(exponent) = get_integer_from_float_constant(&value) {
++ (
++ SUBOPTIMAL_FLOPS,
++ "exponentiation with integer powers can be computed more efficiently",
++ format!(
++ "{}.powi({})",
++ Sugg::hir(cx, &args[0], ".."),
++ numeric_literal::format(&exponent.to_string(), None, false)
++ ),
++ )
++ } else {
++ return;
++ };
++
++ span_lint_and_sugg(
++ cx,
++ lint,
++ expr.span,
++ help,
++ "consider using",
++ suggestion,
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++// TODO: Lint expressions of the form `x.exp() - y` where y > 1
++// and suggest usage of `x.exp_m1() - (y - 1)` instead
++fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind;
++ if cx.tables.expr_ty(lhs).is_floating_point();
++ if let Some((value, _)) = constant(cx, cx.tables, rhs);
++ if F32(1.0) == value || F64(1.0) == value;
++ if let ExprKind::MethodCall(ref path, _, ref method_args) = lhs.kind;
++ if cx.tables.expr_ty(&method_args[0]).is_floating_point();
++ if path.ident.name.as_str() == "exp";
++ then {
++ span_lint_and_sugg(
++ cx,
++ IMPRECISE_FLOPS,
++ expr.span,
++ "(e.pow(x) - 1) can be computed more accurately",
++ "consider using",
++ format!(
++ "{}.exp_m1()",
++ Sugg::hir(cx, &method_args[0], "..")
++ ),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++}
++
++fn is_float_mul_expr<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
++ if_chain! {
++ if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind;
++ if cx.tables.expr_ty(lhs).is_floating_point();
++ if cx.tables.expr_ty(rhs).is_floating_point();
++ then {
++ return Some((lhs, rhs));
++ }
++ }
++
++ None
++}
++
++// TODO: Fix rust-lang/rust-clippy#4735
++fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if let ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ lhs,
++ rhs,
++ ) = &expr.kind
++ {
++ let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
++ (inner_lhs, inner_rhs, rhs)
++ } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
++ (inner_lhs, inner_rhs, lhs)
++ } else {
++ return;
++ };
++
++ span_lint_and_sugg(
++ cx,
++ SUBOPTIMAL_FLOPS,
++ expr.span,
++ "multiply and add expressions can be calculated more efficiently and accurately",
++ "consider using",
++ format!(
++ "{}.mul_add({}, {})",
++ prepare_receiver_sugg(cx, recv),
++ Sugg::hir(cx, arg1, ".."),
++ Sugg::hir(cx, arg2, ".."),
++ ),
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++/// Returns true iff expr is an expression which tests whether or not
++/// test is positive or an expression which tests whether or not test
++/// is nonnegative.
++/// Used for check-custom-abs function below
++fn is_testing_positive(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
++ if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
++ match op {
++ BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test),
++ BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test),
++ _ => false,
++ }
++ } else {
++ false
++ }
++}
++
++/// See [`is_testing_positive`]
++fn is_testing_negative(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
++ if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
++ match op {
++ BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test),
++ BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test),
++ _ => false,
++ }
++ } else {
++ false
++ }
++}
++
++fn are_exprs_equal(cx: &LateContext<'_, '_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
++ SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
++}
++
++/// Returns true iff expr is some zero literal
++fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ match constant_simple(cx, cx.tables, expr) {
++ Some(Constant::Int(i)) => i == 0,
++ Some(Constant::F32(f)) => f == 0.0,
++ Some(Constant::F64(f)) => f == 0.0,
++ _ => false,
++ }
++}
++
++/// If the two expressions are negations of each other, then it returns
++/// a tuple, in which the first element is true iff expr1 is the
++/// positive expressions, and the second element is the positive
++/// one of the two expressions
++/// If the two expressions are not negations of each other, then it
++/// returns None.
++fn are_negated<'a>(cx: &LateContext<'_, '_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
++ if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind {
++ if are_exprs_equal(cx, expr1_negated, expr2) {
++ return Some((false, expr2));
++ }
++ }
++ if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind {
++ if are_exprs_equal(cx, expr1, expr2_negated) {
++ return Some((true, expr1));
++ }
++ }
++ None
++}
++
++fn check_custom_abs(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let Some((cond, body, Some(else_body))) = higher::if_block(&expr);
++ if let ExprKind::Block(block, _) = body.kind;
++ if block.stmts.is_empty();
++ if let Some(if_body_expr) = block.expr;
++ if let ExprKind::Block(else_block, _) = else_body.kind;
++ if else_block.stmts.is_empty();
++ if let Some(else_body_expr) = else_block.expr;
++ if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
++ then {
++ let positive_abs_sugg = (
++ "manual implementation of `abs` method",
++ format!("{}.abs()", Sugg::hir(cx, body, "..")),
++ );
++ let negative_abs_sugg = (
++ "manual implementation of negation of `abs` method",
++ format!("-{}.abs()", Sugg::hir(cx, body, "..")),
++ );
++ let sugg = if is_testing_positive(cx, cond, body) {
++ if if_expr_positive {
++ positive_abs_sugg
++ } else {
++ negative_abs_sugg
++ }
++ } else if is_testing_negative(cx, cond, body) {
++ if if_expr_positive {
++ negative_abs_sugg
++ } else {
++ positive_abs_sugg
++ }
++ } else {
++ return;
++ };
++ span_lint_and_sugg(
++ cx,
++ SUBOPTIMAL_FLOPS,
++ expr.span,
++ sugg.0,
++ "try",
++ sugg.1,
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::MethodCall(ref path, _, args) = &expr.kind {
++ let recv_ty = cx.tables.expr_ty(&args[0]);
++
++ if recv_ty.is_floating_point() {
++ match &*path.ident.name.as_str() {
++ "ln" => check_ln1p(cx, expr, args),
++ "log" => check_log_base(cx, expr, args),
++ "powf" => check_powf(cx, expr, args),
++ _ => {},
++ }
++ }
++ } else {
++ check_expm1(cx, expr);
++ check_mul_add(cx, expr);
++ check_custom_abs(cx, expr);
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths;
++use crate::utils::{
++ is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
++ span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the use of `format!("string literal with no
++ /// argument")` and `format!("{}", foo)` where `foo` is a string.
++ ///
++ /// **Why is this bad?** There is no point of doing that. `format!("foo")` can
++ /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
++ /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
++ /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
++ /// if `foo: &str`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Examples:**
++ /// ```rust
++ /// # let foo = "foo";
++ /// format!("foo");
++ /// format!("{}", foo);
++ /// ```
++ pub USELESS_FORMAT,
++ complexity,
++ "useless use of `format!`"
++}
++
++declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessFormat {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ let span = match is_expn_of(expr.span, "format") {
++ Some(s) if !s.from_expansion() => s,
++ _ => return,
++ };
++
++ // Operate on the only argument of `alloc::fmt::format`.
++ if let Some(sugg) = on_new_v1(cx, expr) {
++ span_useless_format(cx, span, "consider using `.to_string()`", sugg);
++ } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
++ span_useless_format(cx, span, "consider using `.to_string()`", sugg);
++ }
++ }
++}
++
++fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
++ let to_replace = span.source_callsite();
++
++ // The callsite span contains the statement semicolon for some reason.
++ let snippet = snippet(cx, to_replace, "..");
++ if snippet.ends_with(';') {
++ sugg.push(';');
++ }
++
++ span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
++ diag.span_suggestion(
++ to_replace,
++ help,
++ sugg,
++ Applicability::MachineApplicable, // snippet
++ );
++ });
++}
++
++fn on_argumentv1_new<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++ arms: &'tcx [Arm<'_>],
++) -> Option<String> {
++ if_chain! {
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind;
++ if let ExprKind::Array(ref elems) = arms[0].body.kind;
++ if elems.len() == 1;
++ if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
++ // matches `core::fmt::Display::fmt`
++ if args.len() == 2;
++ if let ExprKind::Path(ref qpath) = args[1].kind;
++ if let Some(did) = cx.tables.qpath_res(qpath, args[1].hir_id).opt_def_id();
++ if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
++ // check `(arg0,)` in match block
++ if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
++ if pats.len() == 1;
++ then {
++ let ty = walk_ptrs_ty(cx.tables.pat_ty(&pats[0]));
++ if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++ return None;
++ }
++ if let ExprKind::Lit(ref lit) = format_args.kind {
++ if let LitKind::Str(ref s, _) = lit.node {
++ return Some(format!("{:?}.to_string()", s.as_str()));
++ }
++ } else {
++ let snip = snippet(cx, format_args.span, "<arg>");
++ if let ExprKind::MethodCall(ref path, _, _) = format_args.kind {
++ if path.ident.name == sym!(to_string) {
++ return Some(format!("{}", snip));
++ }
++ } else if let ExprKind::Binary(..) = format_args.kind {
++ return Some(format!("{}", snip));
++ }
++ return Some(format!("{}.to_string()", snip));
++ }
++ }
++ }
++ None
++}
++
++fn on_new_v1<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
++ if_chain! {
++ if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
++ if args.len() == 2;
++ // Argument 1 in `new_v1()`
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind;
++ if let ExprKind::Array(ref pieces) = arr.kind;
++ if pieces.len() == 1;
++ if let ExprKind::Lit(ref lit) = pieces[0].kind;
++ if let LitKind::Str(ref s, _) = lit.node;
++ // Argument 2 in `new_v1()`
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind;
++ if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind;
++ if arms.len() == 1;
++ if let ExprKind::Tup(ref tup) = matchee.kind;
++ then {
++ // `format!("foo")` expansion contains `match () { () => [], }`
++ if tup.is_empty() {
++ return Some(format!("{:?}.to_string()", s.as_str()));
++ } else if s.as_str().is_empty() {
++ return on_argumentv1_new(cx, &tup[0], arms);
++ }
++ }
++ }
++ None
++}
++
++fn on_new_v1_fmt<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
++ if_chain! {
++ if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
++ if args.len() == 3;
++ if check_unformatted(&args[2]);
++ // Argument 1 in `new_v1_formatted()`
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind;
++ if let ExprKind::Array(ref pieces) = arr.kind;
++ if pieces.len() == 1;
++ if let ExprKind::Lit(ref lit) = pieces[0].kind;
++ if let LitKind::Str(..) = lit.node;
++ // Argument 2 in `new_v1_formatted()`
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind;
++ if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind;
++ if arms.len() == 1;
++ if let ExprKind::Tup(ref tup) = matchee.kind;
++ then {
++ return on_argumentv1_new(cx, &tup[0], arms);
++ }
++ }
++ None
++}
++
++/// Checks if the expression matches
++/// ```rust,ignore
++/// &[_ {
++/// format: _ {
++/// width: _::Implied,
++/// precision: _::Implied,
++/// ...
++/// },
++/// ...,
++/// }]
++/// ```
++fn check_unformatted(expr: &Expr<'_>) -> bool {
++ if_chain! {
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind;
++ if let ExprKind::Array(ref exprs) = expr.kind;
++ if exprs.len() == 1;
++ // struct `core::fmt::rt::v1::Argument`
++ if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind;
++ if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format));
++ // struct `core::fmt::rt::v1::FormatSpec`
++ if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind;
++ if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision));
++ if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
++ if last_path_segment(precision_path).ident.name == sym!(Implied);
++ if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width));
++ if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
++ if last_path_segment(width_qpath).ident.name == sym!(Implied);
++ then {
++ return true;
++ }
++ }
++
++ false
++}
--- /dev/null
--- /dev/null
++use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note};
++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 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 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,
++ style,
++ "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) {
++ match (&w[0].kind, &w[1].kind) {
++ (&StmtKind::Expr(ref first), &StmtKind::Expr(ref second))
++ | (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => {
++ 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(else_pos) = else_snippet.find("else");
++ if else_snippet[else_pos..].contains('\n');
++ let else_desc = if is_if(else_) { "if" } else { "{..}" };
++
++ then {
++ 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 {
++ if let ExprKind::Block(..) = expr.kind {
++ true
++ } else {
++ false
++ }
++}
++
++/// Check if the expression is an `if` or `if let`
++fn is_if(expr: &Expr) -> bool {
++ if let ExprKind::If(..) = expr.kind {
++ true
++ } else {
++ false
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path,
++ must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then,
++ trait_ref_of_method, type_is_unsafe_function,
++};
++use rustc_ast::ast::Attribute;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit;
++use rustc_hir::{def::Res, def_id::DefId};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_target::spec::abi::Abi;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for functions with too many parameters.
++ ///
++ /// **Why is this bad?** Functions with lots of parameters are considered bad
++ /// style and reduce readability (“what does the 5th parameter mean?”). Consider
++ /// grouping some parameters into a new type.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # struct Color;
++ /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
++ /// // ..
++ /// }
++ /// ```
++ pub TOO_MANY_ARGUMENTS,
++ complexity,
++ "functions with too many arguments"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for functions with a large amount of lines.
++ ///
++ /// **Why is this bad?** Functions with a lot of lines are harder to understand
++ /// due to having to look at a larger amount of code to understand what the
++ /// function is doing. Consider splitting the body of the function into
++ /// multiple functions.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ``` rust
++ /// fn im_too_long() {
++ /// println!("");
++ /// // ... 100 more LoC
++ /// println!("");
++ /// }
++ /// ```
++ pub TOO_MANY_LINES,
++ pedantic,
++ "functions with too many lines"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for public functions that dereference raw pointer
++ /// arguments but are not marked unsafe.
++ ///
++ /// **Why is this bad?** The function should probably be marked `unsafe`, since
++ /// for an arbitrary raw pointer, there is no way of telling for sure if it is
++ /// valid.
++ ///
++ /// **Known problems:**
++ ///
++ /// * It does not check functions recursively so if the pointer is passed to a
++ /// private non-`unsafe` function which does the dereferencing, the lint won't
++ /// trigger.
++ /// * It only checks for arguments whose type are raw pointers, not raw pointers
++ /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
++ /// `some_argument.get_raw_ptr()`).
++ ///
++ /// **Example:**
++ /// ```rust
++ /// pub fn foo(x: *const u8) {
++ /// println!("{}", unsafe { *x });
++ /// }
++ /// ```
++ pub NOT_UNSAFE_PTR_ARG_DEREF,
++ correctness,
++ "public functions dereferencing raw pointer arguments but not marked `unsafe`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for a [`#[must_use]`] attribute on
++ /// unit-returning functions and methods.
++ ///
++ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++ ///
++ /// **Why is this bad?** Unit values are useless. The attribute is likely
++ /// a remnant of a refactoring that removed the return type.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Examples:**
++ /// ```rust
++ /// #[must_use]
++ /// fn useless() { }
++ /// ```
++ pub MUST_USE_UNIT,
++ style,
++ "`#[must_use]` attribute on a unit-returning function / method"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for a [`#[must_use]`] attribute without
++ /// further information on functions and methods that return a type already
++ /// marked as `#[must_use]`.
++ ///
++ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++ ///
++ /// **Why is this bad?** The attribute isn't needed. Not using the result
++ /// will already be reported. Alternatively, one can add some text to the
++ /// attribute to improve the lint message.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Examples:**
++ /// ```rust
++ /// #[must_use]
++ /// fn double_must_use() -> Result<(), ()> {
++ /// unimplemented!();
++ /// }
++ /// ```
++ pub DOUBLE_MUST_USE,
++ style,
++ "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for public functions that have no
++ /// [`#[must_use]`] attribute, but return something not already marked
++ /// must-use, have no mutable arg and mutate no statics.
++ ///
++ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++ ///
++ /// **Why is this bad?** Not bad at all, this lint just shows places where
++ /// you could add the attribute.
++ ///
++ /// **Known problems:** The lint only checks the arguments for mutable
++ /// types without looking if they are actually changed. On the other hand,
++ /// it also ignores a broad range of potentially interesting side effects,
++ /// because we cannot decide whether the programmer intends the function to
++ /// be called for the side effect or the result. Expect many false
++ /// positives. At least we don't lint if the result type is unit or already
++ /// `#[must_use]`.
++ ///
++ /// **Examples:**
++ /// ```rust
++ /// // this could be annotated with `#[must_use]`.
++ /// fn id<T>(t: T) -> T { t }
++ /// ```
++ pub MUST_USE_CANDIDATE,
++ pedantic,
++ "function or method that could take a `#[must_use]` attribute"
++}
++
++#[derive(Copy, Clone)]
++pub struct Functions {
++ threshold: u64,
++ max_lines: u64,
++}
++
++impl Functions {
++ pub fn new(threshold: u64, max_lines: u64) -> Self {
++ Self { threshold, max_lines }
++ }
++}
++
++impl_lint_pass!(Functions => [
++ TOO_MANY_ARGUMENTS,
++ TOO_MANY_LINES,
++ NOT_UNSAFE_PTR_ARG_DEREF,
++ MUST_USE_UNIT,
++ DOUBLE_MUST_USE,
++ MUST_USE_CANDIDATE,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: intravisit::FnKind<'tcx>,
++ decl: &'tcx hir::FnDecl<'_>,
++ body: &'tcx hir::Body<'_>,
++ span: Span,
++ hir_id: hir::HirId,
++ ) {
++ let unsafety = match kind {
++ intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
++ intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
++ intravisit::FnKind::Closure(_) => return,
++ };
++
++ // don't warn for implementations, it's not their fault
++ if !is_trait_impl_item(cx, hir_id) {
++ // don't lint extern functions decls, it's not their fault either
++ match kind {
++ intravisit::FnKind::Method(
++ _,
++ &hir::FnSig {
++ header: hir::FnHeader { abi: Abi::Rust, .. },
++ ..
++ },
++ _,
++ _,
++ )
++ | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
++ self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi()))
++ },
++ _ => {},
++ }
++ }
++
++ Self::check_raw_ptr(cx, unsafety, decl, body, hir_id);
++ self.check_line_number(cx, span, body);
++ }
++
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ let attr = must_use_attr(&item.attrs);
++ if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
++ if let Some(attr) = attr {
++ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++ check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++ return;
++ }
++ if cx.access_levels.is_exported(item.hir_id)
++ && !is_proc_macro(&item.attrs)
++ && attr_by_name(&item.attrs, "no_mangle").is_none()
++ {
++ check_must_use_candidate(
++ cx,
++ &sig.decl,
++ cx.tcx.hir().body(*body_id),
++ item.span,
++ item.hir_id,
++ item.span.with_hi(sig.decl.output.span().hi()),
++ "this function could have a `#[must_use]` attribute",
++ );
++ }
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++ if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
++ let attr = must_use_attr(&item.attrs);
++ if let Some(attr) = attr {
++ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++ check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++ } else if cx.access_levels.is_exported(item.hir_id)
++ && !is_proc_macro(&item.attrs)
++ && trait_ref_of_method(cx, item.hir_id).is_none()
++ {
++ check_must_use_candidate(
++ cx,
++ &sig.decl,
++ cx.tcx.hir().body(*body_id),
++ item.span,
++ item.hir_id,
++ item.span.with_hi(sig.decl.output.span().hi()),
++ "this method could have a `#[must_use]` attribute",
++ );
++ }
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++ if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
++ // don't lint extern functions decls, it's not their fault
++ if sig.header.abi == Abi::Rust {
++ self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi()));
++ }
++
++ let attr = must_use_attr(&item.attrs);
++ if let Some(attr) = attr {
++ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++ check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++ }
++ if let hir::TraitFn::Provided(eid) = *eid {
++ let body = cx.tcx.hir().body(eid);
++ Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
++
++ if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) {
++ check_must_use_candidate(
++ cx,
++ &sig.decl,
++ body,
++ item.span,
++ item.hir_id,
++ item.span.with_hi(sig.decl.output.span().hi()),
++ "this method could have a `#[must_use]` attribute",
++ );
++ }
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> Functions {
++ fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl<'_>, fn_span: Span) {
++ let args = decl.inputs.len() as u64;
++ if args > self.threshold {
++ span_lint(
++ cx,
++ TOO_MANY_ARGUMENTS,
++ fn_span,
++ &format!("this function has too many arguments ({}/{})", args, self.threshold),
++ );
++ }
++ }
++
++ fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span, body: &'tcx hir::Body<'_>) {
++ if in_external_macro(cx.sess(), span) {
++ return;
++ }
++
++ let code_snippet = snippet(cx, body.value.span, "..");
++ let mut line_count: u64 = 0;
++ let mut in_comment = false;
++ let mut code_in_line;
++
++ // Skip the surrounding function decl.
++ let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1);
++ let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len());
++ let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines();
++
++ for mut line in function_lines {
++ code_in_line = false;
++ loop {
++ line = line.trim_start();
++ if line.is_empty() {
++ break;
++ }
++ if in_comment {
++ match line.find("*/") {
++ Some(i) => {
++ line = &line[i + 2..];
++ in_comment = false;
++ continue;
++ },
++ None => break,
++ }
++ } else {
++ let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
++ let single_idx = line.find("//").unwrap_or_else(|| line.len());
++ code_in_line |= multi_idx > 0 && single_idx > 0;
++ // Implies multi_idx is below line.len()
++ if multi_idx < single_idx {
++ line = &line[multi_idx + 2..];
++ in_comment = true;
++ continue;
++ }
++ break;
++ }
++ }
++ if code_in_line {
++ line_count += 1;
++ }
++ }
++
++ if line_count > self.max_lines {
++ span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
++ }
++ }
++
++ fn check_raw_ptr(
++ cx: &LateContext<'a, 'tcx>,
++ unsafety: hir::Unsafety,
++ decl: &'tcx hir::FnDecl<'_>,
++ body: &'tcx hir::Body<'_>,
++ hir_id: hir::HirId,
++ ) {
++ let expr = &body.value;
++ if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) {
++ let raw_ptrs = iter_input_pats(decl, body)
++ .zip(decl.inputs.iter())
++ .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
++ .collect::<FxHashSet<_>>();
++
++ if !raw_ptrs.is_empty() {
++ let tables = cx.tcx.body_tables(body.id());
++ let mut v = DerefVisitor {
++ cx,
++ ptrs: raw_ptrs,
++ tables,
++ };
++
++ intravisit::walk_expr(&mut v, expr);
++ }
++ }
++ }
++}
++
++fn check_needless_must_use(
++ cx: &LateContext<'_, '_>,
++ decl: &hir::FnDecl<'_>,
++ item_id: hir::HirId,
++ item_span: Span,
++ fn_header_span: Span,
++ attr: &Attribute,
++) {
++ if in_external_macro(cx.sess(), item_span) {
++ return;
++ }
++ if returns_unit(decl) {
++ span_lint_and_then(
++ cx,
++ MUST_USE_UNIT,
++ fn_header_span,
++ "this unit-returning function has a `#[must_use]` attribute",
++ |diag| {
++ diag.span_suggestion(
++ attr.span,
++ "remove the attribute",
++ "".into(),
++ Applicability::MachineApplicable,
++ );
++ },
++ );
++ } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
++ span_lint_and_help(
++ cx,
++ DOUBLE_MUST_USE,
++ fn_header_span,
++ "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
++ None,
++ "either add some descriptive text or remove the attribute",
++ );
++ }
++}
++
++fn check_must_use_candidate<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ decl: &'tcx hir::FnDecl<'_>,
++ body: &'tcx hir::Body<'_>,
++ item_span: Span,
++ item_id: hir::HirId,
++ fn_span: Span,
++ msg: &str,
++) {
++ if has_mutable_arg(cx, body)
++ || mutates_static(cx, body)
++ || in_external_macro(cx.sess(), item_span)
++ || returns_unit(decl)
++ || !cx.access_levels.is_exported(item_id)
++ || is_must_use_ty(cx, return_ty(cx, item_id))
++ {
++ return;
++ }
++ span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
++ if let Some(snippet) = snippet_opt(cx, fn_span) {
++ diag.span_suggestion(
++ fn_span,
++ "add the attribute",
++ format!("#[must_use] {}", snippet),
++ Applicability::MachineApplicable,
++ );
++ }
++ });
++}
++
++fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
++ match decl.output {
++ hir::FnRetTy::DefaultReturn(_) => true,
++ hir::FnRetTy::Return(ref ty) => match ty.kind {
++ hir::TyKind::Tup(ref tys) => tys.is_empty(),
++ hir::TyKind::Never => true,
++ _ => false,
++ },
++ }
++}
++
++fn has_mutable_arg(cx: &LateContext<'_, '_>, body: &hir::Body<'_>) -> bool {
++ let mut tys = FxHashSet::default();
++ body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys))
++}
++
++fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool {
++ if let hir::PatKind::Wild = pat.kind {
++ return false; // ignore `_` patterns
++ }
++ let def_id = pat.hir_id.owner.to_def_id();
++ if cx.tcx.has_typeck_tables(def_id) {
++ is_mutable_ty(
++ cx,
++ &cx.tcx.typeck_tables_of(def_id.expect_local()).pat_ty(pat),
++ pat.span,
++ tys,
++ )
++ } else {
++ false
++ }
++}
++
++static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
++
++fn is_mutable_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
++ match ty.kind {
++ // primitive types are never mutable
++ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
++ ty::Adt(ref adt, ref substs) => {
++ tys.insert(adt.did) && !ty.is_freeze(cx.tcx, cx.param_env, span)
++ || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
++ && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
++ },
++ ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
++ ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
++ ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
++ mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
++ },
++ // calling something constitutes a side effect, so return true on all callables
++ // also never calls need not be used, so return true for them, too
++ _ => true,
++ }
++}
++
++fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> {
++ if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) {
++ Some(id)
++ } else {
++ None
++ }
++}
++
++struct DerefVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ ptrs: FxHashSet<hir::HirId>,
++ tables: &'a ty::TypeckTables<'tcx>,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ match expr.kind {
++ hir::ExprKind::Call(ref f, args) => {
++ let ty = self.tables.expr_ty(f);
++
++ if type_is_unsafe_function(self.cx, ty) {
++ for arg in args {
++ self.check_arg(arg);
++ }
++ }
++ },
++ hir::ExprKind::MethodCall(_, _, args) => {
++ let def_id = self.tables.type_dependent_def_id(expr.hir_id).unwrap();
++ let base_type = self.cx.tcx.type_of(def_id);
++
++ if type_is_unsafe_function(self.cx, base_type) {
++ for arg in args {
++ self.check_arg(arg);
++ }
++ }
++ },
++ hir::ExprKind::Unary(hir::UnOp::UnDeref, ref ptr) => self.check_arg(ptr),
++ _ => (),
++ }
++
++ intravisit::walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++}
++
++impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
++ fn check_arg(&self, ptr: &hir::Expr<'_>) {
++ if let hir::ExprKind::Path(ref qpath) = ptr.kind {
++ if let Res::Local(id) = qpath_res(self.cx, qpath, ptr.hir_id) {
++ if self.ptrs.contains(&id) {
++ span_lint(
++ self.cx,
++ NOT_UNSAFE_PTR_ARG_DEREF,
++ ptr.span,
++ "this public function dereferences a raw pointer but is not marked `unsafe`",
++ );
++ }
++ }
++ }
++ }
++}
++
++struct StaticMutVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ mutates_static: bool,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
++
++ if self.mutates_static {
++ return;
++ }
++ match expr.kind {
++ Call(_, args) | MethodCall(_, _, args) => {
++ let mut tys = FxHashSet::default();
++ for arg in args {
++ let def_id = arg.hir_id.owner.to_def_id();
++ if self.cx.tcx.has_typeck_tables(def_id)
++ && is_mutable_ty(
++ self.cx,
++ self.cx.tcx.typeck_tables_of(def_id.expect_local()).expr_ty(arg),
++ arg.span,
++ &mut tys,
++ )
++ && is_mutated_static(self.cx, arg)
++ {
++ self.mutates_static = true;
++ return;
++ }
++ tys.clear();
++ }
++ },
++ Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
++ self.mutates_static |= is_mutated_static(self.cx, target)
++ },
++ _ => {},
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++}
++
++fn is_mutated_static(cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) -> bool {
++ use hir::ExprKind::{Field, Index, Path};
++
++ match e.kind {
++ Path(ref qpath) => {
++ if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
++ false
++ } else {
++ true
++ }
++ },
++ Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
++ _ => false,
++ }
++}
++
++fn mutates_static<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, body: &'tcx hir::Body<'_>) -> bool {
++ let mut v = StaticMutVisitor {
++ cx,
++ mutates_static: false,
++ };
++ intravisit::walk_expr(&mut v, &body.value);
++ v.mutates_static
++}
--- /dev/null
--- /dev/null
++use crate::utils;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, FnDecl, HirId};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{Opaque, Predicate::Trait, ToPolyTraitRef};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, Span};
++use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
++use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
++
++declare_clippy_lint! {
++ /// **What it does:** This lint requires Future implementations returned from
++ /// functions and methods to implement the `Send` marker trait. It is mostly
++ /// used by library authors (public and internal) that target an audience where
++ /// multithreaded executors are likely to be used for running these Futures.
++ ///
++ /// **Why is this bad?** A Future implementation captures some state that it
++ /// needs to eventually produce its final value. When targeting a multithreaded
++ /// executor (which is the norm on non-embedded devices) this means that this
++ /// state may need to be transported to other threads, in other words the
++ /// whole Future needs to implement the `Send` marker trait. If it does not,
++ /// then the resulting Future cannot be submitted to a thread pool in the
++ /// end user’s code.
++ ///
++ /// Especially for generic functions it can be confusing to leave the
++ /// discovery of this problem to the end user: the reported error location
++ /// will be far from its cause and can in many cases not even be fixed without
++ /// modifying the library where the offending Future implementation is
++ /// produced.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
++ /// ```
++ pub FUTURE_NOT_SEND,
++ nursery,
++ "public Futures must be Send"
++}
++
++declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FutureNotSend {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'tcx>,
++ _: &'tcx Body<'tcx>,
++ _: Span,
++ hir_id: HirId,
++ ) {
++ if let FnKind::Closure(_) = kind {
++ return;
++ }
++ let ret_ty = utils::return_ty(cx, hir_id);
++ if let Opaque(id, subst) = ret_ty.kind {
++ let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst);
++ let mut is_future = false;
++ for p in preds.predicates {
++ if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
++ if Some(trait_ref.def_id()) == cx.tcx.lang_items().future_trait() {
++ is_future = true;
++ break;
++ }
++ }
++ }
++ if is_future {
++ let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
++ let span = decl.output.span();
++ let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
++ let cause = traits::ObligationCause::misc(span, hir_id);
++ let mut fulfillment_cx = traits::FulfillmentContext::new();
++ fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
++ fulfillment_cx.select_all_or_error(&infcx)
++ });
++ if let Err(send_errors) = send_result {
++ utils::span_lint_and_then(
++ cx,
++ FUTURE_NOT_SEND,
++ span,
++ "future cannot be sent between threads safely",
++ |db| {
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ for FulfillmentError { obligation, .. } in send_errors {
++ infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
++ if let Trait(trait_pred, _) = obligation.predicate {
++ let trait_ref = trait_pred.to_poly_trait_ref();
++ db.note(&*format!(
++ "`{}` doesn't implement `{}`",
++ trait_ref.self_ty(),
++ trait_ref.print_only_trait_path(),
++ ));
++ }
++ }
++ })
++ },
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
++
++use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, 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;
++
++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<'a, 'tcx> LateLintPass<'a, 'tcx> for GetLastWithLen {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ // Is a method call
++ if let ExprKind::MethodCall(ref path, _, ref 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.tables.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,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg,
++};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions.
++ ///
++ /// **Why is this bad?** Redundant code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// // format!() returns a `String`
++ /// let s: String = format!("hello").into();
++ /// ```
++ pub IDENTITY_CONVERSION,
++ complexity,
++ "using always-identical `Into`/`From`/`IntoIter` conversions"
++}
++
++#[derive(Default)]
++pub struct IdentityConversion {
++ try_desugar_arm: Vec<HirId>,
++}
++
++impl_lint_pass!(IdentityConversion => [IDENTITY_CONVERSION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
++ fn check_expr(&mut self, cx: &LateContext<'a, '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(_, ref arms, MatchSource::TryDesugar) => {
++ let e = match arms[0].body.kind {
++ ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e,
++ _ => return,
++ };
++ if let ExprKind::Call(_, ref args) = e.kind {
++ self.try_desugar_arm.push(args[0].hir_id);
++ }
++ },
++
++ ExprKind::MethodCall(ref name, .., ref args) => {
++ if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" {
++ let a = cx.tables.expr_ty(e);
++ let b = cx.tables.expr_ty(&args[0]);
++ if same_tys(cx, a, b) {
++ let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
++
++ span_lint_and_sugg(
++ cx,
++ IDENTITY_CONVERSION,
++ e.span,
++ "identical conversion",
++ "consider removing `.into()`",
++ sugg,
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++ }
++ if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
++ let a = cx.tables.expr_ty(e);
++ let b = cx.tables.expr_ty(&args[0]);
++ if same_tys(cx, a, b) {
++ let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
++ span_lint_and_sugg(
++ cx,
++ IDENTITY_CONVERSION,
++ e.span,
++ "identical conversion",
++ "consider removing `.into_iter()`",
++ sugg,
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++ }
++ },
++
++ ExprKind::Call(ref path, ref args) => {
++ if let ExprKind::Path(ref qpath) = path.kind {
++ if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() {
++ if match_def_path(cx, def_id, &paths::FROM_FROM) {
++ let a = cx.tables.expr_ty(e);
++ let b = cx.tables.expr_ty(&args[0]);
++ if same_tys(cx, a, b) {
++ let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
++ let sugg_msg =
++ format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
++ span_lint_and_sugg(
++ cx,
++ IDENTITY_CONVERSION,
++ e.span,
++ "identical conversion",
++ &sugg_msg,
++ sugg,
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++ }
++ }
++ }
++ },
++
++ _ => {},
++ }
++ }
++
++ fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if Some(&e.hir_id) == self.try_desugar_arm.last() {
++ self.try_desugar_arm.pop();
++ }
++ }
++}
--- /dev/null
--- /dev/null
++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::Span;
++
++use crate::consts::{constant_simple, Constant};
++use crate::utils::{clip, snippet, span_lint, unsext};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for identity operations, e.g., `x + 0`.
++ ///
++ /// **Why is this bad?** This code can be removed without changing the
++ /// meaning. So it just obscures what's going on. Delete it mercilessly.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// x / 1 + 0 * 1 - 0 | 0;
++ /// ```
++ pub IDENTITY_OP,
++ complexity,
++ "using identity operations, e.g., `x + 0` or `y / 1`"
++}
++
++declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if e.span.from_expansion() {
++ return;
++ }
++ if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind {
++ match cmp.node {
++ BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
++ check(cx, left, 0, e.span, right.span);
++ check(cx, right, 0, e.span, left.span);
++ },
++ BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
++ BinOpKind::Mul => {
++ check(cx, left, 1, e.span, right.span);
++ check(cx, right, 1, e.span, left.span);
++ },
++ BinOpKind::Div => check(cx, right, 1, e.span, left.span),
++ BinOpKind::BitAnd => {
++ check(cx, left, -1, e.span, right.span);
++ check(cx, right, -1, e.span, left.span);
++ },
++ _ => (),
++ }
++ }
++ }
++}
++
++#[allow(clippy::cast_possible_wrap)]
++fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
++ if let Some(Constant::Int(v)) = constant_simple(cx, cx.tables, e) {
++ let check = match cx.tables.expr_ty(e).kind {
++ ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
++ ty::Uint(uty) => clip(cx.tcx, !0, uty),
++ _ => return,
++ };
++ if match m {
++ 0 => v == 0,
++ -1 => v == check,
++ 1 => v == 1,
++ _ => unreachable!(),
++ } {
++ span_lint(
++ cx,
++ IDENTITY_OP,
++ span,
++ &format!(
++ "the operation is ineffective. Consider reducing it to `{}`",
++ snippet(cx, arg, "..")
++ ),
++ );
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_type_diagnostic_item, span_lint_and_help, 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 LateLintPass<'_, '_> for IfLetMutex {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) {
++ 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(
++ ref op,
++ ref 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<'tcx, 'l> {
++ mutex_lock_called: bool,
++ found_mutex: Option<&'tcx Expr<'tcx>>,
++ cx: &'tcx LateContext<'tcx, 'l>,
++}
++
++impl<'tcx, 'l> Visitor<'tcx> for OppVisitor<'tcx, 'l> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
++ then {
++ 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<'tcx, 'l> {
++ mutex_lock_called: bool,
++ found_mutex: Option<&'tcx Expr<'tcx>>,
++ cx: &'tcx LateContext<'tcx, 'l>,
++}
++
++impl<'tcx, 'l> Visitor<'tcx> for ArmVisitor<'tcx, 'l> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++ if_chain! {
++ if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
++ then {
++ 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 {
++ if let Some(arm_mutex) = self.found_mutex {
++ SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
++ } else {
++ false
++ }
++ }
++}
++
++fn is_mutex_lock_call<'a>(cx: &LateContext<'a, '_>, expr: &'a Expr<'_>) -> Option<&'a Expr<'a>> {
++ if_chain! {
++ if let ExprKind::MethodCall(path, _span, args) = &expr.kind;
++ if path.ident.to_string() == "lock";
++ let ty = cx.tables.expr_ty(&args[0]);
++ if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
++ then {
++ Some(&args[0])
++ } else {
++ None
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:*** Checks for unnecessary `ok()` in if let.
++ ///
++ /// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match
++ /// on `Ok(pat)`
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// for i in iter {
++ /// if let Some(value) = i.parse().ok() {
++ /// vec.push(value)
++ /// }
++ /// }
++ /// ```
++ /// Could be written:
++ ///
++ /// ```ignore
++ /// for i in iter {
++ /// if let Ok(value) = i.parse() {
++ /// vec.push(value)
++ /// }
++ /// }
++ /// ```
++ pub IF_LET_SOME_RESULT,
++ style,
++ "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
++}
++
++declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OkIfLet {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! { //begin checking variables
++ if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match
++ if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let
++ if let ExprKind::MethodCall(_, ok_span, ref result_types) = op.kind; //check is expr.ok() has type Result<T,E>.ok()
++ if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation
++ if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
++ if is_type_diagnostic_item(cx, cx.tables.expr_ty(&result_types[0]), sym!(result_type));
++ if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
++
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
++ let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability);
++ let sugg = format!(
++ "if let Ok({}) = {}",
++ some_expr_string,
++ trimmed_ok.trim().trim_end_matches('.'),
++ );
++ span_lint_and_sugg(
++ cx,
++ IF_LET_SOME_RESULT,
++ expr.span.with_hi(op.span.hi()),
++ "Matching on `Some` with `ok()` is redundant",
++ &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
++ sugg,
++ applicability,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on if branches that could be swapped so no `!` operation is necessary
++//! on the condition
++
++use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::span_lint_and_help;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
++ /// else branch.
++ ///
++ /// **Why is this bad?** Negations reduce the readability of statements.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let v: Vec<usize> = vec![];
++ /// # fn a() {}
++ /// # fn b() {}
++ /// if !v.is_empty() {
++ /// a()
++ /// } else {
++ /// b()
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// # let v: Vec<usize> = vec![];
++ /// # fn a() {}
++ /// # fn b() {}
++ /// if v.is_empty() {
++ /// b()
++ /// } else {
++ /// a()
++ /// }
++ /// ```
++ pub IF_NOT_ELSE,
++ pedantic,
++ "`if` branches that could be swapped so no negation operation is necessary on the condition"
++}
++
++declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
++
++impl EarlyLintPass for IfNotElse {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
++ if in_external_macro(cx.sess(), item.span) {
++ return;
++ }
++ if let ExprKind::If(ref cond, _, Some(ref els)) = item.kind {
++ if let ExprKind::Block(..) = els.kind {
++ match cond.kind {
++ ExprKind::Unary(UnOp::Not, _) => {
++ span_lint_and_help(
++ cx,
++ IF_NOT_ELSE,
++ item.span,
++ "Unnecessary boolean `not` operation",
++ None,
++ "remove the `!` and swap the blocks of the `if`/`else`",
++ );
++ },
++ ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
++ span_lint_and_help(
++ cx,
++ IF_NOT_ELSE,
++ item.span,
++ "Unnecessary `!=` operation",
++ None,
++ "change to `==` and swap the blocks of the `if`/`else`",
++ );
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ fn_has_unsatisfiable_preds, match_def_path,
++ paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
++ snippet_opt, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for missing return statements at the end of a block.
++ ///
++ /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
++ /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
++ /// the last returning statement because the only difference is a missing `;`. Especially in bigger
++ /// code with multiple return paths having a `return` keyword makes it easier to find the
++ /// corresponding statements.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(x: usize) -> usize {
++ /// x
++ /// }
++ /// ```
++ /// add return
++ /// ```rust
++ /// fn foo(x: usize) -> usize {
++ /// return x;
++ /// }
++ /// ```
++ pub IMPLICIT_RETURN,
++ restriction,
++ "use a return statement like `return expr` instead of an expression"
++}
++
++declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
++
++static LINT_BREAK: &str = "change `break` to `return` as shown";
++static LINT_RETURN: &str = "add `return` as shown";
++
++fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
++ let outer_span = outer_span.source_callsite();
++ let inner_span = inner_span.source_callsite();
++
++ span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| {
++ if let Some(snippet) = snippet_opt(cx, inner_span) {
++ diag.span_suggestion(
++ outer_span,
++ msg,
++ format!("return {}", snippet),
++ Applicability::MachineApplicable,
++ );
++ }
++ });
++}
++
++fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ match expr.kind {
++ // loops could be using `break` instead of `return`
++ ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
++ if let Some(expr) = &block.expr {
++ expr_match(cx, expr);
++ }
++ // only needed in the case of `break` with `;` at the end
++ else if let Some(stmt) = block.stmts.last() {
++ if_chain! {
++ if let StmtKind::Semi(expr, ..) = &stmt.kind;
++ // make sure it's a break, otherwise we want to skip
++ if let ExprKind::Break(.., break_expr) = &expr.kind;
++ if let Some(break_expr) = break_expr;
++ then {
++ lint(cx, expr.span, break_expr.span, LINT_BREAK);
++ }
++ }
++ }
++ },
++ // use `return` instead of `break`
++ ExprKind::Break(.., break_expr) => {
++ if let Some(break_expr) = break_expr {
++ lint(cx, expr.span, break_expr.span, LINT_BREAK);
++ }
++ },
++ ExprKind::Match(.., arms, source) => {
++ let check_all_arms = match source {
++ MatchSource::IfLetDesugar {
++ contains_else_clause: has_else,
++ } => has_else,
++ _ => true,
++ };
++
++ if check_all_arms {
++ for arm in arms {
++ expr_match(cx, &arm.body);
++ }
++ } else {
++ expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body);
++ }
++ },
++ // skip if it already has a return statement
++ ExprKind::Ret(..) => (),
++ // make sure it's not a call that panics
++ ExprKind::Call(expr, ..) => {
++ if_chain! {
++ if let ExprKind::Path(qpath) = &expr.kind;
++ if let Some(path_def_id) = cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id();
++ if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
++ match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
++ then { }
++ else {
++ lint(cx, expr.span, expr.span, LINT_RETURN)
++ }
++ }
++ },
++ // everything else is missing `return`
++ _ => lint(cx, expr.span, expr.span, LINT_RETURN),
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ _: FnKind<'tcx>,
++ _: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ span: 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());
++
++ // checking return type through MIR, HIR is not able to determine inferred closure return types
++ // make sure it's not a macro
++ if !mir.return_ty().is_unit() && !span.from_expansion() {
++ expr_match(cx, &body.value);
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{higher, in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for implicit saturating subtraction.
++ ///
++ /// **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let end: u32 = 10;
++ /// let start: u32 = 5;
++ ///
++ /// let mut i: u32 = end - start;
++ ///
++ /// // Bad
++ /// if i != 0 {
++ /// i -= 1;
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```rust
++ /// let end: u32 = 10;
++ /// let start: u32 = 5;
++ ///
++ /// let mut i: u32 = end - start;
++ ///
++ /// // Good
++ /// i = i.saturating_sub(1);
++ /// ```
++ pub IMPLICIT_SATURATING_SUB,
++ pedantic,
++ "Perform saturating subtraction instead of implicitly checking lower bound of data type"
++}
++
++declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitSaturatingSub {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++ if in_macro(expr.span) {
++ return;
++ }
++ if_chain! {
++ if let Some((ref cond, ref then, None)) = higher::if_block(&expr);
++
++ // Check if the conditional expression is a binary operation
++ if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind;
++
++ // Ensure that the binary operator is >, != and <
++ if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
++
++ // Check if the true condition block has only one statement
++ if let ExprKind::Block(ref block, _) = then.kind;
++ if block.stmts.len() == 1 && block.expr.is_none();
++
++ // Check if assign operation is done
++ if let StmtKind::Semi(ref e) = block.stmts[0].kind;
++ if let Some(target) = subtracts_one(cx, e);
++
++ // Extracting out the variable name
++ if let ExprKind::Path(ref assign_path) = target.kind;
++ if let QPath::Resolved(_, ref ares_path) = assign_path;
++
++ then {
++ // Handle symmetric conditions in the if statement
++ let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
++ if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
++ (cond_left, cond_right)
++ } else {
++ return;
++ }
++ } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
++ if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
++ (cond_right, cond_left)
++ } else {
++ return;
++ }
++ } else {
++ return;
++ };
++
++ // Check if the variable in the condition statement is an integer
++ if !cx.tables.expr_ty(cond_var).is_integral() {
++ return;
++ }
++
++ // Get the variable name
++ let var_name = ares_path.segments[0].ident.name.as_str();
++ const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
++
++ match cond_num_val.kind {
++ ExprKind::Lit(ref cond_lit) => {
++ // Check if the constant is zero
++ if let LitKind::Int(0, _) = cond_lit.node {
++ if cx.tables.expr_ty(cond_left).is_signed() {
++ } else {
++ print_lint_and_sugg(cx, &var_name, expr);
++ };
++ }
++ },
++ ExprKind::Path(ref cond_num_path) => {
++ if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
++ print_lint_and_sugg(cx, &var_name, expr);
++ };
++ },
++ ExprKind::Call(ref func, _) => {
++ if let ExprKind::Path(ref cond_num_path) = func.kind {
++ if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
++ print_lint_and_sugg(cx, &var_name, expr);
++ }
++ };
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++}
++
++fn subtracts_one<'a>(cx: &LateContext<'_, '_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> {
++ match expr.kind {
++ ExprKind::AssignOp(ref op1, ref target, ref value) => {
++ if_chain! {
++ if BinOpKind::Sub == op1.node;
++ // Check if literal being subtracted is one
++ if let ExprKind::Lit(ref lit1) = value.kind;
++ if let LitKind::Int(1, _) = lit1.node;
++ then {
++ Some(target)
++ } else {
++ None
++ }
++ }
++ },
++ ExprKind::Assign(ref target, ref value, _) => {
++ if_chain! {
++ if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind;
++ if BinOpKind::Sub == op1.node;
++
++ if SpanlessEq::new(cx).eq_expr(left1, target);
++
++ if let ExprKind::Lit(ref lit1) = right1.kind;
++ if let LitKind::Int(1, _) = lit1.node;
++ then {
++ Some(target)
++ } else {
++ None
++ }
++ }
++ },
++ _ => None,
++ }
++}
++
++fn print_lint_and_sugg(cx: &LateContext<'_, '_>, var_name: &str, expr: &Expr<'_>) {
++ span_lint_and_sugg(
++ cx,
++ IMPLICIT_SATURATING_SUB,
++ expr.span,
++ "Implicitly performing saturating subtraction",
++ "try",
++ format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()),
++ Applicability::MachineApplicable,
++ );
++}
--- /dev/null
--- /dev/null
++//! lint on indexing and slicing operations
++
++use crate::consts::{constant, Constant};
++use crate::utils::{higher, span_lint, span_lint_and_help};
++use rustc_ast::ast::RangeLimits;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for out of bounds array indexing with a constant
++ /// index.
++ ///
++ /// **Why is this bad?** This will always panic at runtime.
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// # #![allow(const_err)]
++ /// let x = [1, 2, 3, 4];
++ ///
++ /// // Bad
++ /// x[9];
++ /// &x[2..9];
++ ///
++ /// // Good
++ /// x[0];
++ /// x[3];
++ /// ```
++ pub OUT_OF_BOUNDS_INDEXING,
++ correctness,
++ "out of bounds constant indexing"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint
++ /// does report on arrays if we can tell that slicing operations are in bounds and does not
++ /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
++ ///
++ /// **Why is this bad?** Indexing and slicing can panic at runtime and there are
++ /// safe alternatives.
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ /// ```rust,no_run
++ /// // Vector
++ /// let x = vec![0; 5];
++ ///
++ /// // Bad
++ /// x[2];
++ /// &x[2..100];
++ /// &x[2..];
++ /// &x[..100];
++ ///
++ /// // Good
++ /// x.get(2);
++ /// x.get(2..100);
++ /// x.get(2..);
++ /// x.get(..100);
++ ///
++ /// // Array
++ /// let y = [0, 1, 2, 3];
++ ///
++ /// // Bad
++ /// &y[10..100];
++ /// &y[10..];
++ /// &y[..100];
++ ///
++ /// // Good
++ /// &y[2..];
++ /// &y[..2];
++ /// &y[0..3];
++ /// y.get(10);
++ /// y.get(10..100);
++ /// y.get(10..);
++ /// y.get(..100);
++ /// ```
++ pub INDEXING_SLICING,
++ restriction,
++ "indexing/slicing usage"
++}
++
++declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Index(ref array, ref index) = &expr.kind {
++ let ty = cx.tables.expr_ty(array);
++ if let Some(range) = higher::range(cx, index) {
++ // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
++ if let ty::Array(_, s) = ty.kind {
++ let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
++ size.into()
++ } else {
++ return;
++ };
++
++ let const_range = to_const_range(cx, range, size);
++
++ if let (Some(start), _) = const_range {
++ if start > size {
++ span_lint(
++ cx,
++ OUT_OF_BOUNDS_INDEXING,
++ range.start.map_or(expr.span, |start| start.span),
++ "range is out of bounds",
++ );
++ return;
++ }
++ }
++
++ if let (_, Some(end)) = const_range {
++ if end > size {
++ span_lint(
++ cx,
++ OUT_OF_BOUNDS_INDEXING,
++ range.end.map_or(expr.span, |end| end.span),
++ "range is out of bounds",
++ );
++ return;
++ }
++ }
++
++ if let (Some(_), Some(_)) = const_range {
++ // early return because both start and end are constants
++ // and we have proven above that they are in bounds
++ return;
++ }
++ }
++
++ let help_msg = match (range.start, range.end) {
++ (None, Some(_)) => "Consider using `.get(..n)`or `.get_mut(..n)` instead",
++ (Some(_), None) => "Consider using `.get(n..)` or .get_mut(n..)` instead",
++ (Some(_), Some(_)) => "Consider using `.get(n..m)` or `.get_mut(n..m)` instead",
++ (None, None) => return, // [..] is ok.
++ };
++
++ span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg);
++ } else {
++ // Catchall non-range index, i.e., [n] or [n << m]
++ if let ty::Array(..) = ty.kind {
++ // Index is a constant uint.
++ if let Some(..) = constant(cx, cx.tables, index) {
++ // Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
++ return;
++ }
++ }
++
++ span_lint_and_help(
++ cx,
++ INDEXING_SLICING,
++ expr.span,
++ "indexing may panic.",
++ None,
++ "Consider using `.get(n)` or `.get_mut(n)` instead",
++ );
++ }
++ }
++ }
++}
++
++/// Returns a tuple of options with the start and end (exclusive) values of
++/// the range. If the start or end is not constant, None is returned.
++fn to_const_range<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ range: higher::Range<'_>,
++ array_size: u128,
++) -> (Option<u128>, Option<u128>) {
++ let s = range.start.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
++ let start = match s {
++ Some(Some(Constant::Int(x))) => Some(x),
++ Some(_) => None,
++ None => Some(0),
++ };
++
++ let e = range.end.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
++ let end = match e {
++ Some(Some(Constant::Int(x))) => {
++ if range.limits == RangeLimits::Closed {
++ Some(x + 1)
++ } else {
++ Some(x)
++ }
++ },
++ Some(_) => None,
++ None => Some(array_size),
++ };
++
++ (start, end)
++}
--- /dev/null
--- /dev/null
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for iteration that is guaranteed to be infinite.
++ ///
++ /// **Why is this bad?** While there may be places where this is acceptable
++ /// (e.g., in event streams), in most cases this is simply an error.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// use std::iter;
++ ///
++ /// iter::repeat(1_u8).collect::<Vec<_>>();
++ /// ```
++ pub INFINITE_ITER,
++ correctness,
++ "infinite iteration"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for iteration that may be infinite.
++ ///
++ /// **Why is this bad?** While there may be places where this is acceptable
++ /// (e.g., in event streams), in most cases this is simply an error.
++ ///
++ /// **Known problems:** The code may have a condition to stop iteration, but
++ /// this lint is not clever enough to analyze it.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let infinite_iter = 0..;
++ /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
++ /// ```
++ pub MAYBE_INFINITE_ITER,
++ pedantic,
++ "possible infinite iteration"
++}
++
++declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InfiniteIter {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ let (lint, msg) = match complete_infinite_iter(cx, expr) {
++ Infinite => (INFINITE_ITER, "infinite iteration detected"),
++ MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
++ Finite => {
++ return;
++ },
++ };
++ span_lint(cx, lint, expr.span, msg)
++ }
++}
++
++#[derive(Copy, Clone, Debug, PartialEq, Eq)]
++enum Finiteness {
++ Infinite,
++ MaybeInfinite,
++ Finite,
++}
++
++use self::Finiteness::{Finite, Infinite, MaybeInfinite};
++
++impl Finiteness {
++ #[must_use]
++ fn and(self, b: Self) -> Self {
++ match (self, b) {
++ (Finite, _) | (_, Finite) => Finite,
++ (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
++ _ => Infinite,
++ }
++ }
++
++ #[must_use]
++ fn or(self, b: Self) -> Self {
++ match (self, b) {
++ (Infinite, _) | (_, Infinite) => Infinite,
++ (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
++ _ => Finite,
++ }
++ }
++}
++
++impl From<bool> for Finiteness {
++ #[must_use]
++ fn from(b: bool) -> Self {
++ if b {
++ Infinite
++ } else {
++ Finite
++ }
++ }
++}
++
++/// This tells us what to look for to know if the iterator returned by
++/// this method is infinite
++#[derive(Copy, Clone)]
++enum Heuristic {
++ /// infinite no matter what
++ Always,
++ /// infinite if the first argument is
++ First,
++ /// infinite if any of the supplied arguments is
++ Any,
++ /// infinite if all of the supplied arguments are
++ All,
++}
++
++use self::Heuristic::{All, Always, Any, First};
++
++/// a slice of (method name, number of args, heuristic, bounds) tuples
++/// that will be used to determine whether the method in question
++/// returns an infinite or possibly infinite iterator. The finiteness
++/// is an upper bound, e.g., some methods can return a possibly
++/// infinite iterator at worst, e.g., `take_while`.
++const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
++ ("zip", 2, All, Infinite),
++ ("chain", 2, Any, Infinite),
++ ("cycle", 1, Always, Infinite),
++ ("map", 2, First, Infinite),
++ ("by_ref", 1, First, Infinite),
++ ("cloned", 1, First, Infinite),
++ ("rev", 1, First, Infinite),
++ ("inspect", 1, First, Infinite),
++ ("enumerate", 1, First, Infinite),
++ ("peekable", 2, First, Infinite),
++ ("fuse", 1, First, Infinite),
++ ("skip", 2, First, Infinite),
++ ("skip_while", 1, First, Infinite),
++ ("filter", 2, First, Infinite),
++ ("filter_map", 2, First, Infinite),
++ ("flat_map", 2, First, Infinite),
++ ("unzip", 1, First, Infinite),
++ ("take_while", 2, First, MaybeInfinite),
++ ("scan", 3, First, MaybeInfinite),
++];
++
++fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness {
++ match expr.kind {
++ ExprKind::MethodCall(ref method, _, ref args) => {
++ for &(name, len, heuristic, cap) in &HEURISTICS {
++ if method.ident.name.as_str() == name && args.len() == len {
++ return (match heuristic {
++ Always => Infinite,
++ First => is_infinite(cx, &args[0]),
++ Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
++ All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
++ })
++ .and(cap);
++ }
++ }
++ if method.ident.name == sym!(flat_map) && args.len() == 2 {
++ if let ExprKind::Closure(_, _, body_id, _, _) = args[1].kind {
++ let body = cx.tcx.hir().body(body_id);
++ return is_infinite(cx, &body.value);
++ }
++ }
++ Finite
++ },
++ ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
++ ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e),
++ ExprKind::Call(ref path, _) => {
++ if let ExprKind::Path(ref qpath) = path.kind {
++ match_qpath(qpath, &paths::REPEAT).into()
++ } else {
++ Finite
++ }
++ },
++ ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(),
++ _ => Finite,
++ }
++}
++
++/// the names and argument lengths of methods that *may* exhaust their
++/// iterators
++const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
++ ("find", 2),
++ ("rfind", 2),
++ ("position", 2),
++ ("rposition", 2),
++ ("any", 2),
++ ("all", 2),
++];
++
++/// the names and argument lengths of methods that *always* exhaust
++/// their iterators
++const COMPLETING_METHODS: [(&str, usize); 12] = [
++ ("count", 1),
++ ("fold", 3),
++ ("for_each", 2),
++ ("partition", 2),
++ ("max", 1),
++ ("max_by", 2),
++ ("max_by_key", 2),
++ ("min", 1),
++ ("min_by", 2),
++ ("min_by_key", 2),
++ ("sum", 1),
++ ("product", 1),
++];
++
++/// the paths of types that are known to be infinitely allocating
++const INFINITE_COLLECTORS: [&[&str]; 8] = [
++ &paths::BINARY_HEAP,
++ &paths::BTREEMAP,
++ &paths::BTREESET,
++ &paths::HASHMAP,
++ &paths::HASHSET,
++ &paths::LINKED_LIST,
++ &paths::VEC,
++ &paths::VEC_DEQUE,
++];
++
++fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness {
++ match expr.kind {
++ ExprKind::MethodCall(ref method, _, ref args) => {
++ for &(name, len) in &COMPLETING_METHODS {
++ if method.ident.name.as_str() == name && args.len() == len {
++ return is_infinite(cx, &args[0]);
++ }
++ }
++ for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
++ if method.ident.name.as_str() == name && args.len() == len {
++ return MaybeInfinite.and(is_infinite(cx, &args[0]));
++ }
++ }
++ if method.ident.name == sym!(last) && args.len() == 1 {
++ let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR)
++ .map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[]));
++ if not_double_ended {
++ return is_infinite(cx, &args[0]);
++ }
++ } else if method.ident.name == sym!(collect) {
++ let ty = cx.tables.expr_ty(expr);
++ if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) {
++ return is_infinite(cx, &args[0]);
++ }
++ }
++ },
++ ExprKind::Binary(op, ref l, ref r) => {
++ if op.node.is_comparison() {
++ return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
++ }
++ }, // TODO: ExprKind::Loop + Match
++ _ => (),
++ }
++ Finite
++}
--- /dev/null
--- /dev/null
++//! lint on inherent implementations
++
++use crate::utils::{in_macro, span_lint_and_then};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::{def_id, Crate, Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for multiple inherent implementations of a struct
++ ///
++ /// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// struct X;
++ /// impl X {
++ /// fn one() {}
++ /// }
++ /// impl X {
++ /// fn other() {}
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// struct X;
++ /// impl X {
++ /// fn one() {}
++ /// fn other() {}
++ /// }
++ /// ```
++ pub MULTIPLE_INHERENT_IMPL,
++ restriction,
++ "Multiple inherent impl that could be grouped"
++}
++
++#[allow(clippy::module_name_repetitions)]
++#[derive(Default)]
++pub struct MultipleInherentImpl {
++ impls: FxHashMap<def_id::DefId, Span>,
++}
++
++impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl {
++ fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Impl {
++ ref generics,
++ of_trait: None,
++ ..
++ } = item.kind
++ {
++ // Remember for each inherent implementation encoutered its span and generics
++ // but filter out implementations that have generic params (type or lifetime)
++ // or are derived from a macro
++ if !in_macro(item.span) && generics.params.is_empty() {
++ self.impls.insert(item.hir_id.owner.to_def_id(), item.span);
++ }
++ }
++ }
++
++ fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx Crate<'_>) {
++ if let Some(item) = krate.items.values().next() {
++ // Retrieve all inherent implementations from the crate, grouped by type
++ for impls in cx
++ .tcx
++ .crate_inherent_impls(item.hir_id.owner.to_def_id().krate)
++ .inherent_impls
++ .values()
++ {
++ // Filter out implementations that have generic params (type or lifetime)
++ let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def));
++ if let Some(initial_span) = impl_spans.next() {
++ impl_spans.for_each(|additional_span| {
++ span_lint_and_then(
++ cx,
++ MULTIPLE_INHERENT_IMPL,
++ *additional_span,
++ "Multiple implementations of this structure",
++ |diag| {
++ diag.span_note(*initial_span, "First implementation here");
++ },
++ )
++ })
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_hir::{ImplItem, ImplItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{
++ get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
++ trait_ref_of_method, walk_ptrs_ty,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
++ ///
++ /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
++ ///
++ /// **Known problems:** None
++ ///
++ /// ** Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// pub struct A;
++ ///
++ /// impl A {
++ /// pub fn to_string(&self) -> String {
++ /// "I am A".to_string()
++ /// }
++ /// }
++ /// ```
++ ///
++ /// ```rust
++ /// // Good
++ /// use std::fmt;
++ ///
++ /// pub struct A;
++ ///
++ /// impl fmt::Display for A {
++ /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++ /// write!(f, "I am A")
++ /// }
++ /// }
++ /// ```
++ pub INHERENT_TO_STRING,
++ style,
++ "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
++ ///
++ /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
++ ///
++ /// **Known problems:** None
++ ///
++ /// ** Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// use std::fmt;
++ ///
++ /// pub struct A;
++ ///
++ /// impl A {
++ /// pub fn to_string(&self) -> String {
++ /// "I am A".to_string()
++ /// }
++ /// }
++ ///
++ /// impl fmt::Display for A {
++ /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++ /// write!(f, "I am A, too")
++ /// }
++ /// }
++ /// ```
++ ///
++ /// ```rust
++ /// // Good
++ /// use std::fmt;
++ ///
++ /// pub struct A;
++ ///
++ /// impl fmt::Display for A {
++ /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++ /// write!(f, "I am A")
++ /// }
++ /// }
++ /// ```
++ pub INHERENT_TO_STRING_SHADOW_DISPLAY,
++ correctness,
++ "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
++}
++
++declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString {
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) {
++ if impl_item.span.from_expansion() {
++ return;
++ }
++
++ if_chain! {
++ // Check if item is a method, called to_string and has a parameter 'self'
++ if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
++ if impl_item.ident.name.as_str() == "to_string";
++ let decl = &signature.decl;
++ if decl.implicit_self.has_implicit_self();
++ if decl.inputs.len() == 1;
++
++ // Check if return type is String
++ if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
++
++ // Filters instances of to_string which are required by a trait
++ if trait_ref_of_method(cx, impl_item.hir_id).is_none();
++
++ then {
++ show_lint(cx, impl_item);
++ }
++ }
++ }
++}
++
++fn show_lint(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) {
++ let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!");
++
++ // Get the real type of 'self'
++ let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
++ let self_type = walk_ptrs_ty(self_type.skip_binder());
++
++ // Emit either a warning or an error
++ if implements_trait(cx, self_type, display_trait_id, &[]) {
++ span_lint_and_help(
++ cx,
++ INHERENT_TO_STRING_SHADOW_DISPLAY,
++ item.span,
++ &format!(
++ "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`",
++ self_type.to_string()
++ ),
++ None,
++ &format!("remove the inherent method from type `{}`", self_type.to_string())
++ );
++ } else {
++ span_lint_and_help(
++ cx,
++ INHERENT_TO_STRING,
++ item.span,
++ &format!(
++ "implementation of inherent method `to_string(&self) -> String` for type `{}`",
++ self_type.to_string()
++ ),
++ None,
++ &format!("implement trait `Display` for type `{}` instead", self_type.to_string()),
++ );
++ }
++}
--- /dev/null
--- /dev/null
++//! checks for `#[inline]` on trait methods without bodies
++
++use crate::utils::span_lint_and_then;
++use crate::utils::sugg::DiagnosticBuilderExt;
++use rustc_ast::ast::{Attribute, Name};
++use rustc_errors::Applicability;
++use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `#[inline]` on trait methods without bodies
++ ///
++ /// **Why is this bad?** Only implementations of trait methods may be inlined.
++ /// The inline attribute is ignored for trait methods without bodies.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// trait Animal {
++ /// #[inline]
++ /// fn name(&self) -> &'static str;
++ /// }
++ /// ```
++ pub INLINE_FN_WITHOUT_BODY,
++ correctness,
++ "use of `#[inline]` on trait methods without bodies"
++}
++
++declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InlineFnWithoutBody {
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++ if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind {
++ check_attrs(cx, item.ident.name, &item.attrs);
++ }
++ }
++}
++
++fn check_attrs(cx: &LateContext<'_, '_>, name: Name, attrs: &[Attribute]) {
++ for attr in attrs {
++ if !attr.check_name(sym!(inline)) {
++ continue;
++ }
++
++ span_lint_and_then(
++ cx,
++ INLINE_FN_WITHOUT_BODY,
++ attr.span,
++ &format!("use of `#[inline]` on trait method `{}` which has no body", name),
++ |diag| {
++ diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
++ },
++ );
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on blocks unnecessarily using >= with a + 1 or - 1
++
++use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{snippet_opt, span_lint_and_sugg};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
++ ///
++ ///
++ /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// # let y = 1;
++ /// if x >= y + 1 {}
++ /// ```
++ ///
++ /// Could be written as:
++ ///
++ /// ```rust
++ /// # let x = 1;
++ /// # let y = 1;
++ /// if x > y {}
++ /// ```
++ pub INT_PLUS_ONE,
++ complexity,
++ "instead of using `x >= y + 1`, use `x > y`"
++}
++
++declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
++
++// cases:
++// BinOpKind::Ge
++// x >= y + 1
++// x - 1 >= y
++//
++// BinOpKind::Le
++// x + 1 <= y
++// x <= y - 1
++
++#[derive(Copy, Clone)]
++enum Side {
++ LHS,
++ RHS,
++}
++
++impl IntPlusOne {
++ #[allow(clippy::cast_sign_loss)]
++ fn check_lit(lit: &Lit, target_value: i128) -> bool {
++ if let LitKind::Int(value, ..) = lit.kind {
++ return value == (target_value as u128);
++ }
++ false
++ }
++
++ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
++ match (binop, &lhs.kind, &rhs.kind) {
++ // case where `x - 1 >= ...` or `-1 + x >= ...`
++ (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
++ match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
++ // `-1 + x`
++ (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
++ Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
++ },
++ // `x - 1`
++ (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
++ },
++ _ => None,
++ }
++ },
++ // case where `... >= y + 1` or `... >= 1 + y`
++ (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs))
++ if rhskind.node == BinOpKind::Add =>
++ {
++ match (&rhslhs.kind, &rhsrhs.kind) {
++ // `y + 1` and `1 + y`
++ (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
++ },
++ (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
++ },
++ _ => None,
++ }
++ }
++ // case where `x + 1 <= ...` or `1 + x <= ...`
++ (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _)
++ if lhskind.node == BinOpKind::Add =>
++ {
++ match (&lhslhs.kind, &lhsrhs.kind) {
++ // `1 + x` and `x + 1`
++ (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
++ },
++ (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
++ },
++ _ => None,
++ }
++ }
++ // case where `... >= y - 1` or `... >= -1 + y`
++ (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
++ match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
++ // `-1 + y`
++ (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
++ Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
++ },
++ // `y - 1`
++ (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++ Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
++ },
++ _ => None,
++ }
++ },
++ _ => None,
++ }
++ }
++
++ fn generate_recommendation(
++ cx: &EarlyContext<'_>,
++ binop: BinOpKind,
++ node: &Expr,
++ other_side: &Expr,
++ side: Side,
++ ) -> Option<String> {
++ let binop_string = match binop {
++ BinOpKind::Ge => ">",
++ BinOpKind::Le => "<",
++ _ => return None,
++ };
++ if let Some(snippet) = snippet_opt(cx, node.span) {
++ if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
++ let rec = match side {
++ Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
++ Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
++ };
++ return rec;
++ }
++ }
++ None
++ }
++
++ fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
++ span_lint_and_sugg(
++ cx,
++ INT_PLUS_ONE,
++ block.span,
++ "Unnecessary `>= y + 1` or `x - 1 >=`",
++ "change it to",
++ recommendation,
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++}
++
++impl EarlyLintPass for IntPlusOne {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
++ if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
++ if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
++ Self::emit_warning(cx, item, rec.clone());
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint_and_help;
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for division of integers
++ ///
++ /// **Why is this bad?** When outside of some very specific algorithms,
++ /// integer division is very often a mistake because it discards the
++ /// remainder.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn main() {
++ /// let x = 3 / 2;
++ /// println!("{}", x);
++ /// }
++ /// ```
++ pub INTEGER_DIVISION,
++ restriction,
++ "integer division may cause loss of precision"
++}
++
++declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IntegerDivision {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if is_integer_division(cx, expr) {
++ span_lint_and_help(
++ cx,
++ INTEGER_DIVISION,
++ expr.span,
++ "integer division",
++ None,
++ "division of integers may cause loss of precision. consider using floats.",
++ );
++ }
++ }
++}
++
++fn is_integer_division<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
++ if_chain! {
++ if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
++ if let hir::BinOpKind::Div = &binop.node;
++ then {
++ let (left_ty, right_ty) = (cx.tables.expr_ty(left), cx.tables.expr_ty(right));
++ return left_ty.is_integral() && right_ty.is_integral();
++ }
++ }
++
++ false
++}
--- /dev/null
--- /dev/null
++//! lint when items are used after statements
++
++use crate::utils::span_lint;
++use rustc_ast::ast::{Block, ItemKind, StmtKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for items declared after some statement in a block.
++ ///
++ /// **Why is this bad?** Items live for the entire scope they are declared
++ /// in. But statements are processed in order. This might cause confusion as
++ /// it's hard to figure out which item is meant in a statement.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo() {
++ /// println!("cake");
++ /// }
++ ///
++ /// fn main() {
++ /// foo(); // prints "foo"
++ /// fn foo() {
++ /// println!("foo");
++ /// }
++ /// foo(); // prints "foo"
++ /// }
++ /// ```
++ pub ITEMS_AFTER_STATEMENTS,
++ pedantic,
++ "blocks where an item comes after a statement"
++}
++
++declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
++
++impl EarlyLintPass for ItemsAfterStatements {
++ fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
++ if item.span.from_expansion() {
++ return;
++ }
++
++ // skip initial items
++ let stmts = item
++ .stmts
++ .iter()
++ .map(|stmt| &stmt.kind)
++ .skip_while(|s| matches!(**s, StmtKind::Item(..)));
++
++ // lint on all further items
++ for stmt in stmts {
++ if let StmtKind::Item(ref it) = *stmt {
++ if it.span.from_expansion() {
++ return;
++ }
++ if let ItemKind::MacroDef(..) = it.kind {
++ // do not lint `macro_rules`, but continue processing further statements
++ continue;
++ }
++ span_lint(
++ cx,
++ ITEMS_AFTER_STATEMENTS,
++ it.span,
++ "adding items after statements is confusing, since items exist from the \
++ start of the scope",
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::rustc_target::abi::LayoutOf;
++use crate::utils::span_lint_and_then;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::interpret::ConstValue;
++use rustc_middle::ty::{self, ConstKind};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{BytePos, Pos, Span};
++use rustc_typeck::hir_ty_to_ty;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for large `const` arrays that should
++ /// be defined as `static` instead.
++ ///
++ /// **Why is this bad?** Performance: const variables are inlined upon use.
++ /// Static items result in only one instance and has a fixed location in memory.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// // Bad
++ /// pub const a = [0u32; 1_000_000];
++ ///
++ /// // Good
++ /// pub static a = [0u32; 1_000_000];
++ /// ```
++ pub LARGE_CONST_ARRAYS,
++ perf,
++ "large non-scalar const array may cause performance overhead"
++}
++
++pub struct LargeConstArrays {
++ maximum_allowed_size: u64,
++}
++
++impl LargeConstArrays {
++ #[must_use]
++ pub fn new(maximum_allowed_size: u64) -> Self {
++ Self { maximum_allowed_size }
++ }
++}
++
++impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeConstArrays {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if_chain! {
++ if !item.span.from_expansion();
++ if let ItemKind::Const(hir_ty, _) = &item.kind;
++ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++ if let ty::Array(element_type, cst) = ty.kind;
++ if let ConstKind::Value(val) = cst.val;
++ if let ConstValue::Scalar(element_count) = val;
++ if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
++ if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
++ if self.maximum_allowed_size < element_count * element_size;
++
++ then {
++ let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
++ let sugg_span = Span::new(
++ hi_pos - BytePos::from_usize("const".len()),
++ hi_pos,
++ item.span.ctxt(),
++ );
++ span_lint_and_then(
++ cx,
++ LARGE_CONST_ARRAYS,
++ item.span,
++ "large array defined as const",
++ |diag| {
++ diag.span_suggestion(
++ sugg_span,
++ "make this a static item",
++ "static".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint when there is a large size difference between variants on an enum
++
++use crate::utils::{snippet_opt, span_lint_and_then};
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind, VariantData};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_target::abi::LayoutOf;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for large size differences between variants on
++ /// `enum`s.
++ ///
++ /// **Why is this bad?** Enum size is bounded by the largest variant. Having a
++ /// large variant can penalize the memory layout of that enum.
++ ///
++ /// **Known problems:** This lint obviously cannot take the distribution of
++ /// variants in your running program into account. It is possible that the
++ /// smaller variants make up less than 1% of all instances, in which case
++ /// the overhead is negligible and the boxing is counter-productive. Always
++ /// measure the change this lint suggests.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// enum Test {
++ /// A(i32),
++ /// B([i32; 8000]),
++ /// }
++ ///
++ /// // Possibly better
++ /// enum Test2 {
++ /// A(i32),
++ /// B(Box<[i32; 8000]>),
++ /// }
++ /// ```
++ pub LARGE_ENUM_VARIANT,
++ perf,
++ "large size difference between variants on an enum"
++}
++
++#[derive(Copy, Clone)]
++pub struct LargeEnumVariant {
++ maximum_size_difference_allowed: u64,
++}
++
++impl LargeEnumVariant {
++ #[must_use]
++ pub fn new(maximum_size_difference_allowed: u64) -> Self {
++ Self {
++ maximum_size_difference_allowed,
++ }
++ }
++}
++
++impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
++ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++ let did = cx.tcx.hir().local_def_id(item.hir_id);
++ if let ItemKind::Enum(ref def, _) = item.kind {
++ let ty = cx.tcx.type_of(did);
++ let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
++
++ let mut largest_variant: Option<(_, _)> = None;
++ let mut second_variant: Option<(_, _)> = None;
++
++ for (i, variant) in adt.variants.iter().enumerate() {
++ let size: u64 = variant
++ .fields
++ .iter()
++ .filter_map(|f| {
++ let ty = cx.tcx.type_of(f.did);
++ // don't count generics by filtering out everything
++ // that does not have a layout
++ cx.layout_of(ty).ok().map(|l| l.size.bytes())
++ })
++ .sum();
++
++ let grouped = (size, (i, variant));
++
++ if grouped.0 >= largest_variant.map_or(0, |x| x.0) {
++ second_variant = largest_variant;
++ largest_variant = Some(grouped);
++ }
++ }
++
++ if let (Some(largest), Some(second)) = (largest_variant, second_variant) {
++ let difference = largest.0 - second.0;
++
++ if difference > self.maximum_size_difference_allowed {
++ let (i, variant) = largest.1;
++
++ let help_text = "consider boxing the large fields to reduce the total size of the enum";
++ span_lint_and_then(
++ cx,
++ LARGE_ENUM_VARIANT,
++ def.variants[i].span,
++ "large size difference between variants",
++ |diag| {
++ diag.span_label(
++ def.variants[(largest.1).0].span,
++ &format!("this variant is {} bytes", largest.0),
++ );
++ diag.span_note(
++ def.variants[(second.1).0].span,
++ &format!("and the second-largest variant is {} bytes:", second.0),
++ );
++ if variant.fields.len() == 1 {
++ let span = match def.variants[i].data {
++ VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => {
++ fields[0].ty.span
++ },
++ VariantData::Unit(..) => unreachable!(),
++ };
++ if let Some(snip) = snippet_opt(cx, span) {
++ diag.span_suggestion(
++ span,
++ help_text,
++ format!("Box<{}>", snip),
++ Applicability::MaybeIncorrect,
++ );
++ return;
++ }
++ }
++ diag.span_help(def.variants[i].span, help_text);
++ },
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::interpret::ConstValue;
++use rustc_middle::ty::{self, ConstKind};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++use if_chain::if_chain;
++
++use crate::rustc_target::abi::LayoutOf;
++use crate::utils::{snippet, span_lint_and_help};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for local arrays that may be too large.
++ ///
++ /// **Why is this bad?** Large local arrays may cause stack overflow.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// let a = [0u32; 1_000_000];
++ /// ```
++ pub LARGE_STACK_ARRAYS,
++ pedantic,
++ "allocating large arrays on stack may cause stack overflow"
++}
++
++pub struct LargeStackArrays {
++ maximum_allowed_size: u64,
++}
++
++impl LargeStackArrays {
++ #[must_use]
++ pub fn new(maximum_allowed_size: u64) -> Self {
++ Self { maximum_allowed_size }
++ }
++}
++
++impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Repeat(_, _) = expr.kind;
++ if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind;
++ if let ConstKind::Value(val) = cst.val;
++ if let ConstValue::Scalar(element_count) = val;
++ if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
++ if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
++ if self.maximum_allowed_size < element_count * element_size;
++ then {
++ span_lint_and_help(
++ cx,
++ LARGE_STACK_ARRAYS,
++ expr.span,
++ &format!(
++ "allocating a local array larger than {} bytes",
++ self.maximum_allowed_size
++ ),
++ None,
++ &format!(
++ "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
++ snippet(cx, expr.span, "[...]")
++ ),
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
++use rustc_ast::ast::{LitKind, Name};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir::def_id::DefId;
++use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{Span, Spanned};
++
++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_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if item.span.from_expansion() {
++ return;
++ }
++
++ match item.kind {
++ ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
++ ItemKind::Impl {
++ of_trait: None,
++ items: ref impl_items,
++ ..
++ } => check_impl_items(cx, item, impl_items),
++ _ => (),
++ }
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref 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]) {
++ fn is_named_self(cx: &LateContext<'_, '_>, item: &TraitItemRef, name: &str) -> bool {
++ item.ident.name.as_str() == name
++ && if let AssocItemKind::Fn { has_self } = item.kind {
++ has_self && {
++ let did = cx.tcx.hir().local_def_id(item.id.hir_id);
++ cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
++ }
++ } else {
++ false
++ }
++ }
++
++ // fill the set with current and super traits
++ fn fill_trait_set(traitt: DefId, set: &mut FxHashSet<DefId>, 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 cx.access_levels.is_exported(visited_trait.hir_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
++ let mut current_and_super_traits = FxHashSet::default();
++ let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.hir_id);
++ 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
++ ),
++ );
++ }
++ }
++}
++
++fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) {
++ fn is_named_self(cx: &LateContext<'_, '_>, item: &ImplItemRef<'_>, name: &str) -> bool {
++ item.ident.name.as_str() == name
++ && if let AssocItemKind::Fn { has_self } = item.kind {
++ has_self && {
++ let did = cx.tcx.hir().local_def_id(item.id.hir_id);
++ cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
++ }
++ } else {
++ false
++ }
++ }
++
++ let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
++ if cx.access_levels.is_exported(is_empty.id.hir_id) {
++ return;
++ } else {
++ "a private"
++ }
++ } else {
++ "no corresponding"
++ };
++
++ if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) {
++ if cx.access_levels.is_exported(i.id.hir_id) {
++ let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ let ty = cx.tcx.type_of(def_id);
++
++ span_lint(
++ cx,
++ LEN_WITHOUT_IS_EMPTY,
++ item.span,
++ &format!(
++ "item `{}` has a public `len` method but {} `is_empty` method",
++ ty, is_empty
++ ),
++ );
++ }
++ }
++}
++
++fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
++ if let (&ExprKind::MethodCall(ref method_path, _, ref 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)
++ }
++}
++
++fn check_len(
++ cx: &LateContext<'_, '_>,
++ span: Span,
++ method_name: Name,
++ 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.as_str() == "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,
++ );
++ }
++ }
++}
++
++/// 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 = &walk_ptrs_ty(cx.tables.expr_ty(expr));
++ match ty.kind {
++ ty::Dynamic(ref tt, ..) => {
++ if let Some(principal) = tt.principal() {
++ cx.tcx
++ .associated_items(principal.def_id())
++ .in_definition_order()
++ .any(|item| is_is_empty(cx, &item))
++ } else {
++ false
++ }
++ },
++ 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,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{higher, qpath_res, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit;
++use rustc_hir::BindingAnnotation;
++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 variable declarations immediately followed by a
++ /// conditional affectation.
++ ///
++ /// **Why is this bad?** This is not idiomatic Rust.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// let foo;
++ ///
++ /// if bar() {
++ /// foo = 42;
++ /// } else {
++ /// foo = 0;
++ /// }
++ ///
++ /// let mut baz = None;
++ ///
++ /// if bar() {
++ /// baz = Some(42);
++ /// }
++ /// ```
++ ///
++ /// should be written
++ ///
++ /// ```rust,ignore
++ /// let foo = if bar() {
++ /// 42
++ /// } else {
++ /// 0
++ /// };
++ ///
++ /// let baz = if bar() {
++ /// Some(42)
++ /// } else {
++ /// None
++ /// };
++ /// ```
++ pub USELESS_LET_IF_SEQ,
++ style,
++ "unidiomatic `let mut` declaration followed by initialization in `if`"
++}
++
++declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
++ fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) {
++ let mut it = block.stmts.iter().peekable();
++ while let Some(stmt) = it.next() {
++ if_chain! {
++ if let Some(expr) = it.peek();
++ if let hir::StmtKind::Local(ref local) = stmt.kind;
++ if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
++ if let hir::StmtKind::Expr(ref if_) = expr.kind;
++ if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_);
++ if !used_in_expr(cx, canonical_id, cond);
++ if let hir::ExprKind::Block(ref then, _) = then.kind;
++ if let Some(value) = check_assign(cx, canonical_id, &*then);
++ if !used_in_expr(cx, canonical_id, value);
++ then {
++ let span = stmt.span.to(if_.span);
++
++ let has_interior_mutability = !cx.tables.node_type(canonical_id).is_freeze(
++ cx.tcx,
++ cx.param_env,
++ span
++ );
++ if has_interior_mutability { return; }
++
++ let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
++ if let hir::ExprKind::Block(ref else_, _) = else_.kind {
++ if let Some(default) = check_assign(cx, canonical_id, else_) {
++ (else_.stmts.len() > 1, default)
++ } else if let Some(ref default) = local.init {
++ (true, &**default)
++ } else {
++ continue;
++ }
++ } else {
++ continue;
++ }
++ } else if let Some(ref default) = local.init {
++ (false, &**default)
++ } else {
++ continue;
++ };
++
++ let mutability = match mode {
++ BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
++ _ => "",
++ };
++
++ // FIXME: this should not suggest `mut` if we can detect that the variable is not
++ // use mutably after the `if`
++
++ let sug = format!(
++ "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
++ mut=mutability,
++ name=ident.name,
++ cond=snippet(cx, cond.span, "_"),
++ then=if then.stmts.len() > 1 { " ..;" } else { "" },
++ else=if default_multi_stmts { " ..;" } else { "" },
++ value=snippet(cx, value.span, "<value>"),
++ default=snippet(cx, default.span, "<default>"),
++ );
++ span_lint_and_then(cx,
++ USELESS_LET_IF_SEQ,
++ span,
++ "`if _ { .. } else { .. }` is an expression",
++ |diag| {
++ diag.span_suggestion(
++ span,
++ "it is more idiomatic to write",
++ sug,
++ Applicability::HasPlaceholders,
++ );
++ if !mutability.is_empty() {
++ diag.note("you might not need `mut` at all");
++ }
++ });
++ }
++ }
++ }
++ }
++}
++
++struct UsedVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ id: hir::HirId,
++ used: bool,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ if_chain! {
++ if let hir::ExprKind::Path(ref qpath) = expr.kind;
++ if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id);
++ if self.id == local_id;
++ then {
++ self.used = true;
++ return;
++ }
++ }
++ intravisit::walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++}
++
++fn check_assign<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ decl: hir::HirId,
++ block: &'tcx hir::Block<'_>,
++) -> Option<&'tcx hir::Expr<'tcx>> {
++ if_chain! {
++ if block.expr.is_none();
++ if let Some(expr) = block.stmts.iter().last();
++ if let hir::StmtKind::Semi(ref expr) = expr.kind;
++ if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind;
++ if let hir::ExprKind::Path(ref qpath) = var.kind;
++ if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id);
++ if decl == local_id;
++ then {
++ let mut v = UsedVisitor {
++ cx,
++ id: decl,
++ used: false,
++ };
++
++ for s in block.stmts.iter().take(block.stmts.len()-1) {
++ intravisit::walk_stmt(&mut v, s);
++
++ if v.used {
++ return None;
++ }
++ }
++
++ return Some(value);
++ }
++ }
++
++ None
++}
++
++fn used_in_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool {
++ let mut v = UsedVisitor { cx, id, used: false };
++ intravisit::walk_expr(&mut v, expr);
++ v.used
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_hir::{Local, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::subst::GenericArgKind;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `let _ = <expr>`
++ /// where expr is #[must_use]
++ ///
++ /// **Why is this bad?** It's better to explicitly
++ /// handle the value of a #[must_use] expr
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn f() -> Result<u32, u32> {
++ /// Ok(0)
++ /// }
++ ///
++ /// let _ = f();
++ /// // is_ok() is marked #[must_use]
++ /// let _ = f().is_ok();
++ /// ```
++ pub LET_UNDERSCORE_MUST_USE,
++ restriction,
++ "non-binding let on a `#[must_use]` expression"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `let _ = sync_lock`
++ ///
++ /// **Why is this bad?** This statement immediately drops the lock instead of
++ /// extending it's lifetime to the end of the scope, which is often not intended.
++ /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
++ /// name instead (i.e. _lock). If you want to explicitly drop the lock,
++ /// `std::mem::drop` conveys your intention better and is less error-prone.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// Bad:
++ /// ```rust,ignore
++ /// let _ = mutex.lock();
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// let _lock = mutex.lock();
++ /// ```
++ pub LET_UNDERSCORE_LOCK,
++ correctness,
++ "non-binding let on a synchronization lock"
++}
++
++declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);
++
++const SYNC_GUARD_PATHS: [&[&str]; 3] = [
++ &paths::MUTEX_GUARD,
++ &paths::RWLOCK_READ_GUARD,
++ &paths::RWLOCK_WRITE_GUARD,
++];
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
++ fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
++ if in_external_macro(cx.tcx.sess, local.span) {
++ return;
++ }
++
++ if_chain! {
++ if let PatKind::Wild = local.pat.kind;
++ if let Some(ref init) = local.init;
++ then {
++ let init_ty = cx.tables.expr_ty(init);
++ let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
++ GenericArgKind::Type(inner_ty) => {
++ SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
++ },
++
++ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
++ });
++ if contains_sync_guard {
++ span_lint_and_help(
++ cx,
++ LET_UNDERSCORE_LOCK,
++ local.span,
++ "non-binding let on a synchronization lock",
++ None,
++ "consider using an underscore-prefixed named \
++ binding or dropping explicitly with `std::mem::drop`"
++ )
++ } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
++ span_lint_and_help(
++ cx,
++ LET_UNDERSCORE_MUST_USE,
++ local.span,
++ "non-binding let on an expression with `#[must_use]` type",
++ None,
++ "consider explicitly using expression value"
++ )
++ } else if is_must_use_func_call(cx, init) {
++ span_lint_and_help(
++ cx,
++ LET_UNDERSCORE_MUST_USE,
++ local.span,
++ "non-binding let on a result of a `#[must_use]` function",
++ None,
++ "consider explicitly using function result"
++ )
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// error-pattern:cargo-clippy
++
++#![feature(box_syntax)]
++#![feature(box_patterns)]
++#![feature(or_patterns)]
++#![feature(rustc_private)]
++#![feature(stmt_expr_attributes)]
++#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
++#![recursion_limit = "512"]
++#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
++#![deny(rustc::internal)]
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![feature(crate_visibility_modifier)]
++#![feature(concat_idents)]
++
++// FIXME: switch to something more ergonomic here, once available.
++// (Currently there is no way to opt into sysroot crates without `extern crate`.)
++#[allow(unused_extern_crates)]
++extern crate fmt_macros;
++#[allow(unused_extern_crates)]
++extern crate rustc_ast;
++#[allow(unused_extern_crates)]
++extern crate rustc_ast_pretty;
++#[allow(unused_extern_crates)]
++extern crate rustc_attr;
++#[allow(unused_extern_crates)]
++extern crate rustc_data_structures;
++#[allow(unused_extern_crates)]
++extern crate rustc_driver;
++#[allow(unused_extern_crates)]
++extern crate rustc_errors;
++#[allow(unused_extern_crates)]
++extern crate rustc_hir;
++#[allow(unused_extern_crates)]
++extern crate rustc_hir_pretty;
++#[allow(unused_extern_crates)]
++extern crate rustc_index;
++#[allow(unused_extern_crates)]
++extern crate rustc_infer;
++#[allow(unused_extern_crates)]
++extern crate rustc_lexer;
++#[allow(unused_extern_crates)]
++extern crate rustc_lint;
++#[allow(unused_extern_crates)]
++extern crate rustc_middle;
++#[allow(unused_extern_crates)]
++extern crate rustc_mir;
++#[allow(unused_extern_crates)]
++extern crate rustc_parse;
++#[allow(unused_extern_crates)]
++extern crate rustc_session;
++#[allow(unused_extern_crates)]
++extern crate rustc_span;
++#[allow(unused_extern_crates)]
++extern crate rustc_target;
++#[allow(unused_extern_crates)]
++extern crate rustc_trait_selection;
++#[allow(unused_extern_crates)]
++extern crate rustc_typeck;
++
++use rustc_data_structures::fx::FxHashSet;
++use rustc_lint::LintId;
++use rustc_session::Session;
++
++/// Macro used to declare a Clippy lint.
++///
++/// Every lint declaration consists of 4 parts:
++///
++/// 1. The documentation, which is used for the website
++/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
++/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
++/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
++/// 4. The `description` that contains a short explanation on what's wrong with code where the
++/// lint is triggered.
++///
++/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default.
++/// As said in the README.md of this repository, if the lint level mapping changes, please update
++/// README.md.
++///
++/// # Example
++///
++/// ```
++/// # #![feature(rustc_private)]
++/// # #[allow(unused_extern_crates)]
++/// # extern crate rustc_middle;
++/// # #[allow(unused_extern_crates)]
++/// # extern crate rustc_session;
++/// # #[macro_use]
++/// # use clippy_lints::declare_clippy_lint;
++/// use rustc_session::declare_tool_lint;
++///
++/// declare_clippy_lint! {
++/// /// **What it does:** Checks for ... (describe what the lint matches).
++/// ///
++/// /// **Why is this bad?** Supply the reason for linting the code.
++/// ///
++/// /// **Known problems:** None. (Or describe where it could go wrong.)
++/// ///
++/// /// **Example:**
++/// ///
++/// /// ```rust
++/// /// // Bad
++/// /// Insert a short example of code that triggers the lint
++/// ///
++/// /// // Good
++/// /// Insert a short example of improved code that doesn't trigger the lint
++/// /// ```
++/// pub LINT_NAME,
++/// pedantic,
++/// "description"
++/// }
++/// ```
++/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++#[macro_export]
++macro_rules! declare_clippy_lint {
++ { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++ }
++ };
++ { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
++ declare_tool_lint! {
++ $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++ }
++ };
++}
++
++mod consts;
++#[macro_use]
++mod utils;
++
++// begin lints modules, do not remove this comment, it’s used in `update_lints`
++mod approx_const;
++mod arithmetic;
++mod as_conversions;
++mod assertions_on_constants;
++mod assign_ops;
++mod atomic_ordering;
++mod attrs;
++mod await_holding_lock;
++mod bit_mask;
++mod blacklisted_name;
++mod block_in_if_condition;
++mod booleans;
++mod bytecount;
++mod cargo_common_metadata;
++mod checked_conversions;
++mod cognitive_complexity;
++mod collapsible_if;
++mod comparison_chain;
++mod copies;
++mod copy_iterator;
++mod dbg_macro;
++mod default_trait_access;
++mod dereference;
++mod derive;
++mod doc;
++mod double_comparison;
++mod double_parens;
++mod drop_bounds;
++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 exit;
++mod explicit_write;
++mod fallible_impl_from;
++mod float_literal;
++mod floating_point_arithmetic;
++mod format;
++mod formatting;
++mod functions;
++mod future_not_send;
++mod get_last_with_len;
++mod identity_conversion;
++mod identity_op;
++mod if_let_mutex;
++mod if_let_some_result;
++mod if_not_else;
++mod implicit_return;
++mod implicit_saturating_sub;
++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 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 map_clone;
++mod map_unit_fn;
++mod match_on_vec_items;
++mod matches;
++mod mem_discriminant;
++mod mem_forget;
++mod mem_replace;
++mod methods;
++mod minmax;
++mod misc;
++mod misc_early;
++mod missing_const_for_fn;
++mod missing_doc;
++mod missing_inline;
++mod modulo_arithmetic;
++mod multiple_crate_versions;
++mod mut_key;
++mod mut_mut;
++mod mut_reference;
++mod mutable_debug_assertion;
++mod mutex_atomic;
++mod needless_bool;
++mod needless_borrow;
++mod needless_borrowed_ref;
++mod needless_continue;
++mod needless_pass_by_value;
++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 open_options;
++mod option_env_unwrap;
++mod overflow_check_conditional;
++mod panic_unimplemented;
++mod partialeq_ne_impl;
++mod path_buf_push_overwrite;
++mod precedence;
++mod ptr;
++mod ptr_offset_with_cast;
++mod question_mark;
++mod ranges;
++mod redundant_clone;
++mod redundant_field_names;
++mod redundant_pattern_matching;
++mod redundant_pub_crate;
++mod redundant_static_lifetimes;
++mod reference;
++mod regex;
++mod returns;
++mod serde_api;
++mod shadow;
++mod single_component_path_imports;
++mod slow_vector_initialization;
++mod strings;
++mod suspicious_trait_impl;
++mod swap;
++mod tabs_in_doc_comments;
++mod temporary_assignment;
++mod to_digit_is_some;
++mod trait_bounds;
++mod transmute;
++mod transmuting_null;
++mod trivially_copy_pass_by_ref;
++mod try_err;
++mod types;
++mod unicode;
++mod unnamed_address;
++mod unsafe_removed_from_name;
++mod unused_io_amount;
++mod unused_self;
++mod unwrap;
++mod use_self;
++mod vec;
++mod verbose_file_reads;
++mod wildcard_dependencies;
++mod wildcard_imports;
++mod write;
++mod zero_div_zero;
++// end lints modules, do not remove this comment, it’s used in `update_lints`
++
++pub use crate::utils::conf::Conf;
++
++mod reexport {
++ pub use rustc_ast::ast::Name;
++}
++
++/// 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, conf: &Conf) {
++ store.register_pre_expansion_pass(|| box write::Write::default());
++ store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames);
++ let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
++ store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames {
++ single_char_binding_names_threshold,
++ });
++ store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
++ store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
++}
++
++#[doc(hidden)]
++pub fn read_conf(args: &[rustc_ast::ast::NestedMetaItem], sess: &Session) -> Conf {
++ use std::path::Path;
++ match utils::conf::file_from_args(args) {
++ Ok(file_name) => {
++ // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
++ // do not require the file to exist
++ let file_name = match file_name {
++ Some(file_name) => file_name,
++ None => match utils::conf::lookup_conf_file() {
++ Ok(Some(path)) => path,
++ Ok(None) => return Conf::default(),
++ Err(error) => {
++ sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
++ .emit();
++ return Conf::default();
++ },
++ },
++ };
++
++ let file_name = if file_name.is_relative() {
++ sess.local_crate_source_file
++ .as_deref()
++ .and_then(Path::parent)
++ .unwrap_or_else(|| Path::new(""))
++ .join(file_name)
++ } else {
++ file_name
++ };
++
++ let (conf, errors) = utils::conf::read(&file_name);
++
++ // all conf errors are non-fatal, we just use the default conf in case of error
++ for error in errors {
++ sess.struct_err(&format!(
++ "error reading Clippy's configuration file `{}`: {}",
++ file_name.display(),
++ error
++ ))
++ .emit();
++ }
++
++ conf
++ },
++ Err((err, span)) => {
++ sess.struct_span_err(span, err)
++ .span_note(span, "Clippy will use default configuration")
++ .emit();
++ Conf::default()
++ },
++ }
++}
++
++/// 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::str_to_string",
++ "using `str::to_string` is common even today and specialization will likely happen soon",
++ );
++ store.register_removed(
++ "clippy::string_to_string",
++ "using `string::to_string` is common even today and specialization will likely happen soon",
++ );
++ 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::invalid_ref",
++ "superseded by rustc lint `invalid_value`",
++ );
++ 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::into_iter_on_array",
++ "this lint has been uplifted to rustc and is now called `array_into_iter`",
++ );
++ store.register_removed(
++ "clippy::unused_label",
++ "this lint has been uplifted to rustc and is now called `unused_labels`",
++ );
++ store.register_removed(
++ "clippy::replace_consts",
++ "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants",
++ );
++ // 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(&[
++ &approx_const::APPROX_CONSTANT,
++ &arithmetic::FLOAT_ARITHMETIC,
++ &arithmetic::INTEGER_ARITHMETIC,
++ &as_conversions::AS_CONVERSIONS,
++ &assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
++ &assign_ops::ASSIGN_OP_PATTERN,
++ &assign_ops::MISREFACTORED_ASSIGN_OP,
++ &atomic_ordering::INVALID_ATOMIC_ORDERING,
++ &attrs::DEPRECATED_CFG_ATTR,
++ &attrs::DEPRECATED_SEMVER,
++ &attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
++ &attrs::INLINE_ALWAYS,
++ &attrs::MISMATCHED_TARGET_OS,
++ &attrs::UNKNOWN_CLIPPY_LINTS,
++ &attrs::USELESS_ATTRIBUTE,
++ &await_holding_lock::AWAIT_HOLDING_LOCK,
++ &bit_mask::BAD_BIT_MASK,
++ &bit_mask::INEFFECTIVE_BIT_MASK,
++ &bit_mask::VERBOSE_BIT_MASK,
++ &blacklisted_name::BLACKLISTED_NAME,
++ &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,
++ &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
++ &booleans::LOGIC_BUG,
++ &booleans::NONMINIMAL_BOOL,
++ &bytecount::NAIVE_BYTECOUNT,
++ &cargo_common_metadata::CARGO_COMMON_METADATA,
++ &checked_conversions::CHECKED_CONVERSIONS,
++ &cognitive_complexity::COGNITIVE_COMPLEXITY,
++ &collapsible_if::COLLAPSIBLE_IF,
++ &comparison_chain::COMPARISON_CHAIN,
++ &copies::IFS_SAME_COND,
++ &copies::IF_SAME_THEN_ELSE,
++ &copies::MATCH_SAME_ARMS,
++ &copies::SAME_FUNCTIONS_IN_IF_CONDITION,
++ ©_iterator::COPY_ITERATOR,
++ &dbg_macro::DBG_MACRO,
++ &default_trait_access::DEFAULT_TRAIT_ACCESS,
++ &dereference::EXPLICIT_DEREF_METHODS,
++ &derive::DERIVE_HASH_XOR_EQ,
++ &derive::EXPL_IMPL_CLONE_ON_COPY,
++ &derive::UNSAFE_DERIVE_DESERIALIZE,
++ &doc::DOC_MARKDOWN,
++ &doc::MISSING_ERRORS_DOC,
++ &doc::MISSING_SAFETY_DOC,
++ &doc::NEEDLESS_DOCTEST_MAIN,
++ &double_comparison::DOUBLE_COMPARISONS,
++ &double_parens::DOUBLE_PARENS,
++ &drop_bounds::DROP_BOUNDS,
++ &drop_forget_ref::DROP_COPY,
++ &drop_forget_ref::DROP_REF,
++ &drop_forget_ref::FORGET_COPY,
++ &drop_forget_ref::FORGET_REF,
++ &duration_subsec::DURATION_SUBSEC,
++ &else_if_without_else::ELSE_IF_WITHOUT_ELSE,
++ &empty_enum::EMPTY_ENUM,
++ &entry::MAP_ENTRY,
++ &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
++ &enum_variants::ENUM_VARIANT_NAMES,
++ &enum_variants::MODULE_INCEPTION,
++ &enum_variants::MODULE_NAME_REPETITIONS,
++ &enum_variants::PUB_ENUM_VARIANT_NAMES,
++ &eq_op::EQ_OP,
++ &eq_op::OP_REF,
++ &erasing_op::ERASING_OP,
++ &escape::BOXED_LOCAL,
++ &eta_reduction::REDUNDANT_CLOSURE,
++ &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++ &eval_order_dependence::DIVERGING_SUB_EXPRESSION,
++ &eval_order_dependence::EVAL_ORDER_DEPENDENCE,
++ &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
++ &excessive_bools::STRUCT_EXCESSIVE_BOOLS,
++ &exit::EXIT,
++ &explicit_write::EXPLICIT_WRITE,
++ &fallible_impl_from::FALLIBLE_IMPL_FROM,
++ &float_literal::EXCESSIVE_PRECISION,
++ &float_literal::LOSSY_FLOAT_LITERAL,
++ &floating_point_arithmetic::IMPRECISE_FLOPS,
++ &floating_point_arithmetic::SUBOPTIMAL_FLOPS,
++ &format::USELESS_FORMAT,
++ &formatting::POSSIBLE_MISSING_COMMA,
++ &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
++ &formatting::SUSPICIOUS_ELSE_FORMATTING,
++ &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
++ &functions::DOUBLE_MUST_USE,
++ &functions::MUST_USE_CANDIDATE,
++ &functions::MUST_USE_UNIT,
++ &functions::NOT_UNSAFE_PTR_ARG_DEREF,
++ &functions::TOO_MANY_ARGUMENTS,
++ &functions::TOO_MANY_LINES,
++ &future_not_send::FUTURE_NOT_SEND,
++ &get_last_with_len::GET_LAST_WITH_LEN,
++ &identity_conversion::IDENTITY_CONVERSION,
++ &identity_op::IDENTITY_OP,
++ &if_let_mutex::IF_LET_MUTEX,
++ &if_let_some_result::IF_LET_SOME_RESULT,
++ &if_not_else::IF_NOT_ELSE,
++ &implicit_return::IMPLICIT_RETURN,
++ &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
++ &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,
++ &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::LEN_WITHOUT_IS_EMPTY,
++ &len_zero::LEN_ZERO,
++ &let_if_seq::USELESS_LET_IF_SEQ,
++ &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,
++ &loops::EMPTY_LOOP,
++ &loops::EXPLICIT_COUNTER_LOOP,
++ &loops::EXPLICIT_INTO_ITER_LOOP,
++ &loops::EXPLICIT_ITER_LOOP,
++ &loops::FOR_KV_MAP,
++ &loops::FOR_LOOP_OVER_OPTION,
++ &loops::FOR_LOOP_OVER_RESULT,
++ &loops::ITER_NEXT_LOOP,
++ &loops::MANUAL_MEMCPY,
++ &loops::MUT_RANGE_BOUND,
++ &loops::NEEDLESS_COLLECT,
++ &loops::NEEDLESS_RANGE_LOOP,
++ &loops::NEVER_LOOP,
++ &loops::REVERSE_RANGE_LOOP,
++ &loops::WHILE_IMMUTABLE_CONDITION,
++ &loops::WHILE_LET_LOOP,
++ &loops::WHILE_LET_ON_ITERATOR,
++ ¯o_use::MACRO_USE_IMPORTS,
++ &main_recursion::MAIN_RECURSION,
++ &map_clone::MAP_CLONE,
++ &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_OVERLAPPING_ARM,
++ &matches::MATCH_REF_PATS,
++ &matches::MATCH_SINGLE_BINDING,
++ &matches::MATCH_WILD_ERR_ARM,
++ &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::CHARS_LAST_CMP,
++ &methods::CHARS_NEXT_CMP,
++ &methods::CLONE_DOUBLE_REF,
++ &methods::CLONE_ON_COPY,
++ &methods::CLONE_ON_REF_PTR,
++ &methods::EXPECT_FUN_CALL,
++ &methods::FILETYPE_IS_FILE,
++ &methods::FILTER_MAP,
++ &methods::FILTER_MAP_NEXT,
++ &methods::FILTER_NEXT,
++ &methods::FIND_MAP,
++ &methods::FLAT_MAP_IDENTITY,
++ &methods::GET_UNWRAP,
++ &methods::INEFFICIENT_TO_STRING,
++ &methods::INTO_ITER_ON_REF,
++ &methods::ITERATOR_STEP_BY_ZERO,
++ &methods::ITER_CLONED_COLLECT,
++ &methods::ITER_NTH,
++ &methods::ITER_NTH_ZERO,
++ &methods::ITER_SKIP_NEXT,
++ &methods::MANUAL_SATURATING_ARITHMETIC,
++ &methods::MAP_FLATTEN,
++ &methods::NEW_RET_NO_SELF,
++ &methods::OK_EXPECT,
++ &methods::OPTION_AND_THEN_SOME,
++ &methods::OPTION_AS_REF_DEREF,
++ &methods::OPTION_EXPECT_USED,
++ &methods::OPTION_MAP_OR_NONE,
++ &methods::OPTION_MAP_UNWRAP_OR,
++ &methods::OPTION_MAP_UNWRAP_OR_ELSE,
++ &methods::OPTION_UNWRAP_USED,
++ &methods::OR_FUN_CALL,
++ &methods::RESULT_EXPECT_USED,
++ &methods::RESULT_MAP_OR_INTO_OPTION,
++ &methods::RESULT_MAP_UNWRAP_OR_ELSE,
++ &methods::RESULT_UNWRAP_USED,
++ &methods::SEARCH_IS_SOME,
++ &methods::SHOULD_IMPLEMENT_TRAIT,
++ &methods::SINGLE_CHAR_PATTERN,
++ &methods::SKIP_WHILE_NEXT,
++ &methods::STRING_EXTEND_CHARS,
++ &methods::SUSPICIOUS_MAP,
++ &methods::TEMPORARY_CSTRING_AS_PTR,
++ &methods::UNINIT_ASSUMED_INIT,
++ &methods::UNNECESSARY_FILTER_MAP,
++ &methods::UNNECESSARY_FOLD,
++ &methods::USELESS_ASREF,
++ &methods::WRONG_PUB_SELF_CONVENTION,
++ &methods::WRONG_SELF_CONVENTION,
++ &methods::ZST_OFFSET,
++ &minmax::MIN_MAX,
++ &misc::CMP_NAN,
++ &misc::CMP_OWNED,
++ &misc::FLOAT_CMP,
++ &misc::FLOAT_CMP_CONST,
++ &misc::MODULO_ONE,
++ &misc::SHORT_CIRCUIT_STATEMENT,
++ &misc::TOPLEVEL_REF_ARG,
++ &misc::USED_UNDERSCORE_BINDING,
++ &misc::ZERO_PTR,
++ &misc_early::BUILTIN_TYPE_SHADOW,
++ &misc_early::DOUBLE_NEG,
++ &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
++ &misc_early::MIXED_CASE_HEX_LITERALS,
++ &misc_early::REDUNDANT_CLOSURE_CALL,
++ &misc_early::REDUNDANT_PATTERN,
++ &misc_early::UNNEEDED_FIELD_PATTERN,
++ &misc_early::UNNEEDED_WILDCARD_PATTERN,
++ &misc_early::UNSEPARATED_LITERAL_SUFFIX,
++ &misc_early::ZERO_PREFIXED_LITERAL,
++ &missing_const_for_fn::MISSING_CONST_FOR_FN,
++ &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
++ &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
++ &modulo_arithmetic::MODULO_ARITHMETIC,
++ &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
++ &mut_key::MUTABLE_KEY_TYPE,
++ &mut_mut::MUT_MUT,
++ &mut_reference::UNNECESSARY_MUT_PASSED,
++ &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
++ &mutex_atomic::MUTEX_ATOMIC,
++ &mutex_atomic::MUTEX_INTEGER,
++ &needless_bool::BOOL_COMPARISON,
++ &needless_bool::NEEDLESS_BOOL,
++ &needless_borrow::NEEDLESS_BORROW,
++ &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
++ &needless_continue::NEEDLESS_CONTINUE,
++ &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
++ &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,
++ &open_options::NONSENSICAL_OPEN_OPTIONS,
++ &option_env_unwrap::OPTION_ENV_UNWRAP,
++ &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
++ &panic_unimplemented::PANIC,
++ &panic_unimplemented::PANIC_PARAMS,
++ &panic_unimplemented::TODO,
++ &panic_unimplemented::UNIMPLEMENTED,
++ &panic_unimplemented::UNREACHABLE,
++ &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
++ &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
++ &precedence::PRECEDENCE,
++ &ptr::CMP_NULL,
++ &ptr::MUT_FROM_REF,
++ &ptr::PTR_ARG,
++ &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
++ &question_mark::QUESTION_MARK,
++ &ranges::RANGE_MINUS_ONE,
++ &ranges::RANGE_PLUS_ONE,
++ &ranges::RANGE_ZIP_WITH_LEN,
++ &redundant_clone::REDUNDANT_CLONE,
++ &redundant_field_names::REDUNDANT_FIELD_NAMES,
++ &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
++ &redundant_pub_crate::REDUNDANT_PUB_CRATE,
++ &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
++ &reference::DEREF_ADDROF,
++ &reference::REF_IN_DEREF,
++ ®ex::INVALID_REGEX,
++ ®ex::REGEX_MACRO,
++ ®ex::TRIVIAL_REGEX,
++ &returns::LET_AND_RETURN,
++ &returns::NEEDLESS_RETURN,
++ &returns::UNUSED_UNIT,
++ &serde_api::SERDE_API_MISUSE,
++ &shadow::SHADOW_REUSE,
++ &shadow::SHADOW_SAME,
++ &shadow::SHADOW_UNRELATED,
++ &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
++ &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
++ &strings::STRING_ADD,
++ &strings::STRING_ADD_ASSIGN,
++ &strings::STRING_LIT_AS_BYTES,
++ &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,
++ &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
++ &transmute::CROSSPOINTER_TRANSMUTE,
++ &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,
++ &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
++ &try_err::TRY_ERR,
++ &types::ABSURD_EXTREME_COMPARISONS,
++ &types::BORROWED_BOX,
++ &types::BOX_VEC,
++ &types::CAST_LOSSLESS,
++ &types::CAST_POSSIBLE_TRUNCATION,
++ &types::CAST_POSSIBLE_WRAP,
++ &types::CAST_PRECISION_LOSS,
++ &types::CAST_PTR_ALIGNMENT,
++ &types::CAST_REF_TO_MUT,
++ &types::CAST_SIGN_LOSS,
++ &types::CHAR_LIT_AS_U8,
++ &types::FN_TO_NUMERIC_CAST,
++ &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++ &types::IMPLICIT_HASHER,
++ &types::INVALID_UPCAST_COMPARISONS,
++ &types::LET_UNIT_VALUE,
++ &types::LINKEDLIST,
++ &types::OPTION_OPTION,
++ &types::REDUNDANT_ALLOCATION,
++ &types::TYPE_COMPLEXITY,
++ &types::UNIT_ARG,
++ &types::UNIT_CMP,
++ &types::UNNECESSARY_CAST,
++ &types::VEC_BOX,
++ &unicode::NON_ASCII_LITERAL,
++ &unicode::UNICODE_NOT_NFC,
++ &unicode::ZERO_WIDTH_SPACE,
++ &unnamed_address::FN_ADDRESS_COMPARISONS,
++ &unnamed_address::VTABLE_ADDRESS_COMPARISONS,
++ &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
++ &unused_io_amount::UNUSED_IO_AMOUNT,
++ &unused_self::UNUSED_SELF,
++ &unwrap::PANICKING_UNWRAP,
++ &unwrap::UNNECESSARY_UNWRAP,
++ &use_self::USE_SELF,
++ &utils::internal_lints::CLIPPY_LINTS_INTERNAL,
++ &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
++ &utils::internal_lints::COMPILER_LINT_FUNCTIONS,
++ &utils::internal_lints::DEFAULT_LINT,
++ &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
++ &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
++ &utils::internal_lints::PRODUCE_ICE,
++ &vec::USELESS_VEC,
++ &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_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,
++ ]);
++ // end register lints, do not remove this comment, it’s used in `update_lints`
++
++ store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock);
++ store.register_late_pass(|| box serde_api::SerdeAPI);
++ store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
++ store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
++ store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
++ store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
++ store.register_late_pass(|| box utils::author::Author);
++ let vec_box_size_threshold = conf.vec_box_size_threshold;
++ store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
++ store.register_late_pass(|| box booleans::NonminimalBool);
++ store.register_late_pass(|| box eq_op::EqOp);
++ store.register_late_pass(|| box enum_clike::UnportableVariant);
++ store.register_late_pass(|| box float_literal::FloatLiteral);
++ let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
++ store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
++ store.register_late_pass(|| box ptr::Ptr);
++ store.register_late_pass(|| box needless_bool::NeedlessBool);
++ store.register_late_pass(|| box needless_bool::BoolComparison);
++ 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 block_in_if_condition::BlockInIfCondition);
++ store.register_late_pass(|| box unicode::Unicode);
++ 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 methods::Methods);
++ store.register_late_pass(|| box map_clone::MapClone);
++ store.register_late_pass(|| box shadow::Shadow);
++ store.register_late_pass(|| box types::LetUnitValue);
++ store.register_late_pass(|| box types::UnitCmp);
++ 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 ranges::Ranges);
++ store.register_late_pass(|| box types::Casts);
++ let type_complexity_threshold = conf.type_complexity_threshold;
++ store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
++ store.register_late_pass(|| box matches::Matches::default());
++ 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(|| box panic_unimplemented::PanicUnimplemented);
++ store.register_late_pass(|| box strings::StringLitAsBytes);
++ store.register_late_pass(|| box derive::Derive);
++ store.register_late_pass(|| box types::CharLitAsU8);
++ store.register_late_pass(|| box vec::UselessVec);
++ store.register_late_pass(|| box drop_bounds::DropBounds);
++ 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 types::AbsurdExtremeComparisons);
++ store.register_late_pass(|| box types::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_threshold1 = conf.too_many_arguments_threshold;
++ let too_many_lines_threshold2 = conf.too_many_lines_threshold;
++ store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2));
++ 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 mem_replace::MemReplace);
++ 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(|| box if_let_some_result::OkIfLet);
++ store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
++ 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 trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new(
++ conf.trivial_copy_size_limit,
++ &sess.target,
++ );
++ store.register_late_pass(move || box trivially_copy_pass_by_ref);
++ store.register_late_pass(|| box try_err::TryErr);
++ store.register_late_pass(|| box use_self::UseSelf);
++ 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 identity_conversion::IdentityConversion::default());
++ store.register_late_pass(|| box types::ImplicitHasher);
++ store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
++ store.register_late_pass(|| box types::UnitArg);
++ store.register_late_pass(|| box double_comparison::DoubleComparisons);
++ store.register_late_pass(|| box question_mark::QuestionMark);
++ store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
++ store.register_late_pass(|| box map_unit_fn::MapUnit);
++ store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
++ store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
++ store.register_late_pass(|| box unwrap::Unwrap);
++ store.register_late_pass(|| box duration_subsec::DurationSubsec);
++ store.register_late_pass(|| box default_trait_access::DefaultTraitAccess);
++ 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 types::RefToMut);
++ store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
++ store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
++ store.register_late_pass(|| box transmuting_null::TransmutingNull);
++ store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
++ store.register_late_pass(|| box checked_conversions::CheckedConversions);
++ store.register_late_pass(|| box integer_division::IntegerDivision);
++ store.register_late_pass(|| box inherent_to_string::InherentToString);
++ store.register_late_pass(|| box trait_bounds::TraitBounds);
++ 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_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 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_static_lifetimes::RedundantStaticLifetimes);
++ store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
++ store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
++ store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
++ store.register_early_pass(|| box literal_representation::LiteralDigitGrouping);
++ let literal_representation_threshold = conf.literal_representation_threshold;
++ store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
++ store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
++ let enum_variant_name_threshold = conf.enum_variant_name_threshold;
++ store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
++ store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
++ 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(move || box floating_point_arithmetic::FloatingPointArithmetic);
++ store.register_early_pass(|| box as_conversions::AsConversions);
++ store.register_early_pass(|| box utils::internal_lints::ProduceIce);
++ 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);
++ store.register_late_pass(|| box wildcard_imports::WildcardImports);
++ store.register_early_pass(|| box macro_use::MacroUseImports);
++ 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);
++ store.register_late_pass(|| box future_not_send::FutureNotSend);
++ store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
++ store.register_late_pass(|| box if_let_mutex::IfLetMutex);
++ store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
++
++ 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(&dbg_macro::DBG_MACRO),
++ LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
++ LintId::of(&exit::EXIT),
++ LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
++ 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(&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::FILETYPE_IS_FILE),
++ LintId::of(&methods::GET_UNWRAP),
++ LintId::of(&methods::OPTION_EXPECT_USED),
++ LintId::of(&methods::OPTION_UNWRAP_USED),
++ LintId::of(&methods::RESULT_EXPECT_USED),
++ LintId::of(&methods::RESULT_UNWRAP_USED),
++ LintId::of(&methods::WRONG_PUB_SELF_CONVENTION),
++ LintId::of(&misc::FLOAT_CMP_CONST),
++ LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN),
++ LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
++ LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
++ LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
++ LintId::of(&panic_unimplemented::PANIC),
++ LintId::of(&panic_unimplemented::TODO),
++ LintId::of(&panic_unimplemented::UNIMPLEMENTED),
++ LintId::of(&panic_unimplemented::UNREACHABLE),
++ LintId::of(&shadow::SHADOW_REUSE),
++ LintId::of(&shadow::SHADOW_SAME),
++ LintId::of(&strings::STRING_ADD),
++ LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
++ 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_lock::AWAIT_HOLDING_LOCK),
++ LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
++ LintId::of(&copies::MATCH_SAME_ARMS),
++ LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
++ LintId::of(©_iterator::COPY_ITERATOR),
++ LintId::of(&default_trait_access::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(&empty_enum::EMPTY_ENUM),
++ LintId::of(&enum_variants::MODULE_NAME_REPETITIONS),
++ LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES),
++ LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
++ LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
++ LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS),
++ LintId::of(&functions::MUST_USE_CANDIDATE),
++ LintId::of(&functions::TOO_MANY_LINES),
++ LintId::of(&if_not_else::IF_NOT_ELSE),
++ LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
++ LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
++ LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
++ LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
++ 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(¯o_use::MACRO_USE_IMPORTS),
++ LintId::of(&matches::MATCH_BOOL),
++ LintId::of(&matches::SINGLE_MATCH_ELSE),
++ LintId::of(&methods::FILTER_MAP),
++ LintId::of(&methods::FILTER_MAP_NEXT),
++ LintId::of(&methods::FIND_MAP),
++ LintId::of(&methods::INEFFICIENT_TO_STRING),
++ LintId::of(&methods::MAP_FLATTEN),
++ LintId::of(&methods::OPTION_MAP_UNWRAP_OR),
++ LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE),
++ LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE),
++ LintId::of(&misc::USED_UNDERSCORE_BINDING),
++ LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX),
++ LintId::of(&mut_mut::MUT_MUT),
++ LintId::of(&needless_continue::NEEDLESS_CONTINUE),
++ LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
++ LintId::of(&non_expressive_names::SIMILAR_NAMES),
++ LintId::of(&ranges::RANGE_PLUS_ONE),
++ LintId::of(&shadow::SHADOW_UNRELATED),
++ LintId::of(&strings::STRING_ADD_ASSIGN),
++ LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
++ LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
++ LintId::of(&types::CAST_LOSSLESS),
++ LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
++ LintId::of(&types::CAST_POSSIBLE_WRAP),
++ LintId::of(&types::CAST_PRECISION_LOSS),
++ LintId::of(&types::CAST_SIGN_LOSS),
++ LintId::of(&types::IMPLICIT_HASHER),
++ LintId::of(&types::INVALID_UPCAST_COMPARISONS),
++ LintId::of(&types::LET_UNIT_VALUE),
++ LintId::of(&types::LINKEDLIST),
++ LintId::of(&types::OPTION_OPTION),
++ LintId::of(&unicode::NON_ASCII_LITERAL),
++ LintId::of(&unicode::UNICODE_NOT_NFC),
++ LintId::of(&unused_self::UNUSED_SELF),
++ LintId::of(&wildcard_imports::ENUM_GLOB_USE),
++ LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
++ ]);
++
++ 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::LINT_WITHOUT_LINT_PASS),
++ LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
++ LintId::of(&utils::internal_lints::PRODUCE_ICE),
++ ]);
++
++ store.register_group(true, "clippy::all", Some("clippy"), vec![
++ 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(&atomic_ordering::INVALID_ATOMIC_ORDERING),
++ LintId::of(&attrs::DEPRECATED_CFG_ATTR),
++ LintId::of(&attrs::DEPRECATED_SEMVER),
++ LintId::of(&attrs::MISMATCHED_TARGET_OS),
++ LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
++ LintId::of(&attrs::USELESS_ATTRIBUTE),
++ LintId::of(&bit_mask::BAD_BIT_MASK),
++ LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
++ LintId::of(&bit_mask::VERBOSE_BIT_MASK),
++ LintId::of(&blacklisted_name::BLACKLISTED_NAME),
++ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
++ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
++ LintId::of(&booleans::LOGIC_BUG),
++ LintId::of(&booleans::NONMINIMAL_BOOL),
++ LintId::of(&bytecount::NAIVE_BYTECOUNT),
++ LintId::of(&collapsible_if::COLLAPSIBLE_IF),
++ LintId::of(&comparison_chain::COMPARISON_CHAIN),
++ LintId::of(&copies::IFS_SAME_COND),
++ LintId::of(&copies::IF_SAME_THEN_ELSE),
++ LintId::of(&derive::DERIVE_HASH_XOR_EQ),
++ 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_bounds::DROP_BOUNDS),
++ 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_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(&functions::DOUBLE_MUST_USE),
++ LintId::of(&functions::MUST_USE_UNIT),
++ LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
++ LintId::of(&functions::TOO_MANY_ARGUMENTS),
++ LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
++ LintId::of(&identity_conversion::IDENTITY_CONVERSION),
++ 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::LEN_WITHOUT_IS_EMPTY),
++ LintId::of(&len_zero::LEN_ZERO),
++ LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
++ 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(&loops::EMPTY_LOOP),
++ LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
++ LintId::of(&loops::FOR_KV_MAP),
++ LintId::of(&loops::FOR_LOOP_OVER_OPTION),
++ LintId::of(&loops::FOR_LOOP_OVER_RESULT),
++ LintId::of(&loops::ITER_NEXT_LOOP),
++ 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::REVERSE_RANGE_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(&map_clone::MAP_CLONE),
++ LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
++ LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
++ LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
++ LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
++ LintId::of(&matches::MATCH_AS_REF),
++ LintId::of(&matches::MATCH_OVERLAPPING_ARM),
++ LintId::of(&matches::MATCH_REF_PATS),
++ LintId::of(&matches::MATCH_SINGLE_BINDING),
++ LintId::of(&matches::MATCH_WILD_ERR_ARM),
++ 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::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_NEXT),
++ LintId::of(&methods::FLAT_MAP_IDENTITY),
++ LintId::of(&methods::INTO_ITER_ON_REF),
++ LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
++ LintId::of(&methods::ITER_CLONED_COLLECT),
++ LintId::of(&methods::ITER_NTH),
++ LintId::of(&methods::ITER_NTH_ZERO),
++ LintId::of(&methods::ITER_SKIP_NEXT),
++ LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
++ LintId::of(&methods::NEW_RET_NO_SELF),
++ LintId::of(&methods::OK_EXPECT),
++ LintId::of(&methods::OPTION_AND_THEN_SOME),
++ LintId::of(&methods::OPTION_AS_REF_DEREF),
++ 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_PATTERN),
++ LintId::of(&methods::SKIP_WHILE_NEXT),
++ LintId::of(&methods::STRING_EXTEND_CHARS),
++ LintId::of(&methods::SUSPICIOUS_MAP),
++ LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
++ LintId::of(&methods::UNINIT_ASSUMED_INIT),
++ LintId::of(&methods::UNNECESSARY_FILTER_MAP),
++ LintId::of(&methods::UNNECESSARY_FOLD),
++ 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_CLOSURE_CALL),
++ 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_reference::UNNECESSARY_MUT_PASSED),
++ LintId::of(&mutex_atomic::MUTEX_ATOMIC),
++ LintId::of(&needless_bool::BOOL_COMPARISON),
++ LintId::of(&needless_bool::NEEDLESS_BOOL),
++ LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++ 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(&open_options::NONSENSICAL_OPEN_OPTIONS),
++ LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
++ LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
++ LintId::of(&panic_unimplemented::PANIC_PARAMS),
++ LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
++ LintId::of(&precedence::PRECEDENCE),
++ LintId::of(&ptr::CMP_NULL),
++ LintId::of(&ptr::MUT_FROM_REF),
++ LintId::of(&ptr::PTR_ARG),
++ LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
++ LintId::of(&question_mark::QUESTION_MARK),
++ LintId::of(&ranges::RANGE_MINUS_ONE),
++ LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
++ LintId::of(&redundant_clone::REDUNDANT_CLONE),
++ LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
++ LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
++ LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++ LintId::of(&reference::DEREF_ADDROF),
++ LintId::of(&reference::REF_IN_DEREF),
++ LintId::of(®ex::INVALID_REGEX),
++ LintId::of(®ex::REGEX_MACRO),
++ LintId::of(®ex::TRIVIAL_REGEX),
++ LintId::of(&returns::LET_AND_RETURN),
++ LintId::of(&returns::NEEDLESS_RETURN),
++ LintId::of(&returns::UNUSED_UNIT),
++ LintId::of(&serde_api::SERDE_API_MISUSE),
++ LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++ LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
++ LintId::of(&strings::STRING_LIT_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(&transmute::CROSSPOINTER_TRANSMUTE),
++ 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_PTR),
++ 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::ABSURD_EXTREME_COMPARISONS),
++ LintId::of(&types::BORROWED_BOX),
++ LintId::of(&types::BOX_VEC),
++ LintId::of(&types::CAST_PTR_ALIGNMENT),
++ LintId::of(&types::CAST_REF_TO_MUT),
++ LintId::of(&types::CHAR_LIT_AS_U8),
++ LintId::of(&types::FN_TO_NUMERIC_CAST),
++ LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++ LintId::of(&types::REDUNDANT_ALLOCATION),
++ LintId::of(&types::TYPE_COMPLEXITY),
++ LintId::of(&types::UNIT_ARG),
++ LintId::of(&types::UNIT_CMP),
++ LintId::of(&types::UNNECESSARY_CAST),
++ LintId::of(&types::VEC_BOX),
++ LintId::of(&unicode::ZERO_WIDTH_SPACE),
++ LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
++ LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++ LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++ LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
++ LintId::of(&unwrap::PANICKING_UNWRAP),
++ LintId::of(&unwrap::UNNECESSARY_UNWRAP),
++ LintId::of(&vec::USELESS_VEC),
++ LintId::of(&write::PRINTLN_EMPTY_STRING),
++ LintId::of(&write::PRINT_LITERAL),
++ LintId::of(&write::PRINT_WITH_NEWLINE),
++ LintId::of(&write::WRITELN_EMPTY_STRING),
++ LintId::of(&write::WRITE_LITERAL),
++ LintId::of(&write::WRITE_WITH_NEWLINE),
++ LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
++ ]);
++
++ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
++ LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
++ LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
++ LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
++ LintId::of(&bit_mask::VERBOSE_BIT_MASK),
++ LintId::of(&blacklisted_name::BLACKLISTED_NAME),
++ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
++ LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
++ LintId::of(&collapsible_if::COLLAPSIBLE_IF),
++ LintId::of(&comparison_chain::COMPARISON_CHAIN),
++ LintId::of(&doc::MISSING_SAFETY_DOC),
++ LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
++ LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
++ LintId::of(&enum_variants::MODULE_INCEPTION),
++ LintId::of(&eq_op::OP_REF),
++ LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
++ LintId::of(&float_literal::EXCESSIVE_PRECISION),
++ LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
++ LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
++ LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
++ LintId::of(&functions::DOUBLE_MUST_USE),
++ LintId::of(&functions::MUST_USE_UNIT),
++ LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
++ LintId::of(&inherent_to_string::INHERENT_TO_STRING),
++ LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
++ LintId::of(&len_zero::LEN_ZERO),
++ LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
++ LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
++ LintId::of(&loops::EMPTY_LOOP),
++ LintId::of(&loops::FOR_KV_MAP),
++ LintId::of(&loops::NEEDLESS_RANGE_LOOP),
++ LintId::of(&loops::WHILE_LET_ON_ITERATOR),
++ LintId::of(&main_recursion::MAIN_RECURSION),
++ LintId::of(&map_clone::MAP_CLONE),
++ LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
++ LintId::of(&matches::MATCH_OVERLAPPING_ARM),
++ LintId::of(&matches::MATCH_REF_PATS),
++ LintId::of(&matches::MATCH_WILD_ERR_ARM),
++ 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::CHARS_LAST_CMP),
++ LintId::of(&methods::CHARS_NEXT_CMP),
++ LintId::of(&methods::INTO_ITER_ON_REF),
++ LintId::of(&methods::ITER_CLONED_COLLECT),
++ LintId::of(&methods::ITER_NTH_ZERO),
++ LintId::of(&methods::ITER_SKIP_NEXT),
++ LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
++ 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::STRING_EXTEND_CHARS),
++ LintId::of(&methods::UNNECESSARY_FOLD),
++ LintId::of(&methods::WRONG_SELF_CONVENTION),
++ LintId::of(&misc::TOPLEVEL_REF_ARG),
++ LintId::of(&misc::ZERO_PTR),
++ LintId::of(&misc_early::BUILTIN_TYPE_SHADOW),
++ LintId::of(&misc_early::DOUBLE_NEG),
++ LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
++ LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
++ LintId::of(&misc_early::REDUNDANT_PATTERN),
++ LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
++ LintId::of(&neg_multiply::NEG_MULTIPLY),
++ LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
++ LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
++ LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++ LintId::of(&panic_unimplemented::PANIC_PARAMS),
++ LintId::of(&ptr::CMP_NULL),
++ LintId::of(&ptr::PTR_ARG),
++ LintId::of(&question_mark::QUESTION_MARK),
++ LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
++ LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
++ LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++ LintId::of(®ex::REGEX_MACRO),
++ LintId::of(®ex::TRIVIAL_REGEX),
++ LintId::of(&returns::LET_AND_RETURN),
++ LintId::of(&returns::NEEDLESS_RETURN),
++ LintId::of(&returns::UNUSED_UNIT),
++ LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++ LintId::of(&strings::STRING_LIT_AS_BYTES),
++ 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(&types::FN_TO_NUMERIC_CAST),
++ LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++ LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++ LintId::of(&write::PRINTLN_EMPTY_STRING),
++ LintId::of(&write::PRINT_LITERAL),
++ LintId::of(&write::PRINT_WITH_NEWLINE),
++ LintId::of(&write::WRITELN_EMPTY_STRING),
++ LintId::of(&write::WRITE_LITERAL),
++ LintId::of(&write::WRITE_WITH_NEWLINE),
++ ]);
++
++ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
++ LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
++ LintId::of(&attrs::DEPRECATED_CFG_ATTR),
++ LintId::of(&booleans::NONMINIMAL_BOOL),
++ LintId::of(&double_comparison::DOUBLE_COMPARISONS),
++ LintId::of(&double_parens::DOUBLE_PARENS),
++ LintId::of(&duration_subsec::DURATION_SUBSEC),
++ LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
++ LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
++ LintId::of(&explicit_write::EXPLICIT_WRITE),
++ LintId::of(&format::USELESS_FORMAT),
++ LintId::of(&functions::TOO_MANY_ARGUMENTS),
++ LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
++ LintId::of(&identity_conversion::IDENTITY_CONVERSION),
++ 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::MUT_RANGE_BOUND),
++ LintId::of(&loops::WHILE_LET_LOOP),
++ 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::CLONE_ON_COPY),
++ LintId::of(&methods::FILTER_NEXT),
++ LintId::of(&methods::FLAT_MAP_IDENTITY),
++ LintId::of(&methods::OPTION_AND_THEN_SOME),
++ LintId::of(&methods::OPTION_AS_REF_DEREF),
++ LintId::of(&methods::SEARCH_IS_SOME),
++ LintId::of(&methods::SKIP_WHILE_NEXT),
++ LintId::of(&methods::SUSPICIOUS_MAP),
++ LintId::of(&methods::UNNECESSARY_FILTER_MAP),
++ LintId::of(&methods::USELESS_ASREF),
++ LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
++ LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL),
++ LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
++ LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
++ LintId::of(&needless_bool::BOOL_COMPARISON),
++ LintId::of(&needless_bool::NEEDLESS_BOOL),
++ LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++ 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_MINUS_ONE),
++ LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
++ LintId::of(&reference::DEREF_ADDROF),
++ LintId::of(&reference::REF_IN_DEREF),
++ LintId::of(&swap::MANUAL_SWAP),
++ LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
++ LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
++ 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_PTR),
++ LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
++ LintId::of(&types::BORROWED_BOX),
++ LintId::of(&types::CHAR_LIT_AS_U8),
++ LintId::of(&types::TYPE_COMPLEXITY),
++ LintId::of(&types::UNIT_ARG),
++ LintId::of(&types::UNNECESSARY_CAST),
++ LintId::of(&types::VEC_BOX),
++ LintId::of(&unwrap::UNNECESSARY_UNWRAP),
++ LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
++ ]);
++
++ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
++ LintId::of(&approx_const::APPROX_CONSTANT),
++ 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(&copies::IFS_SAME_COND),
++ LintId::of(&copies::IF_SAME_THEN_ELSE),
++ LintId::of(&derive::DERIVE_HASH_XOR_EQ),
++ LintId::of(&drop_bounds::DROP_BOUNDS),
++ LintId::of(&drop_forget_ref::DROP_COPY),
++ LintId::of(&drop_forget_ref::DROP_REF),
++ LintId::of(&drop_forget_ref::FORGET_COPY),
++ LintId::of(&drop_forget_ref::FORGET_REF),
++ LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
++ LintId::of(&eq_op::EQ_OP),
++ LintId::of(&erasing_op::ERASING_OP),
++ LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
++ LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
++ LintId::of(&if_let_mutex::IF_LET_MUTEX),
++ LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
++ LintId::of(&infinite_iter::INFINITE_ITER),
++ LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
++ LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
++ LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
++ LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
++ LintId::of(&loops::FOR_LOOP_OVER_OPTION),
++ LintId::of(&loops::FOR_LOOP_OVER_RESULT),
++ LintId::of(&loops::ITER_NEXT_LOOP),
++ LintId::of(&loops::NEVER_LOOP),
++ LintId::of(&loops::REVERSE_RANGE_LOOP),
++ LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
++ LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
++ 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::TEMPORARY_CSTRING_AS_PTR),
++ LintId::of(&methods::UNINIT_ASSUMED_INIT),
++ LintId::of(&methods::ZST_OFFSET),
++ LintId::of(&minmax::MIN_MAX),
++ LintId::of(&misc::CMP_NAN),
++ LintId::of(&misc::FLOAT_CMP),
++ LintId::of(&misc::MODULO_ONE),
++ LintId::of(&mut_key::MUTABLE_KEY_TYPE),
++ LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
++ LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
++ LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
++ LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
++ LintId::of(&ptr::MUT_FROM_REF),
++ LintId::of(®ex::INVALID_REGEX),
++ LintId::of(&serde_api::SERDE_API_MISUSE),
++ LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++ LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++ LintId::of(&swap::ALMOST_SWAPPED),
++ LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
++ LintId::of(&transmute::WRONG_TRANSMUTE),
++ LintId::of(&transmuting_null::TRANSMUTING_NULL),
++ LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
++ LintId::of(&types::CAST_PTR_ALIGNMENT),
++ LintId::of(&types::CAST_REF_TO_MUT),
++ LintId::of(&types::UNIT_CMP),
++ LintId::of(&unicode::ZERO_WIDTH_SPACE),
++ 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),
++ ]);
++
++ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
++ LintId::of(&bytecount::NAIVE_BYTECOUNT),
++ LintId::of(&entry::MAP_ENTRY),
++ LintId::of(&escape::BOXED_LOCAL),
++ LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
++ LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
++ LintId::of(&loops::MANUAL_MEMCPY),
++ LintId::of(&loops::NEEDLESS_COLLECT),
++ LintId::of(&methods::EXPECT_FUN_CALL),
++ LintId::of(&methods::ITER_NTH),
++ LintId::of(&methods::OR_FUN_CALL),
++ LintId::of(&methods::SINGLE_CHAR_PATTERN),
++ LintId::of(&misc::CMP_OWNED),
++ LintId::of(&mutex_atomic::MUTEX_ATOMIC),
++ LintId::of(&redundant_clone::REDUNDANT_CLONE),
++ LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
++ LintId::of(&types::BOX_VEC),
++ LintId::of(&types::REDUNDANT_ALLOCATION),
++ LintId::of(&vec::USELESS_VEC),
++ ]);
++
++ 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(&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(&missing_const_for_fn::MISSING_CONST_FOR_FN),
++ LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
++ LintId::of(&mutex_atomic::MUTEX_INTEGER),
++ LintId::of(&needless_borrow::NEEDLESS_BORROW),
++ LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
++ LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
++ LintId::of(&transmute::USELESS_TRANSMUTE),
++ LintId::of(&use_self::USE_SELF),
++ ]);
++}
++
++#[rustfmt::skip]
++fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
++ store.register_removed(
++ "should_assert_eq",
++ "`assert!()` will be more flexible with RFC 2011",
++ );
++ store.register_removed(
++ "extend_from_slice",
++ "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
++ );
++ store.register_removed(
++ "range_step_by_zero",
++ "`iterator.step_by(0)` panics nowadays",
++ );
++ store.register_removed(
++ "unstable_as_slice",
++ "`Vec::as_slice` has been stabilized in 1.7",
++ );
++ store.register_removed(
++ "unstable_as_mut_slice",
++ "`Vec::as_mut_slice` has been stabilized in 1.7",
++ );
++ store.register_removed(
++ "str_to_string",
++ "using `str::to_string` is common even today and specialization will likely happen soon",
++ );
++ store.register_removed(
++ "string_to_string",
++ "using `string::to_string` is common even today and specialization will likely happen soon",
++ );
++ 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",
++ );
++}
++
++/// 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");
++}
++
++// 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");
++}
--- /dev/null
--- /dev/null
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{
++ walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor,
++};
++use rustc_hir::FnRetTy::Return;
++use rustc_hir::{
++ BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item,
++ ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty,
++ TyKind, WhereClause, WherePredicate,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::kw;
++
++use crate::reexport::Name;
++use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for lifetime annotations which can be removed by
++ /// relying on lifetime elision.
++ ///
++ /// **Why is this bad?** The additional lifetimes make the code look more
++ /// complicated, while there is nothing out of the ordinary going on. Removing
++ /// them leads to more readable code.
++ ///
++ /// **Known problems:** Potential false negatives: we bail out if the function
++ /// has a `where` clause where lifetimes are mentioned.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// // Bad: unnecessary lifetime annotations
++ /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
++ /// x
++ /// }
++ ///
++ /// // Good
++ /// fn elided(x: &u8, y: u8) -> &u8 {
++ /// x
++ /// }
++ /// ```
++ pub NEEDLESS_LIFETIMES,
++ complexity,
++ "using explicit lifetimes for references in function arguments when elision rules \
++ would allow omitting them"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for lifetimes in generics that are never used
++ /// anywhere else.
++ ///
++ /// **Why is this bad?** The additional lifetimes make the code look more
++ /// complicated, while there is nothing out of the ordinary going on. Removing
++ /// them leads to more readable code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// // Bad: unnecessary lifetimes
++ /// fn unused_lifetime<'a>(x: u8) {
++ /// // ..
++ /// }
++ ///
++ /// // Good
++ /// fn no_lifetime(x: u8) {
++ /// // ...
++ /// }
++ /// ```
++ pub EXTRA_UNUSED_LIFETIMES,
++ complexity,
++ "unused lifetimes in function definitions"
++}
++
++declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Lifetimes {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
++ check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true);
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++ if let ImplItemKind::Fn(ref sig, id) = item.kind {
++ let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id).is_none();
++ check_fn_inner(
++ cx,
++ &sig.decl,
++ Some(id),
++ &item.generics,
++ item.span,
++ report_extra_lifetimes,
++ );
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++ if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
++ let body = match *body {
++ TraitFn::Required(_) => None,
++ TraitFn::Provided(id) => Some(id),
++ };
++ check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true);
++ }
++ }
++}
++
++/// The lifetime of a &-reference.
++#[derive(PartialEq, Eq, Hash, Debug)]
++enum RefLt {
++ Unnamed,
++ Static,
++ Named(Name),
++}
++
++fn check_fn_inner<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: Option<BodyId>,
++ generics: &'tcx Generics<'_>,
++ span: Span,
++ report_extra_lifetimes: bool,
++) {
++ if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
++ return;
++ }
++
++ let mut bounds_lts = Vec::new();
++ let types = generics.params.iter().filter(|param| match param.kind {
++ GenericParamKind::Type { .. } => true,
++ _ => false,
++ });
++ for typ in types {
++ for bound in typ.bounds {
++ let mut visitor = RefVisitor::new(cx);
++ walk_param_bound(&mut visitor, bound);
++ if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
++ return;
++ }
++ if let GenericBound::Trait(ref trait_ref, _) = *bound {
++ let params = &trait_ref
++ .trait_ref
++ .path
++ .segments
++ .last()
++ .expect("a path must have at least one segment")
++ .args;
++ if let Some(ref params) = *params {
++ let lifetimes = params.args.iter().filter_map(|arg| match arg {
++ GenericArg::Lifetime(lt) => Some(lt),
++ _ => None,
++ });
++ for bound in lifetimes {
++ if bound.name != LifetimeName::Static && !bound.is_elided() {
++ return;
++ }
++ bounds_lts.push(bound);
++ }
++ }
++ }
++ }
++ }
++ if could_use_elision(cx, decl, body, &generics.params, bounds_lts) {
++ span_lint(
++ cx,
++ NEEDLESS_LIFETIMES,
++ span.with_hi(decl.output.span().hi()),
++ "explicit lifetimes given in parameter types where they could be elided \
++ (or replaced with `'_` if needed by type declaration)",
++ );
++ }
++ if report_extra_lifetimes {
++ self::report_extra_lifetimes(cx, decl, generics);
++ }
++}
++
++fn could_use_elision<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ func: &'tcx FnDecl<'_>,
++ body: Option<BodyId>,
++ named_generics: &'tcx [GenericParam<'_>],
++ bounds_lts: Vec<&'tcx Lifetime>,
++) -> bool {
++ // There are two scenarios where elision works:
++ // * no output references, all input references have different LT
++ // * output references, exactly one input reference with same LT
++ // All lifetimes must be unnamed, 'static or defined without bounds on the
++ // level of the current item.
++
++ // check named LTs
++ let allowed_lts = allowed_lts_from(named_generics);
++
++ // these will collect all the lifetimes for references in arg/return types
++ let mut input_visitor = RefVisitor::new(cx);
++ let mut output_visitor = RefVisitor::new(cx);
++
++ // extract lifetimes in input argument types
++ for arg in func.inputs {
++ input_visitor.visit_ty(arg);
++ }
++ // extract lifetimes in output type
++ if let Return(ref ty) = func.output {
++ output_visitor.visit_ty(ty);
++ }
++
++ let input_lts = match input_visitor.into_vec() {
++ Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
++ None => return false,
++ };
++ let output_lts = match output_visitor.into_vec() {
++ Some(val) => val,
++ None => return false,
++ };
++
++ if let Some(body_id) = body {
++ let mut checker = BodyLifetimeChecker {
++ lifetimes_used_in_body: false,
++ };
++ checker.visit_expr(&cx.tcx.hir().body(body_id).value);
++ if checker.lifetimes_used_in_body {
++ return false;
++ }
++ }
++
++ // check for lifetimes from higher scopes
++ for lt in input_lts.iter().chain(output_lts.iter()) {
++ if !allowed_lts.contains(lt) {
++ return false;
++ }
++ }
++
++ // no input lifetimes? easy case!
++ if input_lts.is_empty() {
++ false
++ } else if output_lts.is_empty() {
++ // no output lifetimes, check distinctness of input lifetimes
++
++ // only unnamed and static, ok
++ let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
++ if unnamed_and_static {
++ return false;
++ }
++ // we have no output reference, so we only need all distinct lifetimes
++ input_lts.len() == unique_lifetimes(&input_lts)
++ } else {
++ // we have output references, so we need one input reference,
++ // and all output lifetimes must be the same
++ if unique_lifetimes(&output_lts) > 1 {
++ return false;
++ }
++ if input_lts.len() == 1 {
++ match (&input_lts[0], &output_lts[0]) {
++ (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
++ (&RefLt::Named(_), &RefLt::Unnamed) => true,
++ _ => false, /* already elided, different named lifetimes
++ * or something static going on */
++ }
++ } else {
++ false
++ }
++ }
++}
++
++fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
++ let mut allowed_lts = FxHashSet::default();
++ for par in named_generics.iter() {
++ if let GenericParamKind::Lifetime { .. } = par.kind {
++ if par.bounds.is_empty() {
++ allowed_lts.insert(RefLt::Named(par.name.ident().name));
++ }
++ }
++ }
++ allowed_lts.insert(RefLt::Unnamed);
++ allowed_lts.insert(RefLt::Static);
++ allowed_lts
++}
++
++fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
++ for lt in bounds_lts {
++ if lt.name != LifetimeName::Static {
++ vec.push(RefLt::Named(lt.name.ident().name));
++ }
++ }
++
++ vec
++}
++
++/// Number of unique lifetimes in the given vector.
++#[must_use]
++fn unique_lifetimes(lts: &[RefLt]) -> usize {
++ lts.iter().collect::<FxHashSet<_>>().len()
++}
++
++/// A visitor usable for `rustc_front::visit::walk_ty()`.
++struct RefVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ lts: Vec<RefLt>,
++ abort: bool,
++}
++
++impl<'v, 't> RefVisitor<'v, 't> {
++ fn new(cx: &'v LateContext<'v, 't>) -> Self {
++ Self {
++ cx,
++ lts: Vec::new(),
++ abort: false,
++ }
++ }
++
++ fn record(&mut self, lifetime: &Option<Lifetime>) {
++ if let Some(ref lt) = *lifetime {
++ if lt.name == LifetimeName::Static {
++ self.lts.push(RefLt::Static);
++ } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name {
++ // Fresh lifetimes generated should be ignored.
++ } else if lt.is_elided() {
++ self.lts.push(RefLt::Unnamed);
++ } else {
++ self.lts.push(RefLt::Named(lt.name.ident().name));
++ }
++ } else {
++ self.lts.push(RefLt::Unnamed);
++ }
++ }
++
++ fn into_vec(self) -> Option<Vec<RefLt>> {
++ if self.abort {
++ None
++ } else {
++ Some(self.lts)
++ }
++ }
++
++ fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
++ if let Some(ref last_path_segment) = last_path_segment(qpath).args {
++ if !last_path_segment.parenthesized
++ && !last_path_segment.args.iter().any(|arg| match arg {
++ GenericArg::Lifetime(_) => true,
++ _ => false,
++ })
++ {
++ let hir_id = ty.hir_id;
++ match self.cx.tables.qpath_res(qpath, hir_id) {
++ Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
++ let generics = self.cx.tcx.generics_of(def_id);
++ for _ in generics.params.as_slice() {
++ self.record(&None);
++ }
++ },
++ Res::Def(DefKind::Trait, def_id) => {
++ let trait_def = self.cx.tcx.trait_def(def_id);
++ for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
++ self.record(&None);
++ }
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ // for lifetimes as parameters of generics
++ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++ self.record(&Some(*lifetime));
++ }
++
++ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
++ match ty.kind {
++ TyKind::Rptr(ref lt, _) if lt.is_elided() => {
++ self.record(&None);
++ },
++ TyKind::Path(ref path) => {
++ self.collect_anonymous_lifetimes(path, ty);
++ },
++ TyKind::Def(item, _) => {
++ let map = self.cx.tcx.hir();
++ if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind {
++ for bound in exist_ty.bounds {
++ if let GenericBound::Outlives(_) = *bound {
++ self.record(&None);
++ }
++ }
++ } else {
++ unreachable!()
++ }
++ walk_ty(self, ty);
++ },
++ TyKind::TraitObject(bounds, ref lt) => {
++ if !lt.is_elided() {
++ self.abort = true;
++ }
++ for bound in bounds {
++ self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
++ }
++ return;
++ },
++ _ => (),
++ }
++ walk_ty(self, ty);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
++/// reason about elision.
++fn has_where_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool {
++ for predicate in where_clause.predicates {
++ match *predicate {
++ WherePredicate::RegionPredicate(..) => return true,
++ WherePredicate::BoundPredicate(ref pred) => {
++ // a predicate like F: Trait or F: for<'a> Trait<'a>
++ let mut visitor = RefVisitor::new(cx);
++ // walk the type F, it may not contain LT refs
++ walk_ty(&mut visitor, &pred.bounded_ty);
++ if !visitor.lts.is_empty() {
++ return true;
++ }
++ // if the bounds define new lifetimes, they are fine to occur
++ let allowed_lts = allowed_lts_from(&pred.bound_generic_params);
++ // now walk the bounds
++ for bound in pred.bounds.iter() {
++ walk_param_bound(&mut visitor, bound);
++ }
++ // and check that all lifetimes are allowed
++ match visitor.into_vec() {
++ None => return false,
++ Some(lts) => {
++ for lt in lts {
++ if !allowed_lts.contains(<) {
++ return true;
++ }
++ }
++ },
++ }
++ },
++ WherePredicate::EqPredicate(ref pred) => {
++ let mut visitor = RefVisitor::new(cx);
++ walk_ty(&mut visitor, &pred.lhs_ty);
++ walk_ty(&mut visitor, &pred.rhs_ty);
++ if !visitor.lts.is_empty() {
++ return true;
++ }
++ },
++ }
++ }
++ false
++}
++
++struct LifetimeChecker {
++ map: FxHashMap<Name, Span>,
++}
++
++impl<'tcx> Visitor<'tcx> for LifetimeChecker {
++ type Map = Map<'tcx>;
++
++ // for lifetimes as parameters of generics
++ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++ self.map.remove(&lifetime.name.ident().name);
++ }
++
++ fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
++ // don't actually visit `<'a>` or `<'a: 'b>`
++ // we've already visited the `'a` declarations and
++ // don't want to spuriously remove them
++ // `'b` in `'a: 'b` is useless unless used elsewhere in
++ // a non-lifetime bound
++ if let GenericParamKind::Type { .. } = param.kind {
++ walk_generic_param(self, param)
++ }
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn report_extra_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
++ let hs = generics
++ .params
++ .iter()
++ .filter_map(|par| match par.kind {
++ GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
++ _ => None,
++ })
++ .collect();
++ let mut checker = LifetimeChecker { map: hs };
++
++ walk_generics(&mut checker, generics);
++ walk_fn_decl(&mut checker, func);
++
++ for &v in checker.map.values() {
++ span_lint(
++ cx,
++ EXTRA_UNUSED_LIFETIMES,
++ v,
++ "this lifetime isn't used in the function definition",
++ );
++ }
++}
++
++struct BodyLifetimeChecker {
++ lifetimes_used_in_body: bool,
++}
++
++impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
++ type Map = Map<'tcx>;
++
++ // for lifetimes as parameters of generics
++ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++ if lifetime.name.ident().name != kw::Invalid && lifetime.name.ident().name != kw::StaticLifetime {
++ self.lifetimes_used_in_body = true;
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++//! Lints concerned with the grouping of digits with underscores in integral or
++//! floating-point literal expressions.
++
++use crate::utils::{
++ in_macro,
++ numeric_literal::{NumericLiteral, Radix},
++ snippet_opt, span_lint_and_sugg,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if a long integral or floating-point constant does
++ /// not contain underscores.
++ ///
++ /// **Why is this bad?** Reading long numbers is difficult without separators.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x: u64 = 61864918973511;
++ /// ```
++ pub UNREADABLE_LITERAL,
++ pedantic,
++ "long integer literal without underscores"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns for mistyped suffix in literals
++ ///
++ /// **Why is this bad?** This is most probably a typo
++ ///
++ /// **Known problems:**
++ /// - Recommends a signed suffix, even though the number might be too big and an unsigned
++ /// suffix is required
++ /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// 2_32;
++ /// ```
++ pub MISTYPED_LITERAL_SUFFIXES,
++ correctness,
++ "mistyped literal suffix"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if an integral or floating-point constant is
++ /// grouped inconsistently with underscores.
++ ///
++ /// **Why is this bad?** Readers may incorrectly interpret inconsistently
++ /// grouped digits.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x: u64 = 618_64_9189_73_511;
++ /// ```
++ pub INCONSISTENT_DIGIT_GROUPING,
++ style,
++ "integer literals with digits grouped inconsistently"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if the digits of an integral or floating-point
++ /// constant are grouped into groups that
++ /// are too large.
++ ///
++ /// **Why is this bad?** Negatively impacts readability.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x: u64 = 6186491_8973511;
++ /// ```
++ pub LARGE_DIGIT_GROUPS,
++ pedantic,
++ "grouping digits into groups that are too large"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if there is a better representation for a numeric literal.
++ ///
++ /// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
++ /// readable than a decimal representation.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// `255` => `0xFF`
++ /// `65_535` => `0xFFFF`
++ /// `4_042_322_160` => `0xF0F0_F0F0`
++ pub DECIMAL_LITERAL_REPRESENTATION,
++ restriction,
++ "using decimal representation when hexadecimal would be better"
++}
++
++enum WarningType {
++ UnreadableLiteral,
++ InconsistentDigitGrouping,
++ LargeDigitGroups,
++ DecimalRepresentation,
++ MistypedLiteralSuffix,
++}
++
++impl WarningType {
++ fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: rustc_span::Span) {
++ match self {
++ Self::MistypedLiteralSuffix => span_lint_and_sugg(
++ cx,
++ MISTYPED_LITERAL_SUFFIXES,
++ span,
++ "mistyped literal suffix",
++ "did you mean to write",
++ suggested_format,
++ Applicability::MaybeIncorrect,
++ ),
++ Self::UnreadableLiteral => span_lint_and_sugg(
++ cx,
++ UNREADABLE_LITERAL,
++ span,
++ "long literal lacking separators",
++ "consider",
++ suggested_format,
++ Applicability::MachineApplicable,
++ ),
++ Self::LargeDigitGroups => span_lint_and_sugg(
++ cx,
++ LARGE_DIGIT_GROUPS,
++ span,
++ "digit groups should be smaller",
++ "consider",
++ suggested_format,
++ Applicability::MachineApplicable,
++ ),
++ Self::InconsistentDigitGrouping => span_lint_and_sugg(
++ cx,
++ INCONSISTENT_DIGIT_GROUPING,
++ span,
++ "digits grouped inconsistently by underscores",
++ "consider",
++ suggested_format,
++ Applicability::MachineApplicable,
++ ),
++ Self::DecimalRepresentation => span_lint_and_sugg(
++ cx,
++ DECIMAL_LITERAL_REPRESENTATION,
++ span,
++ "integer literal has a better hexadecimal representation",
++ "consider",
++ suggested_format,
++ Applicability::MachineApplicable,
++ ),
++ };
++ }
++}
++
++declare_lint_pass!(LiteralDigitGrouping => [
++ UNREADABLE_LITERAL,
++ INCONSISTENT_DIGIT_GROUPING,
++ LARGE_DIGIT_GROUPS,
++ MISTYPED_LITERAL_SUFFIXES,
++]);
++
++impl EarlyLintPass for LiteralDigitGrouping {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ if let ExprKind::Lit(ref lit) = expr.kind {
++ Self::check_lit(cx, lit)
++ }
++ }
++}
++
++// Length of each UUID hyphenated group in hex digits.
++const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
++
++impl LiteralDigitGrouping {
++ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
++ if_chain! {
++ if let Some(src) = snippet_opt(cx, lit.span);
++ if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
++ then {
++ if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) {
++ return;
++ }
++
++ if Self::is_literal_uuid_formatted(&mut num_lit) {
++ return;
++ }
++
++ let result = (|| {
++
++ let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
++ if let Some(fraction) = num_lit.fraction {
++ let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
++
++ let consistent = Self::parts_consistent(integral_group_size,
++ fractional_group_size,
++ num_lit.integer.len(),
++ fraction.len());
++ if !consistent {
++ return Err(WarningType::InconsistentDigitGrouping);
++ };
++ }
++ Ok(())
++ })();
++
++
++ if let Err(warning_type) = result {
++ let should_warn = match warning_type {
++ | WarningType::UnreadableLiteral
++ | WarningType::InconsistentDigitGrouping
++ | WarningType::LargeDigitGroups => {
++ !in_macro(lit.span)
++ }
++ WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
++ true
++ }
++ };
++ if should_warn {
++ warning_type.display(num_lit.format(), cx, lit.span)
++ }
++ }
++ }
++ }
++ }
++
++ // Returns `false` if the check fails
++ fn check_for_mistyped_suffix(
++ cx: &EarlyContext<'_>,
++ span: rustc_span::Span,
++ num_lit: &mut NumericLiteral<'_>,
++ ) -> bool {
++ if num_lit.suffix.is_some() {
++ return true;
++ }
++
++ let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
++ (exponent, &["32", "64"][..], 'f')
++ } else if let Some(fraction) = &mut num_lit.fraction {
++ (fraction, &["32", "64"][..], 'f')
++ } else {
++ (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
++ };
++
++ let mut split = part.rsplit('_');
++ let last_group = split.next().expect("At least one group");
++ if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
++ *part = &part[..part.len() - last_group.len()];
++ let mut sugg = num_lit.format();
++ sugg.push('_');
++ sugg.push(missing_char);
++ sugg.push_str(last_group);
++ WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
++ false
++ } else {
++ true
++ }
++ }
++
++ /// Checks whether the numeric literal matches the formatting of a UUID.
++ ///
++ /// Returns `true` if the radix is hexadecimal, and the groups match the
++ /// UUID format of 8-4-4-4-12.
++ fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool {
++ if num_lit.radix != Radix::Hexadecimal {
++ return false;
++ }
++
++ // UUIDs should not have a fraction
++ if num_lit.fraction.is_some() {
++ return false;
++ }
++
++ let group_sizes: Vec<usize> = num_lit.integer.split('_').map(str::len).collect();
++ if UUID_GROUP_LENS.len() == group_sizes.len() {
++ UUID_GROUP_LENS.iter().zip(&group_sizes).all(|(&a, &b)| a == b)
++ } else {
++ false
++ }
++ }
++
++ /// Given the sizes of the digit groups of both integral and fractional
++ /// parts, and the length
++ /// of both parts, determine if the digits have been grouped consistently.
++ #[must_use]
++ fn parts_consistent(
++ int_group_size: Option<usize>,
++ frac_group_size: Option<usize>,
++ int_size: usize,
++ frac_size: usize,
++ ) -> bool {
++ match (int_group_size, frac_group_size) {
++ // No groups on either side of decimal point - trivially consistent.
++ (None, None) => true,
++ // Integral part has grouped digits, fractional part does not.
++ (Some(int_group_size), None) => frac_size <= int_group_size,
++ // Fractional part has grouped digits, integral part does not.
++ (None, Some(frac_group_size)) => int_size <= frac_group_size,
++ // Both parts have grouped digits. Groups should be the same size.
++ (Some(int_group_size), Some(frac_group_size)) => int_group_size == frac_group_size,
++ }
++ }
++
++ /// Returns the size of the digit groups (or None if ungrouped) if successful,
++ /// otherwise returns a `WarningType` for linting.
++ fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
++ let mut groups = groups.map(str::len);
++
++ let first = groups.next().expect("At least one group");
++
++ if let Some(second) = groups.next() {
++ if !groups.all(|x| x == second) || first > second {
++ Err(WarningType::InconsistentDigitGrouping)
++ } else if second > 4 {
++ Err(WarningType::LargeDigitGroups)
++ } else {
++ Ok(Some(second))
++ }
++ } else if first > 5 {
++ Err(WarningType::UnreadableLiteral)
++ } else {
++ Ok(None)
++ }
++ }
++}
++
++#[allow(clippy::module_name_repetitions)]
++#[derive(Copy, Clone)]
++pub struct DecimalLiteralRepresentation {
++ threshold: u64,
++}
++
++impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]);
++
++impl EarlyLintPass for DecimalLiteralRepresentation {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ if let ExprKind::Lit(ref lit) = expr.kind {
++ self.check_lit(cx, lit)
++ }
++ }
++}
++
++impl DecimalLiteralRepresentation {
++ #[must_use]
++ pub fn new(threshold: u64) -> Self {
++ Self { threshold }
++ }
++ fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
++ // Lint integral literals.
++ if_chain! {
++ if let LitKind::Int(val, _) = lit.kind;
++ if let Some(src) = snippet_opt(cx, lit.span);
++ if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit);
++ if num_lit.radix == Radix::Decimal;
++ if val >= u128::from(self.threshold);
++ then {
++ let hex = format!("{:#X}", val);
++ let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
++ let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
++ warning_type.display(num_lit.format(), cx, lit.span)
++ });
++ }
++ }
++ }
++
++ fn do_lint(digits: &str) -> Result<(), WarningType> {
++ if digits.len() == 1 {
++ // Lint for 1 digit literals, if someone really sets the threshold that low
++ if digits == "1"
++ || digits == "2"
++ || digits == "4"
++ || digits == "8"
++ || digits == "3"
++ || digits == "7"
++ || digits == "F"
++ {
++ return Err(WarningType::DecimalRepresentation);
++ }
++ } else if digits.len() < 4 {
++ // Lint for Literals with a hex-representation of 2 or 3 digits
++ let f = &digits[0..1]; // first digit
++ let s = &digits[1..]; // suffix
++
++ // Powers of 2
++ if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
++ // Powers of 2 minus 1
++ || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
++ {
++ return Err(WarningType::DecimalRepresentation);
++ }
++ } else {
++ // Lint for Literals with a hex-representation of 4 digits or more
++ let f = &digits[0..1]; // first digit
++ let m = &digits[1..digits.len() - 1]; // middle digits, except last
++ let s = &digits[1..]; // suffix
++
++ // Powers of 2 with a margin of +15/-16
++ if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
++ || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
++ // Lint for representations with only 0s and Fs, while allowing 7 as the first
++ // digit
++ || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
++ {
++ return Err(WarningType::DecimalRepresentation);
++ }
++ }
++
++ Ok(())
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, Constant};
++use crate::reexport::Name;
++use crate::utils::paths;
++use crate::utils::usage::{is_unused, mutated_variables};
++use crate::utils::{
++ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
++ is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
++ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
++ span_lint_and_sugg, span_lint_and_then, SpanlessEq,
++};
++use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg};
++use if_chain::if_chain;
++use itertools::Itertools;
++use rustc_ast::ast;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
++use rustc_hir::{
++ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, LoopSource,
++ MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
++};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::middle::region;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::BytePos;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++use std::iter::{once, Iterator};
++use std::mem;
++
++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` values.
++ ///
++ /// **Why is this bad?** Readability. This is more clearly expressed as an `if
++ /// let`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// for x in option {
++ /// ..
++ /// }
++ /// ```
++ ///
++ /// This should be
++ /// ```ignore
++ /// if let Some(x) = option {
++ /// ..
++ /// }
++ /// ```
++ pub FOR_LOOP_OVER_OPTION,
++ correctness,
++ "for-looping over an `Option`, which is more clearly expressed as an `if let`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `for` loops over `Result` values.
++ ///
++ /// **Why is this bad?** Readability. This is more clearly expressed as an `if
++ /// let`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// for x in result {
++ /// ..
++ /// }
++ /// ```
++ ///
++ /// This should be
++ /// ```ignore
++ /// if let Ok(x) = result {
++ /// ..
++ /// }
++ /// ```
++ pub FOR_LOOP_OVER_RESULT,
++ correctness,
++ "for-looping over 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).
++ ///
++ /// **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 ranges `x..y` where both `x` and `y`
++ /// are constant and `x` is greater or equal to `y`, unless the range is
++ /// reversed or has a negative `.step_by(_)`.
++ ///
++ /// **Why is it bad?** Such loops will either be skipped or loop until
++ /// wrap-around (in debug code, this may `panic!()`). Both options are probably
++ /// not intended.
++ ///
++ /// **Known problems:** The lint cannot catch loops over dynamically defined
++ /// ranges. Doing this would require simulating all possible inputs and code
++ /// paths through the program, which would be complex and error-prone.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// for x in 5..10 - 5 {
++ /// ..
++ /// } // oops, stray `-`
++ /// ```
++ pub REVERSE_RANGE_LOOP,
++ correctness,
++ "iteration over an empty range, such as `10..0` or `5..5`"
++}
++
++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?** Those busy loops burn CPU cycles without doing
++ /// anything. Think of the environment and either block on something or at least
++ /// make the thread sleep for some microseconds.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// loop {}
++ /// ```
++ pub EMPTY_LOOP,
++ style,
++ "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,
++ complexity,
++ "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_lint_pass!(Loops => [
++ MANUAL_MEMCPY,
++ NEEDLESS_RANGE_LOOP,
++ EXPLICIT_ITER_LOOP,
++ EXPLICIT_INTO_ITER_LOOP,
++ ITER_NEXT_LOOP,
++ FOR_LOOP_OVER_RESULT,
++ FOR_LOOP_OVER_OPTION,
++ WHILE_LET_LOOP,
++ NEEDLESS_COLLECT,
++ REVERSE_RANGE_LOOP,
++ EXPLICIT_COUNTER_LOOP,
++ EMPTY_LOOP,
++ WHILE_LET_ON_ITERATOR,
++ FOR_KV_MAP,
++ NEVER_LOOP,
++ MUT_RANGE_BOUND,
++ WHILE_IMMUTABLE_CONDITION,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops {
++ #[allow(clippy::too_many_lines)]
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let Some((pat, arg, body)) = 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);
++ }
++
++ // we don't want to check expanded macros
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ // check for never_loop
++ if let ExprKind::Loop(ref block, _, _) = expr.kind {
++ match never_loop_block(block, expr.hir_id) {
++ NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"),
++ NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
++ }
++ }
++
++ // 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(ref block, _, LoopSource::Loop) = expr.kind {
++ // also check for empty `loop {}` statements
++ if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
++ span_lint(
++ cx,
++ EMPTY_LOOP,
++ expr.span,
++ "empty `loop {}` detected. You may want to either use `panic!()` or add \
++ `std::thread::sleep(..);` to the loop body.",
++ );
++ }
++
++ // extract the expression from the first statement (if any) in a block
++ let inner_stmt_expr = extract_expr_from_first_stmt(block);
++ // or extract the first expression (if any) from the block
++ if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
++ if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind {
++ // ensure "if let" compatible match structure
++ match *source {
++ MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
++ if arms.len() == 2
++ && arms[0].guard.is_none()
++ && arms[1].guard.is_none()
++ && is_simple_break_expr(&arms[1].body)
++ {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ // NOTE: we used to build a body here instead of using
++ // ellipsis, this was removed because:
++ // 1) it was ugly with big bodies;
++ // 2) it was not indented properly;
++ // 3) it wasn’t very smart (see #675).
++ let mut applicability = Applicability::HasPlaceholders;
++ span_lint_and_sugg(
++ cx,
++ WHILE_LET_LOOP,
++ expr.span,
++ "this loop could be written as a `while let` loop",
++ "try",
++ format!(
++ "while let {} = {} {{ .. }}",
++ snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
++ snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
++ ),
++ applicability,
++ );
++ }
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++ if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind {
++ let pat = &arms[0].pat.kind;
++ if let (
++ &PatKind::TupleStruct(ref qpath, ref pat_args, _),
++ &ExprKind::MethodCall(ref method_path, _, ref method_args),
++ ) = (pat, &match_expr.kind)
++ {
++ let iter_expr = &method_args[0];
++
++ // Don't lint when the iterator is recreated on every iteration
++ if_chain! {
++ if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
++ if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR);
++ if implements_trait(cx, cx.tables.expr_ty(iter_expr), iter_def_id, &[]);
++ then {
++ return;
++ }
++ }
++
++ let lhs_constructor = last_path_segment(qpath);
++ if method_path.ident.name == sym!(next)
++ && match_trait_method(cx, match_expr, &paths::ITERATOR)
++ && lhs_constructor.ident.name == sym!(Some)
++ && (pat_args.is_empty()
++ || !is_refutable(cx, &pat_args[0])
++ && !is_used_inside(cx, iter_expr, &arms[0].body)
++ && !is_iterator_used_after_while_let(cx, iter_expr)
++ && !is_nested(cx, expr, &method_args[0]))
++ {
++ let mut applicability = Applicability::MachineApplicable;
++ let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
++ let loop_var = if pat_args.is_empty() {
++ "_".to_string()
++ } else {
++ snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
++ };
++ span_lint_and_sugg(
++ cx,
++ WHILE_LET_ON_ITERATOR,
++ expr.span.with_hi(match_expr.span.hi()),
++ "this loop could be written as a `for` loop",
++ "try",
++ format!("for {} in {}", loop_var, iterator),
++ applicability,
++ );
++ }
++ }
++ }
++
++ if let Some((cond, body)) = higher::while_loop(&expr) {
++ check_infinite_loop(cx, cond, body);
++ }
++
++ check_needless_collect(expr, cx);
++ }
++}
++
++enum NeverLoopResult {
++ // A break/return always get triggered but not necessarily for the main loop.
++ AlwaysBreak,
++ // A continue may occur for the main loop.
++ MayContinueMainLoop,
++ Otherwise,
++}
++
++#[must_use]
++fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
++ match *arg {
++ NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
++ NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
++ }
++}
++
++// Combine two results for parts that are called in order.
++#[must_use]
++fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
++ match first {
++ NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
++ NeverLoopResult::Otherwise => second,
++ }
++}
++
++// Combine two results where both parts are called but not necessarily in order.
++#[must_use]
++fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
++ match (left, right) {
++ (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
++ NeverLoopResult::MayContinueMainLoop
++ },
++ (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
++ (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
++ }
++}
++
++// Combine two results where only one of the part may have been executed.
++#[must_use]
++fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
++ match (b1, b2) {
++ (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
++ (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
++ NeverLoopResult::MayContinueMainLoop
++ },
++ (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
++ }
++}
++
++fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
++ let stmts = block.stmts.iter().map(stmt_to_expr);
++ let expr = once(block.expr.as_deref());
++ let mut iter = stmts.chain(expr).filter_map(|e| e);
++ never_loop_expr_seq(&mut iter, main_loop_id)
++}
++
++fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ match stmt.kind {
++ StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
++ StmtKind::Local(ref local) => local.init.as_deref(),
++ _ => None,
++ }
++}
++
++fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
++ match expr.kind {
++ ExprKind::Box(ref e)
++ | ExprKind::Unary(_, ref e)
++ | ExprKind::Cast(ref e, _)
++ | ExprKind::Type(ref e, _)
++ | ExprKind::Field(ref e, _)
++ | ExprKind::AddrOf(_, _, ref e)
++ | ExprKind::Struct(_, _, Some(ref e))
++ | ExprKind::Repeat(ref e, _)
++ | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id),
++ ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es) | ExprKind::Tup(ref es) => {
++ never_loop_expr_all(&mut es.iter(), main_loop_id)
++ },
++ ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id),
++ ExprKind::Binary(_, ref e1, ref e2)
++ | ExprKind::Assign(ref e1, ref e2, _)
++ | ExprKind::AssignOp(_, ref e1, ref e2)
++ | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id),
++ ExprKind::Loop(ref b, _, _) => {
++ // Break can come from the inner loop so remove them.
++ absorb_break(&never_loop_block(b, main_loop_id))
++ },
++ ExprKind::Match(ref e, ref arms, _) => {
++ let e = never_loop_expr(e, main_loop_id);
++ if arms.is_empty() {
++ e
++ } else {
++ let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
++ combine_seq(e, arms)
++ }
++ },
++ ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id),
++ ExprKind::Continue(d) => {
++ let id = d
++ .target_id
++ .expect("target ID can only be missing in the presence of compilation errors");
++ if id == main_loop_id {
++ NeverLoopResult::MayContinueMainLoop
++ } else {
++ NeverLoopResult::AlwaysBreak
++ }
++ },
++ ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
++ if let Some(ref e) = *e {
++ combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
++ } else {
++ NeverLoopResult::AlwaysBreak
++ }
++ },
++ ExprKind::Struct(_, _, None)
++ | ExprKind::Yield(_, _)
++ | ExprKind::Closure(_, _, _, _, _)
++ | ExprKind::LlvmInlineAsm(_)
++ | ExprKind::Path(_)
++ | ExprKind::Lit(_)
++ | ExprKind::Err => NeverLoopResult::Otherwise,
++ }
++}
++
++fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++ es.map(|e| never_loop_expr(e, main_loop_id))
++ .fold(NeverLoopResult::Otherwise, combine_seq)
++}
++
++fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++ es.map(|e| never_loop_expr(e, main_loop_id))
++ .fold(NeverLoopResult::Otherwise, combine_both)
++}
++
++fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++ e.map(|e| never_loop_expr(e, main_loop_id))
++ .fold(NeverLoopResult::AlwaysBreak, combine_branches)
++}
++
++fn check_for_loop<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ pat: &'tcx Pat<'_>,
++ arg: &'tcx Expr<'_>,
++ body: &'tcx Expr<'_>,
++ expr: &'tcx Expr<'_>,
++) {
++ check_for_loop_range(cx, pat, arg, body, expr);
++ check_for_loop_reverse_range(cx, arg, expr);
++ check_for_loop_arg(cx, pat, arg, expr);
++ check_for_loop_explicit_counter(cx, pat, arg, body, expr);
++ check_for_loop_over_map_kv(cx, pat, arg, body, expr);
++ check_for_mut_range_bound(cx, arg, body);
++ detect_manual_memcpy(cx, pat, arg, body, expr);
++}
++
++fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
++ if_chain! {
++ if let ExprKind::Path(ref qpath) = expr.kind;
++ if let QPath::Resolved(None, ref path) = *qpath;
++ if path.segments.len() == 1;
++ if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
++ // our variable!
++ if local_id == var;
++ then {
++ return true;
++ }
++ }
++
++ false
++}
++
++struct Offset {
++ value: String,
++ negate: bool,
++}
++
++impl Offset {
++ fn negative(s: String) -> Self {
++ Self { value: s, negate: true }
++ }
++
++ fn positive(s: String) -> Self {
++ Self {
++ value: s,
++ negate: false,
++ }
++ }
++}
++
++struct FixedOffsetVar {
++ var_name: String,
++ offset: Offset,
++}
++
++fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, '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 get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option<FixedOffsetVar> {
++ fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> {
++ match e.kind {
++ ExprKind::Lit(ref l) => match l.node {
++ ast::LitKind::Int(x, _ty) => Some(x.to_string()),
++ _ => None,
++ },
++ ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
++ _ => None,
++ }
++ }
++
++ if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind {
++ let ty = cx.tables.expr_ty(seqexpr);
++ if !is_slice_like(cx, ty) {
++ return None;
++ }
++
++ let offset = match idx.kind {
++ ExprKind::Binary(op, ref lhs, ref rhs) => match op.node {
++ BinOpKind::Add => {
++ let offset_opt = if same_var(cx, lhs, var) {
++ extract_offset(cx, rhs, var)
++ } else if same_var(cx, rhs, var) {
++ extract_offset(cx, lhs, var)
++ } else {
++ None
++ };
++
++ offset_opt.map(Offset::positive)
++ },
++ BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
++ _ => None,
++ },
++ ExprKind::Path(..) => {
++ if same_var(cx, idx, var) {
++ Some(Offset::positive("0".into()))
++ } else {
++ None
++ }
++ },
++ _ => None,
++ };
++
++ offset.map(|o| FixedOffsetVar {
++ var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
++ offset: o,
++ })
++ } else {
++ None
++ }
++}
++
++fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &Expr<'_>,
++ var: HirId,
++) -> Option<FixedOffsetVar> {
++ if_chain! {
++ if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
++ if method.ident.name == sym!(clone);
++ if args.len() == 1;
++ if let Some(arg) = args.get(0);
++ then {
++ return get_fixed_offset_var(cx, arg, var);
++ }
++ }
++
++ get_fixed_offset_var(cx, expr, var)
++}
++
++fn get_indexed_assignments<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ body: &Expr<'_>,
++ var: HirId,
++) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
++ fn get_assignment<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ e: &Expr<'_>,
++ var: HirId,
++ ) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
++ if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind {
++ match (
++ get_fixed_offset_var(cx, lhs, var),
++ fetch_cloned_fixed_offset_var(cx, rhs, var),
++ ) {
++ (Some(offset_left), Some(offset_right)) => {
++ // Source and destination must be different
++ if offset_left.var_name == offset_right.var_name {
++ None
++ } else {
++ Some((offset_left, offset_right))
++ }
++ },
++ _ => None,
++ }
++ } else {
++ None
++ }
++ }
++
++ if let ExprKind::Block(ref b, _) = body.kind {
++ let Block {
++ ref stmts, ref expr, ..
++ } = **b;
++
++ stmts
++ .iter()
++ .map(|stmt| match stmt.kind {
++ StmtKind::Local(..) | StmtKind::Item(..) => None,
++ StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)),
++ })
++ .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var))))
++ .filter_map(|op| op)
++ .collect::<Option<Vec<_>>>()
++ .unwrap_or_default()
++ } else {
++ get_assignment(cx, body, var).into_iter().collect()
++ }
++}
++
++/// Checks for for loops that sequentially copy items from one slice-like
++/// object to another.
++fn detect_manual_memcpy<'a, 'tcx>(
++ cx: &LateContext<'a, '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(cx, arg)
++ {
++ // the var must be a single name
++ if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
++ let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
++ match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
++ ("0", _, "0", _) => "".into(),
++ ("0", _, x, false) | (x, false, "0", false) => x.into(),
++ ("0", _, x, true) | (x, false, "0", true) => format!("-{}", x),
++ (x, false, y, false) => format!("({} + {})", x, y),
++ (x, false, y, true) => {
++ if x == y {
++ "0".into()
++ } else {
++ format!("({} - {})", x, y)
++ }
++ },
++ (x, true, y, false) => {
++ if x == y {
++ "0".into()
++ } else {
++ format!("({} - {})", y, x)
++ }
++ },
++ (x, true, y, true) => format!("-({} + {})", x, y),
++ }
++ };
++
++ let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| {
++ if let Some(end) = *end {
++ if_chain! {
++ if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind;
++ if method.ident.name == sym!(len);
++ if len_args.len() == 1;
++ if let Some(arg) = len_args.get(0);
++ if snippet(cx, arg.span, "??") == var_name;
++ then {
++ return if offset.negate {
++ format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
++ } else {
++ String::new()
++ };
++ }
++ }
++
++ let end_str = match limits {
++ ast::RangeLimits::Closed => {
++ let end = sugg::Sugg::hir(cx, end, "<count>");
++ format!("{}", end + sugg::ONE)
++ },
++ ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
++ };
++
++ print_sum(&Offset::positive(end_str), &offset)
++ } else {
++ "..".into()
++ }
++ };
++
++ // The only statements in the for loops can be indexed assignments from
++ // indexed retrievals.
++ let manual_copies = get_indexed_assignments(cx, body, canonical_id);
++
++ let big_sugg = manual_copies
++ .into_iter()
++ .map(|(dst_var, src_var)| {
++ let start_str = Offset::positive(snippet(cx, start.span, "").to_string());
++ let dst_offset = print_sum(&start_str, &dst_var.offset);
++ let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
++ let src_offset = print_sum(&start_str, &src_var.offset);
++ let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
++ let dst = if dst_offset == "" && dst_limit == "" {
++ dst_var.var_name
++ } else {
++ format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
++ };
++
++ format!(
++ "{}.clone_from_slice(&{}[{}..{}])",
++ dst, src_var.var_name, src_offset, src_limit
++ )
++ })
++ .join("\n ");
++
++ if !big_sugg.is_empty() {
++ span_lint_and_sugg(
++ cx,
++ MANUAL_MEMCPY,
++ expr.span,
++ "it looks like you're manually copying between slices",
++ "try replacing the loop by",
++ big_sugg,
++ Applicability::Unspecified,
++ );
++ }
++ }
++ }
++}
++
++/// 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)]
++fn check_for_loop_range<'a, 'tcx>(
++ cx: &LateContext<'a, '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(cx, 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 {
++ 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, ref left, ref 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 {
++ 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".to_string(),
++ 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".to_string(),
++ vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
++ );
++ },
++ );
++ }
++ }
++ }
++ }
++}
++
++fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
++ if_chain! {
++ if let ExprKind::MethodCall(ref method, _, ref len_args) = expr.kind;
++ if len_args.len() == 1;
++ if method.ident.name == sym!(len);
++ if let ExprKind::Path(QPath::Resolved(_, ref 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
++}
++
++fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
++ // if this for loop is iterating over a two-sided range...
++ if let Some(higher::Range {
++ start: Some(start),
++ end: Some(end),
++ limits,
++ }) = higher::range(cx, arg)
++ {
++ // ...and both sides are compile-time constant integers...
++ if let Some((start_idx, _)) = constant(cx, cx.tables, start) {
++ if let Some((end_idx, _)) = constant(cx, cx.tables, end) {
++ // ...and the start index is greater than the end index,
++ // this loop will never run. This is often confusing for developers
++ // who think that this will iterate from the larger value to the
++ // smaller value.
++ let ty = cx.tables.expr_ty(start);
++ let (sup, eq) = match (start_idx, end_idx) {
++ (Constant::Int(start_idx), Constant::Int(end_idx)) => (
++ match ty.kind {
++ ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity),
++ ty::Uint(_) => start_idx > end_idx,
++ _ => false,
++ },
++ start_idx == end_idx,
++ ),
++ _ => (false, false),
++ };
++
++ if sup {
++ let start_snippet = snippet(cx, start.span, "_");
++ let end_snippet = snippet(cx, end.span, "_");
++ let dots = if limits == ast::RangeLimits::Closed {
++ "..="
++ } else {
++ ".."
++ };
++
++ span_lint_and_then(
++ cx,
++ REVERSE_RANGE_LOOP,
++ expr.span,
++ "this range is empty so this for loop will never run",
++ |diag| {
++ diag.span_suggestion(
++ arg.span,
++ "consider using the following if you are attempting to iterate over this \
++ range in reverse",
++ format!(
++ "({end}{dots}{start}).rev()",
++ end = end_snippet,
++ dots = dots,
++ start = start_snippet
++ ),
++ Applicability::MaybeIncorrect,
++ );
++ },
++ );
++ } else if eq && limits != ast::RangeLimits::Closed {
++ // if they are equal, it's also problematic - this loop
++ // will never run.
++ span_lint(
++ cx,
++ REVERSE_RANGE_LOOP,
++ expr.span,
++ "this range is empty so this for loop will never run",
++ );
++ }
++ }
++ }
++ }
++}
++
++fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
++ let mut applicability = Applicability::MachineApplicable;
++ let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
++ let muta = if method_name == "iter_mut" { "mut " } else { "" };
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_ITER_LOOP,
++ arg.span,
++ "it is more concise to loop over references to containers instead of using explicit \
++ iteration methods",
++ "to write this more concisely, try",
++ format!("&{}{}", muta, object),
++ applicability,
++ )
++}
++
++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(ref method, _, ref args) = arg.kind {
++ // just the receiver, no arguments
++ if args.len() == 1 {
++ let method_name = &*method.ident.as_str();
++ // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
++ if method_name == "iter" || method_name == "iter_mut" {
++ if is_ref_iterable_type(cx, &args[0]) {
++ lint_iter_method(cx, args, arg, method_name);
++ }
++ } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
++ let receiver_ty = cx.tables.expr_ty(&args[0]);
++ let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]);
++ if same_tys(cx, receiver_ty, receiver_ty_adjusted) {
++ let mut applicability = Applicability::MachineApplicable;
++ let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_INTO_ITER_LOOP,
++ arg.span,
++ "it is more concise to loop over containers instead of using explicit \
++ iteration methods",
++ "to write this more concisely, try",
++ object.to_string(),
++ applicability,
++ );
++ } else {
++ let ref_receiver_ty = cx.tcx.mk_ref(
++ cx.tcx.lifetimes.re_erased,
++ ty::TypeAndMut {
++ ty: receiver_ty,
++ mutbl: Mutability::Not,
++ },
++ );
++ if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) {
++ lint_iter_method(cx, args, arg, method_name)
++ }
++ }
++ } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
++ span_lint(
++ cx,
++ ITER_NEXT_LOOP,
++ expr.span,
++ "you are iterating over `Iterator::next()` which is an Option; this will compile but is \
++ probably not what you want",
++ );
++ next_loop_linted = true;
++ }
++ }
++ }
++ if !next_loop_linted {
++ check_arg_type(cx, pat, arg);
++ }
++}
++
++/// Checks for `for` loops over `Option`s and `Result`s.
++fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) {
++ let ty = cx.tables.expr_ty(arg);
++ if is_type_diagnostic_item(cx, ty, sym!(option_type)) {
++ span_lint_and_help(
++ cx,
++ FOR_LOOP_OVER_OPTION,
++ arg.span,
++ &format!(
++ "for loop over `{0}`, which is an `Option`. This is more readably written as an \
++ `if let` statement.",
++ snippet(cx, arg.span, "_")
++ ),
++ None,
++ &format!(
++ "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
++ snippet(cx, pat.span, "_"),
++ snippet(cx, arg.span, "_")
++ ),
++ );
++ } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) {
++ span_lint_and_help(
++ cx,
++ FOR_LOOP_OVER_RESULT,
++ arg.span,
++ &format!(
++ "for loop over `{0}`, which is a `Result`. This is more readably written as an \
++ `if let` statement.",
++ snippet(cx, arg.span, "_")
++ ),
++ None,
++ &format!(
++ "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
++ snippet(cx, pat.span, "_"),
++ snippet(cx, arg.span, "_")
++ ),
++ );
++ }
++}
++
++fn check_for_loop_explicit_counter<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ pat: &'tcx Pat<'_>,
++ arg: &'tcx Expr<'_>,
++ body: &'tcx Expr<'_>,
++ expr: &'tcx Expr<'_>,
++) {
++ // Look for variables that are incremented once per loop iteration.
++ let mut visitor = IncrementVisitor {
++ cx,
++ states: FxHashMap::default(),
++ depth: 0,
++ done: false,
++ };
++ walk_expr(&mut visitor, body);
++
++ // For each candidate, check the parent block to see if
++ // it's initialized to zero at the start of the loop.
++ if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
++ for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
++ let mut visitor2 = InitializeVisitor {
++ cx,
++ end_expr: expr,
++ var_id: *id,
++ state: VarState::IncrOnce,
++ name: None,
++ depth: 0,
++ past_loop: false,
++ };
++ walk_block(&mut visitor2, block);
++
++ if visitor2.state == VarState::Warn {
++ if let Some(name) = visitor2.name {
++ let mut applicability = Applicability::MachineApplicable;
++
++ // for some reason this is the only way to get the `Span`
++ // of the entire `for` loop
++ let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind {
++ arms[0].body.span
++ } else {
++ unreachable!()
++ };
++
++ span_lint_and_sugg(
++ cx,
++ EXPLICIT_COUNTER_LOOP,
++ for_span.with_hi(arg.span.hi()),
++ &format!("the variable `{}` is used as a loop counter.", name),
++ "consider using",
++ format!(
++ "for ({}, {}) in {}.enumerate()",
++ name,
++ snippet_with_applicability(cx, pat.span, "item", &mut applicability),
++ make_iterator_snippet(cx, arg, &mut applicability),
++ ),
++ applicability,
++ );
++ }
++ }
++ }
++ }
++}
++
++/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
++/// actual `Iterator` that the loop uses.
++fn make_iterator_snippet(cx: &LateContext<'_, '_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
++ let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR)
++ .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(arg), id, &[]));
++ if impls_iterator {
++ format!(
++ "{}",
++ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
++ )
++ } else {
++ // (&x).into_iter() ==> x.iter()
++ // (&mut x).into_iter() ==> x.iter_mut()
++ match &arg.kind {
++ ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
++ if has_iter_method(cx, cx.tables.expr_ty(&arg_inner)).is_some() =>
++ {
++ let meth_name = match mutability {
++ Mutability::Mut => "iter_mut",
++ Mutability::Not => "iter",
++ };
++ format!(
++ "{}.{}()",
++ sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(),
++ meth_name,
++ )
++ }
++ _ => format!(
++ "{}.into_iter()",
++ sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
++ ),
++ }
++ }
++}
++
++/// Checks for the `FOR_KV_MAP` lint.
++fn check_for_loop_over_map_kv<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ pat: &'tcx Pat<'_>,
++ arg: &'tcx Expr<'_>,
++ body: &'tcx Expr<'_>,
++ expr: &'tcx Expr<'_>,
++) {
++ let pat_span = pat.span;
++
++ if let PatKind::Tuple(ref pat, _) = pat.kind {
++ if pat.len() == 2 {
++ let arg_span = arg.span;
++ let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).kind {
++ ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
++ (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl),
++ (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not),
++ _ => return,
++ },
++ _ => return,
++ };
++ let mutbl = match mutbl {
++ Mutability::Not => "",
++ Mutability::Mut => "_mut",
++ };
++ let arg = match arg.kind {
++ ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr,
++ _ => arg,
++ };
++
++ if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP) {
++ span_lint_and_then(
++ cx,
++ FOR_KV_MAP,
++ expr.span,
++ &format!("you seem to want to iterate on a map's {}s", kind),
++ |diag| {
++ let map = sugg::Sugg::hir(cx, arg, "map");
++ multispan_sugg(
++ diag,
++ "use the corresponding method".into(),
++ vec![
++ (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
++ (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
++ ],
++ );
++ },
++ );
++ }
++ }
++ }
++}
++
++struct MutatePairDelegate {
++ hir_id_low: Option<HirId>,
++ hir_id_high: Option<HirId>,
++ span_low: Option<Span>,
++ span_high: Option<Span>,
++}
++
++impl<'tcx> Delegate<'tcx> for MutatePairDelegate {
++ fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {}
++
++ fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) {
++ if let ty::BorrowKind::MutBorrow = bk {
++ if let PlaceBase::Local(id) = cmt.base {
++ if Some(id) == self.hir_id_low {
++ self.span_low = Some(cmt.span)
++ }
++ if Some(id) == self.hir_id_high {
++ self.span_high = Some(cmt.span)
++ }
++ }
++ }
++ }
++
++ fn mutate(&mut self, cmt: &Place<'tcx>) {
++ if let PlaceBase::Local(id) = cmt.base {
++ if Some(id) == self.hir_id_low {
++ self.span_low = Some(cmt.span)
++ }
++ if Some(id) == self.hir_id_high {
++ self.span_high = Some(cmt.span)
++ }
++ }
++ }
++}
++
++impl<'tcx> MutatePairDelegate {
++ fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
++ (self.span_low, self.span_high)
++ }
++}
++
++fn check_for_mut_range_bound(cx: &LateContext<'_, '_>, arg: &Expr<'_>, body: &Expr<'_>) {
++ if let Some(higher::Range {
++ start: Some(start),
++ end: Some(end),
++ ..
++ }) = higher::range(cx, arg)
++ {
++ let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
++ if mut_ids[0].is_some() || mut_ids[1].is_some() {
++ let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
++ mut_warn_with_span(cx, span_low);
++ mut_warn_with_span(cx, span_high);
++ }
++ }
++}
++
++fn mut_warn_with_span(cx: &LateContext<'_, '_>, span: Option<Span>) {
++ if let Some(sp) = span {
++ span_lint(
++ cx,
++ MUT_RANGE_BOUND,
++ sp,
++ "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
++ );
++ }
++}
++
++fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr<'_>) -> Option<HirId> {
++ if_chain! {
++ if let ExprKind::Path(ref qpath) = bound.kind;
++ if let QPath::Resolved(None, _) = *qpath;
++ then {
++ let res = qpath_res(cx, qpath, bound.hir_id);
++ if let Res::Local(hir_id) = res {
++ let node_str = cx.tcx.hir().get(hir_id);
++ if_chain! {
++ if let Node::Binding(pat) = node_str;
++ if let PatKind::Binding(bind_ann, ..) = pat.kind;
++ if let BindingAnnotation::Mutable = bind_ann;
++ then {
++ return Some(hir_id);
++ }
++ }
++ }
++ }
++ }
++ None
++}
++
++fn check_for_mutation(
++ cx: &LateContext<'_, '_>,
++ body: &Expr<'_>,
++ bound_ids: &[Option<HirId>],
++) -> (Option<Span>, Option<Span>) {
++ let mut delegate = MutatePairDelegate {
++ hir_id_low: bound_ids[0],
++ hir_id_high: bound_ids[1],
++ span_low: None,
++ span_high: None,
++ };
++ let def_id = body.hir_id.owner.to_def_id();
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(body);
++ });
++ delegate.mutation_span()
++}
++
++/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
++fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
++ match *pat {
++ PatKind::Wild => true,
++ PatKind::Binding(.., ident, None) if ident.as_str().starts_with('_') => is_unused(&ident, body),
++ _ => false,
++ }
++}
++
++struct LocalUsedVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ local: HirId,
++ used: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if same_var(self.cx, expr, self.local) {
++ self.used = true;
++ } else {
++ walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++struct VarVisitor<'a, 'tcx> {
++ /// context reference
++ cx: &'a LateContext<'a, 'tcx>,
++ /// var name to look for as index
++ var: HirId,
++ /// indexed variables that are used mutably
++ indexed_mut: FxHashSet<Name>,
++ /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
++ indexed_indirectly: FxHashMap<Name, 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<Name, (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<Name>,
++ /// 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, ref seqvar) = *seqpath;
++ if seqvar.segments.len() == 1;
++ then {
++ let index_used_directly = same_var(self.cx, idx, self.var);
++ let indexed_indirectly = {
++ let mut used_visitor = LocalUsedVisitor {
++ cx: self.cx,
++ local: self.var,
++ used: false,
++ };
++ walk_expr(&mut used_visitor, idx);
++ used_visitor.used
++ };
++
++ if indexed_indirectly || index_used_directly {
++ if self.prefer_mutable {
++ self.indexed_mut.insert(seqvar.segments[0].ident.name);
++ }
++ let res = qpath_res(self.cx, 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.tables.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.tables.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(ref meth, _, ref 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(ref seqexpr, ref idx) = expr.kind;
++ if !self.check(idx, seqexpr, expr);
++ then { return }
++ }
++
++ if_chain! {
++ // directly using a variable
++ if let ExprKind::Path(ref qpath) = expr.kind;
++ if let QPath::Resolved(None, ref path) = *qpath;
++ if path.segments.len() == 1;
++ then {
++ if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) {
++ 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(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => {
++ self.prefer_mutable = true;
++ self.visit_expr(lhs);
++ self.prefer_mutable = false;
++ self.visit_expr(rhs);
++ },
++ ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => {
++ if mutbl == Mutability::Mut {
++ self.prefer_mutable = true;
++ }
++ self.visit_expr(expr);
++ },
++ ExprKind::Call(ref f, args) => {
++ self.visit_expr(f);
++ for expr in args {
++ let ty = self.cx.tables.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.tables.type_dependent_def_id(expr.hir_id).unwrap();
++ for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(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
++ }
++}
++
++fn is_used_inside<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
++ let def_id = match var_def_id(cx, expr) {
++ Some(id) => id,
++ None => return false,
++ };
++ if let Some(used_mutably) = mutated_variables(container, cx) {
++ if used_mutably.contains(&def_id) {
++ return true;
++ }
++ }
++ false
++}
++
++fn is_iterator_used_after_while_let<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
++ let def_id = match var_def_id(cx, iter_expr) {
++ Some(id) => id,
++ None => return false,
++ };
++ let mut visitor = VarUsedAfterLoopVisitor {
++ cx,
++ def_id,
++ iter_expr_id: iter_expr.hir_id,
++ past_while_let: false,
++ var_used_after_while_let: false,
++ };
++ if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
++ walk_block(&mut visitor, enclosing_block);
++ }
++ visitor.var_used_after_while_let
++}
++
++struct VarUsedAfterLoopVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ def_id: HirId,
++ iter_expr_id: HirId,
++ past_while_let: bool,
++ var_used_after_while_let: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if self.past_while_let {
++ if Some(self.def_id) == var_def_id(self.cx, expr) {
++ self.var_used_after_while_let = true;
++ }
++ } else if self.iter_expr_id == expr.hir_id {
++ self.past_while_let = true;
++ }
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
++/// for `&T` and `&mut T`, such as `Vec`.
++#[rustfmt::skip]
++fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
++ // no walk_ptrs_ty: calling iter() on a reference can make sense because it
++ // will allow further borrows afterwards
++ let ty = cx.tables.expr_ty(e);
++ is_iterable_array(ty, cx) ||
++ is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
++ match_type(cx, ty, &paths::LINKED_LIST) ||
++ is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
++ is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
++ is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
++ match_type(cx, ty, &paths::BINARY_HEAP) ||
++ match_type(cx, ty, &paths::BTREEMAP) ||
++ match_type(cx, ty, &paths::BTREESET)
++}
++
++fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
++ // IntoIterator is currently only implemented for array sizes <= 32 in rustc
++ match ty.kind {
++ ty::Array(_, n) => {
++ if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) {
++ (0..=32).contains(&val)
++ } else {
++ false
++ }
++ },
++ _ => false,
++ }
++}
++
++/// If a block begins with a statement (possibly a `let` binding) and has an
++/// expression, return it.
++fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ if block.stmts.is_empty() {
++ return None;
++ }
++ if let StmtKind::Local(ref local) = block.stmts[0].kind {
++ if let Some(expr) = local.init {
++ Some(expr)
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++}
++
++/// If a block begins with an expression (with or without semicolon), return it.
++fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ match block.expr {
++ Some(ref expr) if block.stmts.is_empty() => Some(expr),
++ None if !block.stmts.is_empty() => match block.stmts[0].kind {
++ StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr),
++ StmtKind::Local(..) | StmtKind::Item(..) => None,
++ },
++ _ => None,
++ }
++}
++
++/// Returns `true` if expr contains a single break expr without destination label
++/// and
++/// passed expression. The expression may be within a block.
++fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
++ match expr.kind {
++ ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
++ ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
++ _ => false,
++ }
++}
++
++// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
++// incremented exactly once in the loop body, and initialized to zero
++// at the start of the loop.
++#[derive(Debug, PartialEq)]
++enum VarState {
++ Initial, // Not examined yet
++ IncrOnce, // Incremented exactly once, may be a loop counter
++ Declared, // Declared but not (yet) initialized to zero
++ Warn,
++ DontWarn,
++}
++
++/// Scan a for loop for variables that are incremented exactly once.
++struct IncrementVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>, // context reference
++ states: FxHashMap<HirId, VarState>, // incremented variables
++ depth: u32, // depth of conditional expressions
++ done: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if self.done {
++ return;
++ }
++
++ // If node is a variable
++ if let Some(def_id) = var_def_id(self.cx, expr) {
++ if let Some(parent) = get_parent_expr(self.cx, expr) {
++ let state = self.states.entry(def_id).or_insert(VarState::Initial);
++
++ match parent.kind {
++ ExprKind::AssignOp(op, ref lhs, ref rhs) => {
++ if lhs.hir_id == expr.hir_id {
++ if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) {
++ *state = match *state {
++ VarState::Initial if self.depth == 0 => VarState::IncrOnce,
++ _ => VarState::DontWarn,
++ };
++ } else {
++ // Assigned some other value
++ *state = VarState::DontWarn;
++ }
++ }
++ },
++ ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn,
++ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
++ *state = VarState::DontWarn
++ },
++ _ => (),
++ }
++ }
++ } else if is_loop(expr) || is_conditional(expr) {
++ self.depth += 1;
++ walk_expr(self, expr);
++ self.depth -= 1;
++ return;
++ } else if let ExprKind::Continue(_) = expr.kind {
++ self.done = true;
++ return;
++ }
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++/// Checks whether a variable is initialized to zero at the start of a loop.
++struct InitializeVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>, // context reference
++ end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
++ var_id: HirId,
++ state: VarState,
++ name: Option<Name>,
++ depth: u32, // depth of conditional expressions
++ past_loop: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++ // Look for declarations of the variable
++ if let StmtKind::Local(ref local) = stmt.kind {
++ if local.pat.hir_id == self.var_id {
++ if let PatKind::Binding(.., ident, _) = local.pat.kind {
++ self.name = Some(ident.name);
++
++ self.state = if let Some(ref init) = local.init {
++ if is_integer_const(&self.cx, init, 0) {
++ VarState::Warn
++ } else {
++ VarState::Declared
++ }
++ } else {
++ VarState::Declared
++ }
++ }
++ }
++ }
++ walk_stmt(self, stmt);
++ }
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if self.state == VarState::DontWarn {
++ return;
++ }
++ if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) {
++ self.past_loop = true;
++ return;
++ }
++ // No need to visit expressions before the variable is
++ // declared
++ if self.state == VarState::IncrOnce {
++ return;
++ }
++
++ // If node is the desired variable, see how it's used
++ if var_def_id(self.cx, expr) == Some(self.var_id) {
++ if let Some(parent) = get_parent_expr(self.cx, expr) {
++ match parent.kind {
++ ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => {
++ self.state = VarState::DontWarn;
++ },
++ ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => {
++ self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 {
++ VarState::Warn
++ } else {
++ VarState::DontWarn
++ }
++ },
++ ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
++ self.state = VarState::DontWarn
++ },
++ _ => (),
++ }
++ }
++
++ if self.past_loop {
++ self.state = VarState::DontWarn;
++ return;
++ }
++ } else if !self.past_loop && is_loop(expr) {
++ self.state = VarState::DontWarn;
++ return;
++ } else if is_conditional(expr) {
++ self.depth += 1;
++ walk_expr(self, expr);
++ self.depth -= 1;
++ return;
++ }
++ walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++ }
++}
++
++fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<HirId> {
++ if let ExprKind::Path(ref qpath) = expr.kind {
++ let path_res = qpath_res(cx, qpath, expr.hir_id);
++ if let Res::Local(hir_id) = path_res {
++ return Some(hir_id);
++ }
++ }
++ None
++}
++
++fn is_loop(expr: &Expr<'_>) -> bool {
++ match expr.kind {
++ ExprKind::Loop(..) => true,
++ _ => false,
++ }
++}
++
++fn is_conditional(expr: &Expr<'_>) -> bool {
++ match expr.kind {
++ ExprKind::Match(..) => true,
++ _ => false,
++ }
++}
++
++fn is_nested(cx: &LateContext<'_, '_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
++ if_chain! {
++ if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
++ let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
++ if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
++ then {
++ return is_loop_nested(cx, loop_expr, iter_expr)
++ }
++ }
++ false
++}
++
++fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
++ let mut id = loop_expr.hir_id;
++ let iter_name = if let Some(name) = path_name(iter_expr) {
++ name
++ } else {
++ return true;
++ };
++ loop {
++ let parent = cx.tcx.hir().get_parent_node(id);
++ if parent == id {
++ return false;
++ }
++ match cx.tcx.hir().find(parent) {
++ Some(Node::Expr(expr)) => {
++ if let ExprKind::Loop(..) = expr.kind {
++ return true;
++ };
++ },
++ Some(Node::Block(block)) => {
++ let mut block_visitor = LoopNestVisitor {
++ hir_id: id,
++ iterator: iter_name,
++ nesting: Unknown,
++ };
++ walk_block(&mut block_visitor, block);
++ if block_visitor.nesting == RuledOut {
++ return false;
++ }
++ },
++ Some(Node::Stmt(_)) => (),
++ _ => {
++ return false;
++ },
++ }
++ id = parent;
++ }
++}
++
++#[derive(PartialEq, Eq)]
++enum Nesting {
++ Unknown, // no nesting detected yet
++ RuledOut, // the iterator is initialized or assigned within scope
++ LookFurther, // no nesting detected, no further walk required
++}
++
++use self::Nesting::{LookFurther, RuledOut, Unknown};
++
++struct LoopNestVisitor {
++ hir_id: HirId,
++ iterator: Name,
++ nesting: Nesting,
++}
++
++impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
++ type Map = Map<'tcx>;
++
++ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++ if stmt.hir_id == self.hir_id {
++ self.nesting = LookFurther;
++ } else if self.nesting == Unknown {
++ walk_stmt(self, stmt);
++ }
++ }
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if self.nesting != Unknown {
++ return;
++ }
++ if expr.hir_id == self.hir_id {
++ self.nesting = LookFurther;
++ return;
++ }
++ match expr.kind {
++ ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => {
++ if match_var(path, self.iterator) {
++ self.nesting = RuledOut;
++ }
++ },
++ _ => walk_expr(self, expr),
++ }
++ }
++
++ fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
++ if self.nesting != Unknown {
++ return;
++ }
++ if let PatKind::Binding(.., span_name, _) = pat.kind {
++ if self.iterator == span_name.name {
++ self.nesting = RuledOut;
++ return;
++ }
++ }
++ walk_pat(self, pat)
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn path_name(e: &Expr<'_>) -> Option<Name> {
++ if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
++ let segments = &path.segments;
++ if segments.len() == 1 {
++ return Some(segments[0].ident.name);
++ }
++ };
++ None
++}
++
++fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
++ if constant(cx, cx.tables, cond).is_some() {
++ // A pure constant condition (e.g., `while false`) is not linted.
++ return;
++ }
++
++ let mut var_visitor = VarCollectorVisitor {
++ cx,
++ ids: FxHashSet::default(),
++ def_ids: FxHashMap::default(),
++ skip: false,
++ };
++ var_visitor.visit_expr(cond);
++ if var_visitor.skip {
++ return;
++ }
++ let used_in_condition = &var_visitor.ids;
++ let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) {
++ used_in_condition.is_disjoint(&used_mutably)
++ } else {
++ return;
++ };
++ let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
++
++ let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
++ has_break_or_return: false,
++ };
++ has_break_or_return_visitor.visit_expr(expr);
++ let has_break_or_return = has_break_or_return_visitor.has_break_or_return;
++
++ if no_cond_variable_mutated && !mutable_static_in_cond {
++ span_lint_and_then(
++ cx,
++ WHILE_IMMUTABLE_CONDITION,
++ cond.span,
++ "variables in the condition are not mutated in the loop body",
++ |diag| {
++ diag.note("this may lead to an infinite or to a never running loop");
++
++ if has_break_or_return {
++ diag.note("this loop contains `return`s or `break`s");
++ diag.help("rewrite it as `if cond { loop { } }`");
++ }
++ },
++ );
++ }
++}
++
++struct HasBreakOrReturnVisitor {
++ has_break_or_return: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if self.has_break_or_return {
++ return;
++ }
++
++ match expr.kind {
++ ExprKind::Ret(_) | ExprKind::Break(_, _) => {
++ self.has_break_or_return = true;
++ return;
++ },
++ _ => {},
++ }
++
++ walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++/// Collects the set of variables in an expression
++/// Stops analysis if a function call is found
++/// Note: In some cases such as `self`, there are no mutable annotation,
++/// All variables definition IDs are collected
++struct VarCollectorVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ ids: FxHashSet<HirId>,
++ def_ids: FxHashMap<def_id::DefId, bool>,
++ skip: bool,
++}
++
++impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
++ fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Path(ref qpath) = ex.kind;
++ if let QPath::Resolved(None, _) = *qpath;
++ let res = qpath_res(self.cx, qpath, ex.hir_id);
++ then {
++ match res {
++ Res::Local(hir_id) => {
++ self.ids.insert(hir_id);
++ },
++ Res::Def(DefKind::Static, def_id) => {
++ let mutable = self.cx.tcx.is_mutable_static(def_id);
++ self.def_ids.insert(def_id, mutable);
++ },
++ _ => {},
++ }
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
++ match ex.kind {
++ ExprKind::Path(_) => self.insert_def_id(ex),
++ // If there is any function/method call… we just stop analysis
++ ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true,
++
++ _ => walk_expr(self, ex),
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
++
++fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, 'tcx>) {
++ if_chain! {
++ if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
++ if let ExprKind::MethodCall(ref chain_method, _, _) = args[0].kind;
++ if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR);
++ if let Some(ref generic_args) = chain_method.args;
++ if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
++ then {
++ let ty = cx.tables.node_type(ty.hir_id);
++ if is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
++ is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
++ match_type(cx, ty, &paths::BTREEMAP) ||
++ is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
++ if method.ident.name == sym!(len) {
++ let span = shorten_needless_collect_span(expr);
++ span_lint_and_sugg(
++ cx,
++ NEEDLESS_COLLECT,
++ span,
++ NEEDLESS_COLLECT_MSG,
++ "replace with",
++ ".count()".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ if method.ident.name == sym!(is_empty) {
++ let span = shorten_needless_collect_span(expr);
++ span_lint_and_sugg(
++ cx,
++ NEEDLESS_COLLECT,
++ span,
++ NEEDLESS_COLLECT_MSG,
++ "replace with",
++ ".next().is_none()".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ if method.ident.name == sym!(contains) {
++ let contains_arg = snippet(cx, args[1].span, "??");
++ let span = shorten_needless_collect_span(expr);
++ span_lint_and_then(
++ cx,
++ NEEDLESS_COLLECT,
++ span,
++ NEEDLESS_COLLECT_MSG,
++ |diag| {
++ let (arg, pred) = if contains_arg.starts_with('&') {
++ ("x", &contains_arg[1..])
++ } else {
++ ("&x", &*contains_arg)
++ };
++ diag.span_suggestion(
++ span,
++ "replace with",
++ format!(
++ ".any(|{}| x == {})",
++ arg, pred
++ ),
++ Applicability::MachineApplicable,
++ );
++ }
++ );
++ }
++ }
++ }
++ }
++}
++
++fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
++ if_chain! {
++ if let ExprKind::MethodCall(_, _, ref args) = expr.kind;
++ if let ExprKind::MethodCall(_, ref span, _) = args[0].kind;
++ then {
++ return expr.span.with_lo(span.lo() - BytePos(1));
++ }
++ }
++ unreachable!()
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::edition::Edition;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `#[macro_use] use...`.
++ ///
++ /// **Why is this bad?** Since the Rust 2018 edition you can import
++ /// macro's directly, this is considered idiomatic.
++ ///
++ /// **Known problems:** This lint does not generate an auto-applicable suggestion.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// #[macro_use]
++ /// use lazy_static;
++ /// ```
++ pub MACRO_USE_IMPORTS,
++ pedantic,
++ "#[macro_use] is no longer needed"
++}
++
++declare_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
++
++impl EarlyLintPass for MacroUseImports {
++ fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) {
++ if_chain! {
++ if ecx.sess.opts.edition == Edition::Edition2018;
++ if let ast::ItemKind::Use(use_tree) = &item.kind;
++ if let Some(mac_attr) = item
++ .attrs
++ .iter()
++ .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
++ then {
++ let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
++ let help = format!("use {}::<macro name>", snippet(ecx, use_tree.span, "_"));
++ span_lint_and_sugg(
++ ecx,
++ MACRO_USE_IMPORTS,
++ mac_attr.span,
++ msg,
++ "remove the attribute and import the macro directly, try",
++ help,
++ Applicability::HasPlaceholders,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_hir::{Crate, Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help};
++use if_chain::if_chain;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for recursion using the entrypoint.
++ ///
++ /// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),
++ /// recursing into main() seems like an unintuitive antipattern we should be able to detect.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// fn main() {
++ /// main();
++ /// }
++ /// ```
++ pub MAIN_RECURSION,
++ style,
++ "recursion using the entrypoint"
++}
++
++#[derive(Default)]
++pub struct MainRecursion {
++ has_no_std_attr: bool,
++}
++
++impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
++
++impl LateLintPass<'_, '_> for MainRecursion {
++ fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate<'_>) {
++ self.has_no_std_attr = is_no_std_crate(krate);
++ }
++
++ fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if self.has_no_std_attr {
++ return;
++ }
++
++ if_chain! {
++ if let ExprKind::Call(func, _) = &expr.kind;
++ if let ExprKind::Path(path) = &func.kind;
++ if let QPath::Resolved(_, path) = &path;
++ if let Some(def_id) = path.res.opt_def_id();
++ if is_entrypoint_fn(cx, def_id);
++ then {
++ span_lint_and_help(
++ cx,
++ MAIN_RECURSION,
++ func.span,
++ &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
++ None,
++ "consider using another function for this recursion"
++ )
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths;
++use crate::utils::{
++ is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::Ident;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::Mutability;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
++ /// `iterator.cloned()` instead
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x = vec![42, 43];
++ /// let y = x.iter();
++ /// let z = y.map(|i| *i);
++ /// ```
++ ///
++ /// The correct use would be:
++ ///
++ /// ```rust
++ /// let x = vec![42, 43];
++ /// let y = x.iter();
++ /// let z = y.cloned();
++ /// ```
++ pub MAP_CLONE,
++ style,
++ "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
++}
++
++declare_lint_pass!(MapClone => [MAP_CLONE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapClone {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) {
++ if e.span.from_expansion() {
++ return;
++ }
++
++ if_chain! {
++ if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.kind;
++ if args.len() == 2;
++ if method.ident.as_str() == "map";
++ let ty = cx.tables.expr_ty(&args[0]);
++ if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR);
++ if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
++ let closure_body = cx.tcx.hir().body(body_id);
++ let closure_expr = remove_blocks(&closure_body.value);
++ then {
++ match closure_body.params[0].pat.kind {
++ hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
++ hir::BindingAnnotation::Unannotated, .., name, None
++ ) = inner.kind {
++ if ident_eq(name, closure_expr) {
++ lint(cx, e.span, args[0].span, true);
++ }
++ },
++ hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
++ match closure_expr.kind {
++ hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
++ if ident_eq(name, inner) {
++ if let ty::Ref(.., Mutability::Not) = cx.tables.expr_ty(inner).kind {
++ lint(cx, e.span, args[0].span, true);
++ }
++ }
++ },
++ hir::ExprKind::MethodCall(ref method, _, ref obj) => {
++ if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone"
++ && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
++
++ let obj_ty = cx.tables.expr_ty(&obj[0]);
++ if let ty::Ref(_, ty, _) = obj_ty.kind {
++ let copy = is_copy(cx, ty);
++ lint(cx, e.span, args[0].span, copy);
++ } else {
++ lint_needless_cloning(cx, e.span, args[0].span);
++ }
++ }
++ },
++ _ => {},
++ }
++ },
++ _ => {},
++ }
++ }
++ }
++ }
++}
++
++fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
++ if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind {
++ path.segments.len() == 1 && path.segments[0].ident == name
++ } else {
++ false
++ }
++}
++
++fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) {
++ span_lint_and_sugg(
++ cx,
++ MAP_CLONE,
++ root.trim_start(receiver).unwrap(),
++ "You are needlessly cloning iterator elements",
++ "Remove the `map` call",
++ String::new(),
++ Applicability::MachineApplicable,
++ )
++}
++
++fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, copied: bool) {
++ let mut applicability = Applicability::MachineApplicable;
++ if copied {
++ span_lint_and_sugg(
++ cx,
++ MAP_CLONE,
++ replace,
++ "You are using an explicit closure for copying elements",
++ "Consider calling the dedicated `copied` method",
++ format!(
++ "{}.copied()",
++ snippet_with_applicability(cx, root, "..", &mut applicability)
++ ),
++ applicability,
++ )
++ } else {
++ span_lint_and_sugg(
++ cx,
++ MAP_CLONE,
++ replace,
++ "You are using an explicit closure for cloning elements",
++ "Consider calling the dedicated `cloned` method",
++ format!(
++ "{}.cloned()",
++ snippet_with_applicability(cx, root, "..", &mut applicability)
++ ),
++ applicability,
++ )
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++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 usage of `option.map(f)` where f is a function
++ /// or closure that returns the unit type.
++ ///
++ /// **Why is this bad?** Readability, this can be written more clearly with
++ /// an if let statement
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # fn do_stuff() -> Option<String> { Some(String::new()) }
++ /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
++ /// # fn format_msg(foo: String) -> String { String::new() }
++ /// let x: Option<String> = do_stuff();
++ /// x.map(log_err_msg);
++ /// # let x: Option<String> = do_stuff();
++ /// x.map(|msg| log_err_msg(format_msg(msg)));
++ /// ```
++ ///
++ /// The correct use would be:
++ ///
++ /// ```rust
++ /// # fn do_stuff() -> Option<String> { Some(String::new()) }
++ /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
++ /// # fn format_msg(foo: String) -> String { String::new() }
++ /// let x: Option<String> = do_stuff();
++ /// if let Some(msg) = x {
++ /// log_err_msg(msg);
++ /// }
++ ///
++ /// # let x: Option<String> = do_stuff();
++ /// if let Some(msg) = x {
++ /// log_err_msg(format_msg(msg));
++ /// }
++ /// ```
++ pub OPTION_MAP_UNIT_FN,
++ complexity,
++ "using `option.map(f)`, where `f` is a function or closure that returns `()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `result.map(f)` where f is a function
++ /// or closure that returns the unit type.
++ ///
++ /// **Why is this bad?** Readability, this can be written more clearly with
++ /// an if let statement
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
++ /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
++ /// # fn format_msg(foo: String) -> String { String::new() }
++ /// let x: Result<String, String> = do_stuff();
++ /// x.map(log_err_msg);
++ /// # let x: Result<String, String> = do_stuff();
++ /// x.map(|msg| log_err_msg(format_msg(msg)));
++ /// ```
++ ///
++ /// The correct use would be:
++ ///
++ /// ```rust
++ /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
++ /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
++ /// # fn format_msg(foo: String) -> String { String::new() }
++ /// let x: Result<String, String> = do_stuff();
++ /// if let Ok(msg) = x {
++ /// log_err_msg(msg);
++ /// };
++ /// # let x: Result<String, String> = do_stuff();
++ /// if let Ok(msg) = x {
++ /// log_err_msg(format_msg(msg));
++ /// };
++ /// ```
++ pub RESULT_MAP_UNIT_FN,
++ complexity,
++ "using `result.map(f)`, where `f` is a function or closure that returns `()`"
++}
++
++declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
++
++fn is_unit_type(ty: Ty<'_>) -> bool {
++ match ty.kind {
++ ty::Tuple(slice) => slice.is_empty(),
++ ty::Never => true,
++ _ => false,
++ }
++}
++
++fn is_unit_function(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool {
++ let ty = cx.tables.expr_ty(expr);
++
++ if let ty::FnDef(id, _) = ty.kind {
++ if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
++ return is_unit_type(fn_type.output());
++ }
++ }
++ false
++}
++
++fn is_unit_expression(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool {
++ is_unit_type(cx.tables.expr_ty(expr))
++}
++
++/// The expression inside a closure may or may not have surrounding braces and
++/// semicolons, which causes problems when generating a suggestion. Given an
++/// expression that evaluates to '()' or '!', recursively remove useless braces
++/// and semi-colons until is suitable for including in the suggestion template
++fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr<'_>) -> Option<Span> {
++ if !is_unit_expression(cx, expr) {
++ return None;
++ }
++
++ match expr.kind {
++ hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _) => {
++ // Calls can't be reduced any more
++ Some(expr.span)
++ },
++ hir::ExprKind::Block(ref block, _) => {
++ match (&block.stmts[..], block.expr.as_ref()) {
++ (&[], Some(inner_expr)) => {
++ // If block only contains an expression,
++ // reduce `{ X }` to `X`
++ reduce_unit_expression(cx, inner_expr)
++ },
++ (&[ref inner_stmt], None) => {
++ // If block only contains statements,
++ // reduce `{ X; }` to `X` or `X;`
++ match inner_stmt.kind {
++ hir::StmtKind::Local(ref local) => Some(local.span),
++ hir::StmtKind::Expr(ref e) => Some(e.span),
++ hir::StmtKind::Semi(..) => Some(inner_stmt.span),
++ hir::StmtKind::Item(..) => None,
++ }
++ },
++ _ => {
++ // For closures that contain multiple statements
++ // it's difficult to get a correct suggestion span
++ // for all cases (multi-line closures specifically)
++ //
++ // We do not attempt to build a suggestion for those right now.
++ None
++ },
++ }
++ },
++ _ => None,
++ }
++}
++
++fn unit_closure<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'a hir::Expr<'a>,
++) -> Option<(&'tcx hir::Param<'tcx>, &'a hir::Expr<'a>)> {
++ if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind {
++ let body = cx.tcx.hir().body(inner_expr_id);
++ let body_expr = &body.value;
++
++ if_chain! {
++ if decl.inputs.len() == 1;
++ if is_unit_expression(cx, body_expr);
++ if let Some(binding) = iter_input_pats(&decl, body).next();
++ then {
++ return Some((binding, body_expr));
++ }
++ }
++ }
++ None
++}
++
++/// Builds a name for the let binding variable (`var_arg`)
++///
++/// `x.field` => `x_field`
++/// `y` => `_y`
++///
++/// Anything else will return `a`.
++fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr<'_>) -> String {
++ match &var_arg.kind {
++ hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
++ hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
++ _ => "a".to_string(),
++ }
++}
++
++#[must_use]
++fn suggestion_msg(function_type: &str, map_type: &str) -> String {
++ format!(
++ "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type",
++ map_type, function_type
++ )
++}
++
++fn lint_map_unit_fn(cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) {
++ let var_arg = &map_args[0];
++
++ let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(option_type)) {
++ ("Option", "Some", OPTION_MAP_UNIT_FN)
++ } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(result_type)) {
++ ("Result", "Ok", RESULT_MAP_UNIT_FN)
++ } else {
++ return;
++ };
++ let fn_arg = &map_args[1];
++
++ if is_unit_function(cx, fn_arg) {
++ let msg = suggestion_msg("function", map_type);
++ let suggestion = format!(
++ "if let {0}({binding}) = {1} {{ {2}({binding}) }}",
++ variant,
++ snippet(cx, var_arg.span, "_"),
++ snippet(cx, fn_arg.span, "_"),
++ binding = let_binding_name(cx, var_arg)
++ );
++
++ span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
++ diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
++ });
++ } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
++ let msg = suggestion_msg("closure", map_type);
++
++ span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
++ if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
++ let suggestion = format!(
++ "if let {0}({1}) = {2} {{ {3} }}",
++ variant,
++ snippet(cx, binding.pat.span, "_"),
++ snippet(cx, var_arg.span, "_"),
++ snippet(cx, reduced_expr_span, "_")
++ );
++ diag.span_suggestion(
++ stmt.span,
++ "try this",
++ suggestion,
++ Applicability::MachineApplicable, // snippet
++ );
++ } else {
++ let suggestion = format!(
++ "if let {0}({1}) = {2} {{ ... }}",
++ variant,
++ snippet(cx, binding.pat.span, "_"),
++ snippet(cx, var_arg.span, "_"),
++ );
++ diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders);
++ }
++ });
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapUnit {
++ fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
++ if stmt.span.from_expansion() {
++ return;
++ }
++
++ if let hir::StmtKind::Semi(ref expr) = stmt.kind {
++ if let Some(arglists) = method_chain_args(expr, &["map"]) {
++ lint_map_unit_fn(cx, stmt, expr, arglists[0]);
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, MatchSource};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
++ ///
++ /// **Why is this bad?** This can panic at runtime.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust, no_run
++ /// let arr = vec![0, 1, 2, 3];
++ /// let idx = 1;
++ ///
++ /// // Bad
++ /// match arr[idx] {
++ /// 0 => println!("{}", 0),
++ /// 1 => println!("{}", 3),
++ /// _ => {},
++ /// }
++ /// ```
++ /// Use instead:
++ /// ```rust, no_run
++ /// let arr = vec![0, 1, 2, 3];
++ /// let idx = 1;
++ ///
++ /// // Good
++ /// match arr.get(idx) {
++ /// Some(0) => println!("{}", 0),
++ /// Some(1) => println!("{}", 3),
++ /// _ => {},
++ /// }
++ /// ```
++ pub MATCH_ON_VEC_ITEMS,
++ correctness,
++ "matching on vector elements can panic"
++}
++
++declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++ if_chain! {
++ if !in_external_macro(cx.sess(), expr.span);
++ if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind;
++ if let Some(idx_expr) = is_vec_indexing(cx, match_expr);
++ if let ExprKind::Index(vec, idx) = idx_expr.kind;
++
++ then {
++ // FIXME: could be improved to suggest surrounding every pattern with Some(_),
++ // but only when `or_patterns` are stabilized.
++ span_lint_and_sugg(
++ cx,
++ MATCH_ON_VEC_ITEMS,
++ match_expr.span,
++ "indexing into a vector may panic",
++ "try this",
++ format!(
++ "{}.get({})",
++ snippet(cx, vec.span, ".."),
++ snippet(cx, idx.span, "..")
++ ),
++ Applicability::MaybeIncorrect
++ );
++ }
++ }
++ }
++}
++
++fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ if_chain! {
++ if let ExprKind::Index(ref array, _) = expr.kind;
++ let ty = cx.tables.expr_ty(array);
++ let ty = walk_ptrs_ty(ty);
++ if is_type_diagnostic_item(cx, ty, sym!(vec_type));
++
++ then {
++ return Some(expr);
++ }
++ }
++
++ None
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, miri_to_const, Constant};
++use crate::utils::paths;
++use crate::utils::sugg::Sugg;
++use crate::utils::usage::is_unused;
++use crate::utils::{
++ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
++ is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
++ snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
++ span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::def::CtorKind;
++use rustc_hir::{
++ Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
++ QPath, RangeEnd,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use std::cmp::Ordering;
++use std::collections::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");
++ /// match x {
++ /// Some(ref foo) => 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
++ /// 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, just like
++ /// 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,
++ style,
++ "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;
++ /// let r: Option<&()> = match x {
++ /// None => None,
++ /// Some(ref v) => Some(v),
++ /// };
++ /// ```
++ pub MATCH_AS_REF,
++ complexity,
++ "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for wildcard enum matches using `_`.
++ ///
++ /// **Why is this bad?** New enum variants added by library updates can be missed.
++ ///
++ /// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
++ /// variants, and also may not use correct path to enum if it's not present in the current scope.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # enum Foo { A(usize), B(usize) }
++ /// # let x = Foo::B(1);
++ /// match x {
++ /// A => {},
++ /// _ => {},
++ /// }
++ /// ```
++ pub WILDCARD_ENUM_MATCH_ARM,
++ restriction,
++ "a wildcard enum match arm using `_`"
++}
++
++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
++ /// match "foo" {
++ /// "a" => {},
++ /// "bar" | _ => {},
++ /// }
++ /// ```
++ 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"
++}
++
++#[derive(Default)]
++pub struct Matches {
++ infallible_destructuring_match_linted: bool,
++}
++
++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,
++ WILDCARD_IN_OR_PATTERNS,
++ MATCH_SINGLE_BINDING,
++ INFALLIBLE_DESTRUCTURING_MATCH,
++ REST_PAT_IN_FULLY_BOUND_STRUCTS
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++ if let ExprKind::Match(ref ex, ref 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(ref ex, ref arms, _) = expr.kind {
++ check_match_ref_pats(cx, ex, arms, expr);
++ }
++ }
++
++ fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) {
++ if_chain! {
++ if !in_external_macro(cx.sess(), local.span);
++ if !in_macro(local.span);
++ if let Some(ref expr) = local.init;
++ if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind;
++ if arms.len() == 1 && arms[0].guard.is_none();
++ if let PatKind::TupleStruct(
++ QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind;
++ if args.len() == 1;
++ if let Some(arg) = get_arg_name(&args[0]);
++ let body = remove_blocks(&arms[0].body);
++ if match_var(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<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++ if_chain! {
++ if !in_external_macro(cx.sess(), pat.span);
++ if !in_macro(pat.span);
++ if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
++ if let QPath::Resolved(_, ref path) = qpath;
++ 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",
++ );
++ }
++ }
++ }
++}
++
++#[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 = remove_blocks(&arms[1].body);
++ let els = if is_unit_expr(els) {
++ None
++ } else if let ExprKind::Block(_, _) = els.kind {
++ // matches with blocks that contain statements are prettier as `if let + else`
++ Some(els)
++ } else {
++ // allow match arms with just expressions
++ return;
++ };
++ let ty = cx.tables.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)))
++ });
++ span_lint_and_sugg(
++ cx,
++ lint,
++ expr.span,
++ "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
++ let`",
++ "try this",
++ 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,
++ ),
++ 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, ref 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.tables.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(ref 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<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
++ if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
++ let ranges = all_ranges(cx, arms, cx.tables.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(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
++ let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
++ if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
++ for arm in arms {
++ if let PatKind::TupleStruct(ref path, ref 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`)
++ inner.iter().for_each(|pat| {
++ if let PatKind::Binding(.., ident, None) = &pat.kind {
++ if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
++ ident_bind_name = (&ident.name.as_str()).to_string();
++ matching_wild = true;
++ }
++ }
++ });
++ }
++ if_chain! {
++ if matching_wild;
++ if let ExprKind::Block(ref 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",
++ );
++ }
++ }
++ }
++ }
++ }
++ }
++}
++
++fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
++ let ty = cx.tables.expr_ty(ex);
++ if !ty.is_enum() {
++ // If there isn't a nice closed set of possible values that can be conveniently enumerated,
++ // don't complain about not enumerating the mall.
++ 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;
++ for arm in arms {
++ if let PatKind::Wild = arm.pat.kind {
++ wildcard_span = Some(arm.pat.span);
++ } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
++ wildcard_span = Some(arm.pat.span);
++ wildcard_ident = Some(ident);
++ }
++ }
++
++ if let Some(wildcard_span) = wildcard_span {
++ // Accumulate the variants which should be put in place of the wildcard because they're not
++ // already covered.
++
++ let mut missing_variants = vec![];
++ if let ty::Adt(def, _) = ty.kind {
++ for variant in &def.variants {
++ missing_variants.push(variant);
++ }
++ }
++
++ for arm in arms {
++ if arm.guard.is_some() {
++ // 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.
++ continue;
++ }
++ if let PatKind::Path(ref path) = arm.pat.kind {
++ if let QPath::Resolved(_, p) = path {
++ missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
++ }
++ } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind {
++ if let QPath::Resolved(_, p) = path {
++ missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
++ }
++ }
++ }
++
++ let mut suggestion: Vec<String> = missing_variants
++ .iter()
++ .map(|v| {
++ let suffix = match v.ctor_kind {
++ CtorKind::Fn => "(..)",
++ CtorKind::Const | CtorKind::Fictive => "",
++ };
++ let ident_str = if let Some(ident) = wildcard_ident {
++ format!("{} @ ", ident.name)
++ } else {
++ String::new()
++ };
++ // This path assumes that the enum type is imported into scope.
++ format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
++ })
++ .collect();
++
++ if suggestion.is_empty() {
++ return;
++ }
++
++ let mut message = "wildcard match will miss any future added variants";
++
++ if let ty::Adt(def, _) = ty.kind {
++ if def.is_variant_list_non_exhaustive() {
++ message = "match on non-exhaustive enum doesn't explicitly match all known variants";
++ suggestion.push(String::from("_"));
++ }
++ }
++
++ span_lint_and_sugg(
++ cx,
++ WILDCARD_ENUM_MATCH_ARM,
++ wildcard_span,
++ message,
++ "try this",
++ suggestion.join(" | "),
++ Applicability::MachineApplicable,
++ )
++ }
++}
++
++// 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(ref 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, ref 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(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.to_owned(), 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(&arms[0]) {
++ is_ref_some_arm(&arms[1])
++ } else if is_none_arm(&arms[1]) {
++ is_ref_some_arm(&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.tables.expr_ty(expr);
++ let input_ty = cx.tables.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(ref 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.",
++ );
++ }
++ }
++ }
++}
++
++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;
++ }
++ 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.tables.expr_ty(&match_body).is_unit() {
++ snippet_body.push(';');
++ }
++ },
++ _ => {
++ // expr_ty(body) == ()
++ if cx.tables.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);
++ }
++ };
++ (
++ expr.span,
++ format!(
++ "{}let {} = {};\n{}{}{}",
++ cbrace_start,
++ snippet_with_applicability(cx, bind_names, "..", &mut applicability),
++ snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
++ indent,
++ snippet_body,
++ cbrace_end
++ ),
++ )
++ };
++ span_lint_and_sugg(
++ cx,
++ MATCH_SINGLE_BINDING,
++ target_span,
++ "this match could be written as a `let` statement",
++ "consider using `let` statement",
++ sugg,
++ applicability,
++ );
++ },
++ PatKind::Wild => {
++ span_lint_and_sugg(
++ cx,
++ MATCH_SINGLE_BINDING,
++ expr.span,
++ "this match could be replaced by its body itself",
++ "consider using the match body instead",
++ snippet_body,
++ Applicability::MachineApplicable,
++ );
++ },
++ _ => (),
++ }
++}
++
++/// Returns true if the `ex` match expression is in a local (`let`) statement
++fn opt_parent_let<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
++ if_chain! {
++ let map = &cx.tcx.hir();
++ 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<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ arms: &'tcx [Arm<'_>],
++ ty: Ty<'tcx>,
++) -> Vec<SpannedRange<Constant>> {
++ arms.iter()
++ .flat_map(|arm| {
++ if let Arm {
++ ref 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.tables, lhs)?.0,
++ None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
++ };
++ let rhs = match rhs {
++ Some(rhs) => constant(cx, cx.tables, 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(ref value) = pat.kind {
++ let value = constant(cx, cx.tables, 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(ref v) if v.is_empty() => true,
++ ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
++ _ => false,
++ }
++}
++
++// Checks if arm has the form `None => None`
++fn is_none_arm(arm: &Arm<'_>) -> bool {
++ match arm.pat.kind {
++ PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
++ _ => false,
++ }
++}
++
++// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
++fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
++ if_chain! {
++ if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
++ if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
++ if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
++ if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
++ if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
++ if let ExprKind::Path(ref some_path) = e.kind;
++ if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
++ if let ExprKind::Path(ref qpath) = args[0].kind;
++ if let &QPath::Resolved(_, ref path2) = qpath;
++ 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 values.iter().zip(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) => (),
++ _ => return Some((a.range(), b.range())),
++ }
++ }
++
++ 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))
++ ],)
++ );
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use std::iter;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
++ ///
++ /// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
++ /// is unspecified.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::mem;
++ ///
++ /// mem::discriminant(&"hello");
++ /// mem::discriminant(&&Some(2));
++ /// ```
++ pub MEM_DISCRIMINANT_NON_ENUM,
++ correctness,
++ "calling `mem::descriminant` on non-enum type"
++}
++
++declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref func, ref func_args) = expr.kind;
++ // is `mem::discriminant`
++ if let ExprKind::Path(ref func_qpath) = func.kind;
++ if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT);
++ // type is non-enum
++ let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
++ if !ty_param.is_enum();
++
++ then {
++ span_lint_and_then(
++ cx,
++ MEM_DISCRIMINANT_NON_ENUM,
++ expr.span,
++ &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param),
++ |diag| {
++ // if this is a reference to an enum, suggest dereferencing
++ let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param);
++ if ptr_depth >= 1 && base_ty.is_enum() {
++ let param = &func_args[0];
++
++ // cancel out '&'s first
++ let mut derefs_needed = ptr_depth;
++ let mut cur_expr = param;
++ while derefs_needed > 0 {
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind {
++ derefs_needed -= 1;
++ cur_expr = inner_expr;
++ } else {
++ break;
++ }
++ }
++
++ let derefs: String = iter::repeat('*').take(derefs_needed).collect();
++ diag.span_suggestion(
++ param.span,
++ "try dereferencing",
++ format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")),
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ )
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, qpath_res, span_lint};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is
++ /// `Drop`.
++ ///
++ /// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its
++ /// destructor, possibly causing leaks.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::mem;
++ /// # use std::rc::Rc;
++ /// mem::forget(Rc::new(55))
++ /// ```
++ pub MEM_FORGET,
++ restriction,
++ "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
++}
++
++declare_lint_pass!(MemForget => [MEM_FORGET]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::Call(ref path_expr, ref args) = e.kind {
++ if let ExprKind::Path(ref qpath) = path_expr.kind {
++ if let Some(def_id) = qpath_res(cx, qpath, path_expr.hir_id).opt_def_id() {
++ if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++ let forgot_ty = cx.tables.expr_ty(&args[0]);
++
++ if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
++ span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
++ }
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
++ span_lint_and_sugg, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::sym;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `mem::replace()` on an `Option` with
++ /// `None`.
++ ///
++ /// **Why is this bad?** `Option` already has the method `take()` for
++ /// taking its current value (Some(..) or None) and replacing it with
++ /// `None`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::mem;
++ ///
++ /// let mut an_option = Some(0);
++ /// let replaced = mem::replace(&mut an_option, None);
++ /// ```
++ /// Is better expressed with:
++ /// ```rust
++ /// let mut an_option = Some(0);
++ /// let taken = an_option.take();
++ /// ```
++ pub MEM_REPLACE_OPTION_WITH_NONE,
++ style,
++ "replacing an `Option` with `None` instead of `take()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
++ /// and `mem::replace(&mut _, mem::zeroed())`.
++ ///
++ /// **Why is this bad?** This will lead to undefined behavior even if the
++ /// value is overwritten later, because the uninitialized value may be
++ /// observed in the case of a panic.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```
++ /// use std::mem;
++ ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
++ ///
++ /// #[allow(deprecated, invalid_value)]
++ /// fn myfunc (v: &mut Vec<i32>) {
++ /// let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
++ /// let new_v = may_panic(taken_v); // undefined behavior on panic
++ /// mem::forget(mem::replace(v, new_v));
++ /// }
++ /// ```
++ ///
++ /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
++ /// at the cost of either lazily creating a replacement value or aborting
++ /// on panic, to ensure that the uninitialized value cannot be observed.
++ pub MEM_REPLACE_WITH_UNINIT,
++ correctness,
++ "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `std::mem::replace` on a value of type
++ /// `T` with `T::default()`.
++ ///
++ /// **Why is this bad?** `std::mem` module already has the method `take` to
++ /// take the current value and replace it with the default value of that type.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let mut text = String::from("foo");
++ /// let replaced = std::mem::replace(&mut text, String::default());
++ /// ```
++ /// Is better expressed with:
++ /// ```rust
++ /// let mut text = String::from("foo");
++ /// let taken = std::mem::take(&mut text);
++ /// ```
++ pub MEM_REPLACE_WITH_DEFAULT,
++ style,
++ "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
++}
++
++declare_lint_pass!(MemReplace =>
++ [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
++
++fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
++ if let ExprKind::Path(ref replacement_qpath) = src.kind {
++ // Check that second argument is `Option::None`
++ if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
++ // Since this is a late pass (already type-checked),
++ // and we already know that the second argument is an
++ // `Option`, we do not need to check the first
++ // argument's type. All that's left is to get
++ // replacee's path.
++ let replaced_path = match dest.kind {
++ ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
++ if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
++ replaced_path
++ } else {
++ return;
++ }
++ },
++ ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
++ _ => return,
++ };
++
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ MEM_REPLACE_OPTION_WITH_NONE,
++ expr_span,
++ "replacing an `Option` with `None`",
++ "consider `Option::take()` instead",
++ format!(
++ "{}.take()",
++ snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
++ ),
++ applicability,
++ );
++ }
++ }
++}
++
++fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) {
++ if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind {
++ if_chain! {
++ if repl_args.is_empty();
++ if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
++ if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
++ then {
++ if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
++ span_lint_and_help(
++ cx,
++ MEM_REPLACE_WITH_UNINIT,
++ expr_span,
++ "replacing with `mem::uninitialized()`",
++ None,
++ "consider using the `take_mut` crate instead",
++ );
++ } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
++ !cx.tables.expr_ty(src).is_primitive() {
++ span_lint_and_help(
++ cx,
++ MEM_REPLACE_WITH_UNINIT,
++ expr_span,
++ "replacing with `mem::zeroed()`",
++ None,
++ "consider using a default value or the `take_mut` crate instead",
++ );
++ }
++ }
++ }
++ }
++}
++
++fn check_replace_with_default(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
++ if let ExprKind::Call(ref repl_func, _) = src.kind {
++ if_chain! {
++ if !in_external_macro(cx.tcx.sess, expr_span);
++ if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
++ if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
++ if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
++ then {
++ span_lint_and_then(
++ cx,
++ MEM_REPLACE_WITH_DEFAULT,
++ expr_span,
++ "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
++ |diag| {
++ if !in_macro(expr_span) {
++ let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
++
++ diag.span_suggestion(
++ expr_span,
++ "consider using",
++ suggestion,
++ Applicability::MachineApplicable
++ );
++ }
++ }
++ );
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ // Check that `expr` is a call to `mem::replace()`
++ if let ExprKind::Call(ref func, ref func_args) = expr.kind;
++ if let ExprKind::Path(ref func_qpath) = func.kind;
++ if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::MEM_REPLACE);
++ if let [dest, src] = &**func_args;
++ then {
++ check_replace_option_with_none(cx, src, dest, expr.span);
++ check_replace_with_uninit(cx, src, expr.span);
++ check_replace_with_default(cx, src, dest, expr.span);
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use super::INEFFICIENT_TO_STRING;
++use crate::utils::{
++ is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty};
++
++/// Checks for the `INEFFICIENT_TO_STRING` lint
++pub fn lint<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
++ if_chain! {
++ if let Some(to_string_meth_did) = cx.tables.type_dependent_def_id(expr.hir_id);
++ if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
++ if let Some(substs) = cx.tables.node_substs_opt(expr.hir_id);
++ let self_ty = substs.type_at(0);
++ let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
++ if deref_count >= 1;
++ if specializes_tostring(cx, deref_self_ty);
++ then {
++ span_lint_and_then(
++ cx,
++ INEFFICIENT_TO_STRING,
++ expr.span,
++ &format!("calling `to_string` on `{}`", arg_ty),
++ |diag| {
++ diag.help(&format!(
++ "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`",
++ self_ty, deref_self_ty
++ ));
++ let mut applicability = Applicability::MachineApplicable;
++ let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
++ diag.span_suggestion(
++ expr.span,
++ "try dereferencing the receiver",
++ format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet),
++ applicability,
++ );
++ },
++ );
++ }
++ }
++}
++
++/// Returns whether `ty` specializes `ToString`.
++/// Currently, these are `str`, `String`, and `Cow<'_, str>`.
++fn specializes_tostring(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++ if let ty::Str = ty.kind {
++ return true;
++ }
++
++ if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++ return true;
++ }
++
++ if let ty::Adt(adt, substs) = ty.kind {
++ match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str()
++ } else {
++ false
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_target::abi::LayoutOf;
++
++pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) {
++ let unwrap_arg = &args[0][1];
++ let arith_lhs = &args[1][0];
++ let arith_rhs = &args[1][1];
++
++ let ty = cx.tables.expr_ty(arith_lhs);
++ if !ty.is_integral() {
++ return;
++ }
++
++ let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
++ mm
++ } else {
++ return;
++ };
++
++ if ty.is_signed() {
++ use self::{
++ MinMax::{Max, Min},
++ Sign::{Neg, Pos},
++ };
++
++ let sign = if let Some(sign) = lit_sign(arith_rhs) {
++ sign
++ } else {
++ return;
++ };
++
++ match (arith, sign, mm) {
++ ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
++ // "mul" is omitted because lhs can be negative.
++ _ => return,
++ }
++
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ super::MANUAL_SATURATING_ARITHMETIC,
++ expr.span,
++ "manual saturating arithmetic",
++ &format!("try using `saturating_{}`", arith),
++ format!(
++ "{}.saturating_{}({})",
++ snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
++ arith,
++ snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
++ ),
++ applicability,
++ );
++ } else {
++ match (mm, arith) {
++ (MinMax::Max, "add") | (MinMax::Max, "mul") | (MinMax::Min, "sub") => (),
++ _ => return,
++ }
++
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ super::MANUAL_SATURATING_ARITHMETIC,
++ expr.span,
++ "manual saturating arithmetic",
++ &format!("try using `saturating_{}`", arith),
++ format!(
++ "{}.saturating_{}({})",
++ snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
++ arith,
++ snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
++ ),
++ applicability,
++ );
++ }
++}
++
++#[derive(PartialEq, Eq)]
++enum MinMax {
++ Min,
++ Max,
++}
++
++fn is_min_or_max<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
++ // `T::max_value()` `T::min_value()` inherent methods
++ if_chain! {
++ if let hir::ExprKind::Call(func, args) = &expr.kind;
++ if args.is_empty();
++ if let hir::ExprKind::Path(path) = &func.kind;
++ if let hir::QPath::TypeRelative(_, segment) = path;
++ then {
++ match &*segment.ident.as_str() {
++ "max_value" => return Some(MinMax::Max),
++ "min_value" => return Some(MinMax::Min),
++ _ => {}
++ }
++ }
++ }
++
++ let ty = cx.tables.expr_ty(expr);
++ let ty_str = ty.to_string();
++
++ // `std::T::MAX` `std::T::MIN` constants
++ if let hir::ExprKind::Path(path) = &expr.kind {
++ if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
++ return Some(MinMax::Max);
++ }
++
++ if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
++ return Some(MinMax::Min);
++ }
++ }
++
++ // Literals
++ let bits = cx.layout_of(ty).unwrap().size.bits();
++ let (minval, maxval): (u128, u128) = if ty.is_signed() {
++ let minval = 1 << (bits - 1);
++ let mut maxval = !(1 << (bits - 1));
++ if bits != 128 {
++ maxval &= (1 << bits) - 1;
++ }
++ (minval, maxval)
++ } else {
++ (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
++ };
++
++ let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
++ if let hir::ExprKind::Lit(lit) = &expr.kind {
++ if let ast::LitKind::Int(value, _) = lit.node {
++ if value == maxval {
++ return Some(MinMax::Max);
++ }
++
++ if check_min && value == minval {
++ return Some(MinMax::Min);
++ }
++ }
++ }
++
++ None
++ };
++
++ if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
++ return r;
++ }
++
++ if ty.is_signed() {
++ if let hir::ExprKind::Unary(hir::UnOp::UnNeg, val) = &expr.kind {
++ return check_lit(val, true);
++ }
++ }
++
++ None
++}
++
++#[derive(PartialEq, Eq)]
++enum Sign {
++ Pos,
++ Neg,
++}
++
++fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
++ if let hir::ExprKind::Unary(hir::UnOp::UnNeg, inner) = &expr.kind {
++ if let hir::ExprKind::Lit(..) = &inner.kind {
++ return Some(Sign::Neg);
++ }
++ } else if let hir::ExprKind::Lit(..) = &expr.kind {
++ return Some(Sign::Pos);
++ }
++
++ None
++}
--- /dev/null
--- /dev/null
++mod inefficient_to_string;
++mod manual_saturating_arithmetic;
++mod option_map_unwrap_or;
++mod unnecessary_filter_map;
++
++use std::borrow::Cow;
++use std::fmt;
++use std::iter;
++
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{self, Visitor};
++use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::subst::GenericArgKind;
++use rustc_middle::ty::{self, Predicate, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::{sym, SymbolStr};
++
++use crate::consts::{constant, Constant};
++use crate::utils::usage::mutated_variables;
++use crate::utils::{
++ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
++ is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
++ match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
++ remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
++ snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
++ span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.unwrap()` calls on `Option`s.
++ ///
++ /// **Why is this bad?** Usually it is better to handle the `None` case, or to
++ /// 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.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// Using unwrap on an `Option`:
++ ///
++ /// ```rust
++ /// let opt = Some(1);
++ /// opt.unwrap();
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// let opt = Some(1);
++ /// opt.expect("more helpful message");
++ /// ```
++ pub OPTION_UNWRAP_USED,
++ restriction,
++ "using `Option.unwrap()`, which should at least get a better message using `expect()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.unwrap()` calls on `Result`s.
++ ///
++ /// **Why is this bad?** `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.
++ ///
++ /// **Example:**
++ /// Using unwrap on an `Result`:
++ ///
++ /// ```rust
++ /// let res: Result<usize, ()> = Ok(1);
++ /// res.unwrap();
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// let res: Result<usize, ()> = Ok(1);
++ /// res.expect("more helpful message");
++ /// ```
++ pub RESULT_UNWRAP_USED,
++ restriction,
++ "using `Result.unwrap()`, which might be better handled"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.expect()` calls on `Option`s.
++ ///
++ /// **Why is this bad?** Usually it is better to handle the `None` case. Still,
++ /// for a lot of quick-and-dirty code, `expect` is a good choice, which is why
++ /// this lint is `Allow` by default.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// Using expect on an `Option`:
++ ///
++ /// ```rust
++ /// let opt = Some(1);
++ /// opt.expect("one");
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust,ignore
++ /// let opt = Some(1);
++ /// opt?;
++ /// ```
++ pub OPTION_EXPECT_USED,
++ restriction,
++ "using `Option.expect()`, which might be better handled"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.expect()` calls on `Result`s.
++ ///
++ /// **Why is this bad?** `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.
++ ///
++ /// **Example:**
++ /// Using expect on an `Result`:
++ ///
++ /// ```rust
++ /// let res: Result<usize, ()> = Ok(1);
++ /// res.expect("one");
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// let res: Result<usize, ()> = Ok(1);
++ /// res?;
++ /// # Ok::<(), ()>(())
++ /// ```
++ pub RESULT_EXPECT_USED,
++ restriction,
++ "using `Result.expect()`, 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 |`self` taken |
++ /// |-------|----------------------|
++ /// |`as_` |`&self` or `&mut self`|
++ /// |`from_`| none |
++ /// |`into_`|`self` |
++ /// |`is_` |`&self` or none |
++ /// |`to_` |`&self` |
++ ///
++ /// **Why is this bad?** Consistency breeds readability. If you follow the
++ /// conventions, your users won't be surprised that they, e.g., need to supply a
++ /// mutable reference to a `as_..` function.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # struct X;
++ /// impl X {
++ /// fn as_str(self) -> &'static str {
++ /// // ..
++ /// # ""
++ /// }
++ /// }
++ /// ```
++ pub WRONG_SELF_CONVENTION,
++ style,
++ "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This is the same as
++ /// [`wrong_self_convention`](#wrong_self_convention), but for public items.
++ ///
++ /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).
++ ///
++ /// **Known problems:** Actually *renaming* the function may break clients if
++ /// the function is part of the public interface. In that case, be mindful of
++ /// the stability guarantees you've given your users.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # struct X;
++ /// impl<'a> X {
++ /// pub fn as_str(self) -> &'a str {
++ /// "foo"
++ /// }
++ /// }
++ /// ```
++ pub WRONG_PUB_SELF_CONVENTION,
++ restriction,
++ "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `ok().expect(..)`.
++ ///
++ /// **Why is this bad?** Because you usually call `expect()` on the `Result`
++ /// directly to get a better error message.
++ ///
++ /// **Known problems:** The error type needs to implement `Debug`
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = Ok::<_, ()>(());
++ /// x.ok().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 `_.map(_).unwrap_or(_)`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.map_or(_, _)`.
++ ///
++ /// **Known problems:** The order of the arguments is not in execution order
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = Some(1);
++ /// x.map(|a| a + 1).unwrap_or(0);
++ /// ```
++ pub OPTION_MAP_UNWRAP_OR,
++ pedantic,
++ "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.map_or_else(_, _)`.
++ ///
++ /// **Known problems:** The order of the arguments is not in execution order.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = Some(1);
++ /// # fn some_function() -> usize { 1 }
++ /// x.map(|a| a + 1).unwrap_or_else(some_function);
++ /// ```
++ pub OPTION_MAP_UNWRAP_OR_ELSE,
++ pedantic,
++ "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `result.map(_).unwrap_or_else(_)`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `result.map_or_else(_, _)`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x: Result<usize, ()> = Ok(1);
++ /// # fn some_function(foo: ()) -> usize { 1 }
++ /// x.map(|a| a + 1).unwrap_or_else(some_function);
++ /// ```
++ pub RESULT_MAP_UNWRAP_OR_ELSE,
++ pedantic,
++ "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `_.map_or(None, _)`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.and_then(_)`.
++ ///
++ /// **Known problems:** The order of the arguments is not in execution order.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let opt = Some(1);
++ /// opt.map_or(None, |a| Some(a + 1))
++ /// # ;
++ /// ```
++ 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))`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.map(|x| y)`.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x = Some("foo");
++ /// let _ = x.and_then(|s| Some(s.len()));
++ /// ```
++ ///
++ /// The correct use would be:
++ ///
++ /// ```rust
++ /// let x = Some("foo");
++ /// let _ = x.map(|s| s.len());
++ /// ```
++ pub OPTION_AND_THEN_SOME,
++ 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(_)`,
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as a
++ /// single method call.
++ ///
++ /// **Known problems:**
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let vec = vec![vec![1]];
++ /// vec.iter().map(|x| x.iter()).flatten();
++ /// ```
++ 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(_)`,
++ /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as a
++ /// single method call.
++ ///
++ /// **Known problems:** Often requires a condition + Option/Iterator creation
++ /// inside the closure.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let vec = vec![1];
++ /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
++ /// ```
++ pub FILTER_MAP,
++ pedantic,
++ "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
++}
++
++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 a
++ /// single method call.
++ ///
++ /// **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 usage of `_.find(_).map(_)`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as a
++ /// single method call.
++ ///
++ /// **Known problems:** Often requires a condition + Option/Iterator creation
++ /// inside the closure.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// (0..3).find(|x| *x == 2).map(|x| x * 2);
++ /// ```
++ /// Can be written as
++ /// ```rust
++ /// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
++ /// ```
++ pub FIND_MAP,
++ pedantic,
++ "using a combination of `find` and `map` can usually be written as a single method call"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for an iterator search (such as `find()`,
++ /// `position()`, or `rposition()`) followed by a call to `is_some()`.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.any(_)`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let vec = vec![1];
++ /// vec.iter().find(|x| **x == 0).is_some();
++ /// ```
++ /// Could be written as
++ /// ```rust
++ /// # let vec = vec![1];
++ /// vec.iter().any(|x| *x == 0);
++ /// ```
++ pub SEARCH_IS_SOME,
++ complexity,
++ "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`"
++}
++
++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);
++ /// x.clone();
++ /// ```
++ 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:**
++ /// ```rust
++ /// # struct Foo;
++ /// # struct NotAFoo;
++ /// impl Foo {
++ /// fn new() -> NotAFoo {
++ /// # NotAFoo
++ /// }
++ /// }
++ /// ```
++ ///
++ /// ```rust
++ /// # struct Foo;
++ /// # struct FooError;
++ /// impl Foo {
++ /// // Good. Return type contains `Self`
++ /// fn new() -> Result<Foo, FooError> {
++ /// # Ok(Foo)
++ /// }
++ /// }
++ /// ```
++ ///
++ /// ```rust
++ /// # struct Foo;
++ /// struct Bar(Foo);
++ /// impl Foo {
++ /// // Bad. The type name must contain `Self`.
++ /// fn new() -> Bar {
++ /// # Bar(Foo)
++ /// }
++ /// }
++ /// ```
++ 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:**
++ /// `_.split("x")` could be `_.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 getting the inner pointer of a temporary
++ /// `CString`.
++ ///
++ /// **Why is this bad?** The inner pointer of a `CString` is only valid as long
++ /// as the `CString` is alive.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::ffi::CString;
++ /// # fn call_some_ffi_func(_: *const i8) {}
++ /// #
++ /// let c_str = CString::new("foo").unwrap().as_ptr();
++ /// unsafe {
++ /// call_some_ffi_func(c_str);
++ /// }
++ /// ```
++ /// Here `c_str` point to a freed address. The correct use would be:
++ /// ```rust
++ /// # use std::ffi::CString;
++ /// # fn call_some_ffi_func(_: *const i8) {}
++ /// #
++ /// let c_str = CString::new("foo").unwrap();
++ /// unsafe {
++ /// call_some_ffi_func(c_str.as_ptr());
++ /// }
++ /// ```
++ pub TEMPORARY_CSTRING_AS_PTR,
++ correctness,
++ "getting the inner pointer of a temporary `CString`"
++}
++
++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 the use of `iter.nth(0)`.
++ ///
++ /// **Why is this bad?** `iter.next()` is equivalent to
++ /// `iter.nth(0)`, as they both consume the next element,
++ /// but is more readable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # use std::collections::HashSet;
++ /// // Bad
++ /// # let mut s = HashSet::new();
++ /// # s.insert(1);
++ /// let x = s.iter().nth(0);
++ ///
++ /// // Good
++ /// # let mut s = HashSet::new();
++ /// # s.insert(1);
++ /// let x = s.iter().next();
++ /// ```
++ pub ITER_NTH_ZERO,
++ style,
++ "replace `iter.nth(0)` with `iter.next()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `.iter().nth()` (and the related
++ /// `.iter_mut().nth()`) on standard library types with O(1) element access.
++ ///
++ /// **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more
++ /// readable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let some_vec = vec![0, 1, 2, 3];
++ /// let bad_vec = some_vec.iter().nth(3);
++ /// let bad_slice = &some_vec[..].iter().nth(3);
++ /// ```
++ /// The correct use would be:
++ /// ```rust
++ /// let some_vec = vec![0, 1, 2, 3];
++ /// let bad_vec = some_vec.get(3);
++ /// let bad_slice = &some_vec[..].get(3);
++ /// ```
++ pub ITER_NTH,
++ perf,
++ "using `.iter().nth()` on a standard library type with O(1) element access"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `.skip(x).next()` on iterators.
++ ///
++ /// **Why is this bad?** `.nth(x)` is cleaner
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let some_vec = vec![0, 1, 2, 3];
++ /// let bad_vec = some_vec.iter().skip(3).next();
++ /// let bad_slice = &some_vec[..].iter().skip(3).next();
++ /// ```
++ /// The correct use would be:
++ /// ```rust
++ /// let some_vec = vec![0, 1, 2, 3];
++ /// let bad_vec = some_vec.iter().nth(3);
++ /// let bad_slice = &some_vec[..].iter().nth(3);
++ /// ```
++ pub ITER_SKIP_NEXT,
++ style,
++ "using `.skip(x).next()` on an iterator"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `.get().unwrap()` (or
++ /// `.get_mut().unwrap`) on a standard library type which implements `Index`
++ ///
++ /// **Why is this bad?** Using the Index trait (`[]`) is more clear and more
++ /// concise.
++ ///
++ /// **Known problems:** Not a replacement for error handling: Using either
++ /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
++ /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
++ /// temporary placeholder for dealing with the `Option` type, then this does
++ /// not mitigate the need for error handling. If there is a chance that `.get()`
++ /// will be `None` in your program, then it is advisable that the `None` case
++ /// is handled in a future refactor instead of using `.unwrap()` or the Index
++ /// trait.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let mut some_vec = vec![0, 1, 2, 3];
++ /// let last = some_vec.get(3).unwrap();
++ /// *some_vec.get_mut(0).unwrap() = 1;
++ /// ```
++ /// The correct use would be:
++ /// ```rust
++ /// let mut some_vec = vec![0, 1, 2, 3];
++ /// let last = some_vec[3];
++ /// some_vec[0] = 1;
++ /// ```
++ pub GET_UNWRAP,
++ restriction,
++ "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
++ /// `&str` or `String`.
++ ///
++ /// **Why is this bad?** `.push_str(s)` is clearer
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let abc = "abc";
++ /// let def = String::from("def");
++ /// let mut s = String::new();
++ /// s.extend(abc.chars());
++ /// s.extend(def.chars());
++ /// ```
++ /// The correct use would be:
++ /// ```rust
++ /// let abc = "abc";
++ /// let def = String::from("def");
++ /// let mut s = String::new();
++ /// s.push_str(abc);
++ /// s.push_str(&def);
++ /// ```
++ pub STRING_EXTEND_CHARS,
++ style,
++ "using `x.extend(s.chars())` where s is a `&str` or `String`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the use of `.cloned().collect()` on slice to
++ /// create a `Vec`.
++ ///
++ /// **Why is this bad?** `.to_vec()` is clearer
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let s = [1, 2, 3, 4, 5];
++ /// let s2: Vec<isize> = s[..].iter().cloned().collect();
++ /// ```
++ /// The better use would be:
++ /// ```rust
++ /// let s = [1, 2, 3, 4, 5];
++ /// let s2: Vec<isize> = s.to_vec();
++ /// ```
++ pub ITER_CLONED_COLLECT,
++ style,
++ "using `.cloned().collect()` on slice to create a `Vec`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `.chars().last()` or
++ /// `.chars().next_back()` on a `str` to check if it ends with a given char.
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as
++ /// `_.ends_with(_)`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let name = "_";
++ /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-')
++ /// # ;
++ /// ```
++ 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:** False positive in pattern guards. Will be resolved once
++ /// non-lexical lifetimes are stable.
++ ///
++ /// **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:
++ /// ```rust
++ /// 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:
++ /// ```rust
++ /// 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
++ /// let _ = (&vec![3, 4, 5]).into_iter();
++ /// ```
++ pub INTO_ITER_ON_REF,
++ style,
++ "using `.into_iter()` on a reference"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `map` followed by a `count`.
++ ///
++ /// **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.
++ /// If the `map` call is intentional, this should be rewritten. Or, if you intend to
++ /// drive the iterator to completion, you can just use `for_each` instead.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let _ = (0..3).map(|x| x + 2).count();
++ /// ```
++ pub SUSPICIOUS_MAP,
++ complexity,
++ "suspicious usage of map"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.
++ ///
++ /// **Why is this bad?** For most types, this is undefined behavior.
++ ///
++ /// **Known problems:** For now, we accept empty tuples and tuples / arrays
++ /// of `MaybeUninit`. There may be other types that allow uninitialized
++ /// data, but those are not yet rigorously defined.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // Beware the UB
++ /// use std::mem::MaybeUninit;
++ ///
++ /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++ /// ```
++ ///
++ /// Note that the following is OK:
++ ///
++ /// ```rust
++ /// use std::mem::MaybeUninit;
++ ///
++ /// let _: [MaybeUninit<bool>; 5] = unsafe {
++ /// MaybeUninit::uninit().assume_init()
++ /// };
++ /// ```
++ pub UNINIT_ASSUMED_INIT,
++ correctness,
++ "`MaybeUninit::uninit().assume_init()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
++ ///
++ /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # let y: u32 = 0;
++ /// # let x: u32 = 100;
++ /// let add = x.checked_add(y).unwrap_or(u32::MAX);
++ /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
++ /// ```
++ ///
++ /// can be written using dedicated methods for saturating addition/subtraction as:
++ ///
++ /// ```rust
++ /// # let y: u32 = 0;
++ /// # let x: u32 = 100;
++ /// let add = x.saturating_add(y);
++ /// let sub = x.saturating_sub(y);
++ /// ```
++ pub MANUAL_SATURATING_ARITHMETIC,
++ style,
++ "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
++ /// zero-sized types
++ ///
++ /// **Why is this bad?** This is a no-op, and likely unintended
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust
++ /// unsafe { (&() as *const ()).offset(1) };
++ /// ```
++ pub ZST_OFFSET,
++ correctness,
++ "Check for offset calculations on raw pointers to zero-sized types"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `FileType::is_file()`.
++ ///
++ /// **Why is this bad?** When people testing a file type with `FileType::is_file`
++ /// they are testing whether a path is something they can get bytes from. But
++ /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
++ /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # || {
++ /// let metadata = std::fs::metadata("foo.txt")?;
++ /// let filetype = metadata.file_type();
++ ///
++ /// if filetype.is_file() {
++ /// // read file
++ /// }
++ /// # Ok::<_, std::io::Error>(())
++ /// # };
++ /// ```
++ ///
++ /// should be written as:
++ ///
++ /// ```rust
++ /// # || {
++ /// let metadata = std::fs::metadata("foo.txt")?;
++ /// let filetype = metadata.file_type();
++ ///
++ /// if !filetype.is_dir() {
++ /// // read file
++ /// }
++ /// # Ok::<_, std::io::Error>(())
++ /// # };
++ /// ```
++ pub FILETYPE_IS_FILE,
++ restriction,
++ "`FileType::is_file` is not recommended to test for readable file type"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
++ ///
++ /// **Why is this bad?** Readability, this can be written more concisely as a
++ /// single method call.
++ ///
++ /// **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_lint_pass!(Methods => [
++ OPTION_UNWRAP_USED,
++ RESULT_UNWRAP_USED,
++ OPTION_EXPECT_USED,
++ RESULT_EXPECT_USED,
++ SHOULD_IMPLEMENT_TRAIT,
++ WRONG_SELF_CONVENTION,
++ WRONG_PUB_SELF_CONVENTION,
++ OK_EXPECT,
++ OPTION_MAP_UNWRAP_OR,
++ OPTION_MAP_UNWRAP_OR_ELSE,
++ RESULT_MAP_UNWRAP_OR_ELSE,
++ RESULT_MAP_OR_INTO_OPTION,
++ OPTION_MAP_OR_NONE,
++ OPTION_AND_THEN_SOME,
++ OR_FUN_CALL,
++ EXPECT_FUN_CALL,
++ CHARS_NEXT_CMP,
++ CHARS_LAST_CMP,
++ CLONE_ON_COPY,
++ CLONE_ON_REF_PTR,
++ CLONE_DOUBLE_REF,
++ INEFFICIENT_TO_STRING,
++ NEW_RET_NO_SELF,
++ SINGLE_CHAR_PATTERN,
++ SEARCH_IS_SOME,
++ TEMPORARY_CSTRING_AS_PTR,
++ FILTER_NEXT,
++ SKIP_WHILE_NEXT,
++ FILTER_MAP,
++ FILTER_MAP_NEXT,
++ FLAT_MAP_IDENTITY,
++ FIND_MAP,
++ MAP_FLATTEN,
++ ITERATOR_STEP_BY_ZERO,
++ ITER_NTH,
++ ITER_NTH_ZERO,
++ 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,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
++ #[allow(clippy::too_many_lines)]
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if in_macro(expr.span) {
++ return;
++ }
++
++ let (method_names, arg_lists, method_spans) = method_calls(expr, 2);
++ let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
++ let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
++
++ match method_names.as_slice() {
++ ["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false),
++ ["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
++ ["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
++ ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
++ ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
++ ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
++ ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
++ ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
++ ["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
++ ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
++ ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
++ ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
++ ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
++ ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
++ ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
++ ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
++ ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
++ ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
++ ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
++ ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
++ ["is_some", "position"] => {
++ lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
++ },
++ ["is_some", "rposition"] => {
++ lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
++ },
++ ["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
++ ["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
++ lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
++ },
++ ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
++ ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
++ ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
++ ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
++ ["next", "skip"] => lint_iter_skip_next(cx, expr),
++ ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
++ ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
++ ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
++ ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
++ ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
++ ["count", "map"] => lint_suspicious_map(cx, expr),
++ ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
++ ["unwrap_or", arith @ "checked_add"]
++ | ["unwrap_or", arith @ "checked_sub"]
++ | ["unwrap_or", arith @ "checked_mul"] => {
++ manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
++ },
++ ["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
++ check_pointer_offset(cx, expr, arg_lists[0])
++ },
++ ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
++ ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
++ ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
++ _ => {},
++ }
++
++ match expr.kind {
++ hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => {
++ lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
++ lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
++
++ let self_ty = cx.tables.expr_ty_adjusted(&args[0]);
++ if args.len() == 1 && method_call.ident.name == sym!(clone) {
++ lint_clone_on_copy(cx, expr, &args[0], self_ty);
++ lint_clone_on_ref_ptr(cx, expr, &args[0]);
++ }
++ if args.len() == 1 && method_call.ident.name == sym!(to_string) {
++ inefficient_to_string::lint(cx, expr, &args[0], self_ty);
++ }
++
++ match self_ty.kind {
++ ty::Ref(_, ty, _) if ty.kind == ty::Str => {
++ for &(method, pos) in &PATTERN_METHODS {
++ if method_call.ident.name.as_str() == method && args.len() > pos {
++ lint_single_char_pattern(cx, expr, &args[pos]);
++ }
++ }
++ },
++ ty::Ref(..) if method_call.ident.name == sym!(into_iter) => {
++ lint_into_iter(cx, expr, self_ty, *method_span);
++ },
++ _ => (),
++ }
++ },
++ hir::ExprKind::Binary(op, ref lhs, ref 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);
++ }
++ _ => (),
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, '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 def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ let self_ty = cx.tcx.type_of(def_id);
++ 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();
++ if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind;
++
++ let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++ let method_sig = cx.tcx.fn_sig(method_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 cx.access_levels.is_exported(impl_item.hir_id) {
++ // check missing trait implementations
++ for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS {
++ if name == method_name &&
++ sig.decl.inputs.len() == n_args &&
++ out_type.matches(cx, &sig.decl.output) &&
++ self_kind.matches(cx, self_ty, first_arg_ty) &&
++ fn_header_equals(*fn_header, sig.header) {
++ span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!(
++ "defining a method called `{}` on this type; consider implementing \
++ the `{}` trait or choosing a less ambiguous name", name, trait_name));
++ }
++ }
++ }
++
++ if let Some((ref conv, self_kinds)) = &CONVENTIONS
++ .iter()
++ .find(|(ref conv, _)| conv.check(&name))
++ {
++ if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
++ let lint = if item.vis.node.is_pub() {
++ WRONG_PUB_SELF_CONVENTION
++ } else {
++ WRONG_SELF_CONVENTION
++ };
++
++ span_lint(
++ cx,
++ lint,
++ first_arg.pat.span,
++ &format!(
++ "methods called `{}` usually take {}; consider choosing a less \
++ ambiguous name",
++ conv,
++ &self_kinds
++ .iter()
++ .map(|k| k.description())
++ .collect::<Vec<_>>()
++ .join(" or ")
++ ),
++ );
++ }
++ }
++ }
++ }
++
++ if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
++ let ret_ty = return_ty(cx, impl_item.hir_id);
++
++ let contains_self_ty = |ty: Ty<'tcx>| {
++ ty.walk().any(|inner| match inner.unpack() {
++ GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty),
++
++ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
++ })
++ };
++
++ // walk the return type and check for Self (this does not check associated types)
++ if contains_self_ty(ret_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 in cx.tcx.predicates_of(def_id).predicates {
++ match predicate {
++ (Predicate::Projection(poly_projection_predicate), _) => {
++ let binder = poly_projection_predicate.ty();
++ let associated_type = binder.skip_binder();
++
++ // walk the associated type and check for Self
++ if contains_self_ty(associated_type) {
++ return;
++ }
++ },
++ (_, _) => {},
++ }
++ }
++ }
++
++ if name == "new" && !same_tys(cx, ret_ty, self_ty) {
++ span_lint(
++ cx,
++ NEW_RET_NO_SELF,
++ impl_item.span,
++ "methods called `new` usually return `Self`",
++ );
++ }
++ }
++ }
++}
++
++/// Checks for the `OR_FUN_CALL` lint.
++#[allow(clippy::too_many_lines)]
++fn lint_or_fun_call<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &hir::Expr<'_>,
++ method_span: Span,
++ name: &str,
++ args: &'tcx [hir::Expr<'_>],
++) {
++ // Searches an expression for method calls or function calls that aren't ctors
++ struct FunCallFinder<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ found: bool,
++ }
++
++ impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ let call_found = match &expr.kind {
++ // ignore enum and struct constructors
++ hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
++ hir::ExprKind::MethodCall(..) => true,
++ _ => false,
++ };
++
++ if call_found {
++ self.found |= true;
++ }
++
++ if !self.found {
++ intravisit::walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++ }
++
++ /// 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.as_str();
++ if ["default", "new"].contains(&path);
++ let arg_ty = cx.tables.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<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ name: &str,
++ method_span: Span,
++ fun_span: Span,
++ self_expr: &hir::Expr<'_>,
++ arg: &'tcx hir::Expr<'_>,
++ or_has_args: bool,
++ span: Span,
++ ) {
++ // (path, fn_has_argument, methods, suffix)
++ let know_types: &[(&[_], _, &[_], _)] = &[
++ (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
++ (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
++ (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
++ (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
++ ];
++
++ if_chain! {
++ if know_types.iter().any(|k| k.2.contains(&name));
++
++ let mut finder = FunCallFinder { cx: &cx, found: false };
++ if { finder.visit_expr(&arg); finder.found };
++ if !contains_return(&arg);
++
++ let self_ty = cx.tables.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 sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) {
++ (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(),
++ (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(),
++ (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."),
++ };
++ 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(ref fun, ref 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) {
++ check_general_case(
++ cx,
++ name,
++ method_span,
++ fun.span,
++ &args[0],
++ &args[1],
++ or_has_args,
++ expr.span,
++ );
++ }
++ },
++ hir::ExprKind::MethodCall(_, span, ref or_args) => check_general_case(
++ cx,
++ name,
++ method_span,
++ span,
++ &args[0],
++ &args[1],
++ !or_args.is_empty(),
++ expr.span,
++ ),
++ _ => {},
++ }
++ }
++}
++
++/// Checks for the `EXPECT_FUN_CALL` lint.
++#[allow(clippy::too_many_lines)]
++fn lint_expect_fun_call(
++ cx: &LateContext<'_, '_>,
++ expr: &hir::Expr<'_>,
++ method_span: Span,
++ name: &str,
++ args: &[hir::Expr<'_>],
++) {
++ // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
++ // `&str`
++ fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
++ let mut arg_root = arg;
++ loop {
++ arg_root = match &arg_root.kind {
++ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
++ hir::ExprKind::MethodCall(method_name, _, call_args) => {
++ if call_args.len() == 1
++ && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
++ && {
++ let arg_type = cx.tables.expr_ty(&call_args[0]);
++ let base_type = walk_ptrs_ty(arg_type);
++ base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
++ }
++ {
++ &call_args[0]
++ } else {
++ break;
++ }
++ },
++ _ => break,
++ };
++ }
++ arg_root
++ }
++
++ // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
++ // converted to string.
++ fn requires_to_string(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool {
++ let arg_ty = cx.tables.expr_ty(arg);
++ if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) {
++ return false;
++ }
++ if let ty::Ref(_, ty, ..) = arg_ty.kind {
++ if ty.kind == ty::Str && can_be_static_str(cx, arg) {
++ return false;
++ }
++ };
++ true
++ }
++
++ // Check if an expression could have type `&'static str`, knowing that it
++ // has type `&str` for some lifetime.
++ fn can_be_static_str(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool {
++ match arg.kind {
++ hir::ExprKind::Lit(_) => true,
++ hir::ExprKind::Call(fun, _) => {
++ if let hir::ExprKind::Path(ref p) = fun.kind {
++ match cx.tables.qpath_res(p, fun.hir_id) {
++ hir::def::Res::Def(hir::def::DefKind::Fn, def_id)
++ | hir::def::Res::Def(hir::def::DefKind::AssocFn, def_id) => matches!(
++ cx.tcx.fn_sig(def_id).output().skip_binder().kind,
++ ty::Ref(ty::ReStatic, ..)
++ ),
++ _ => false,
++ }
++ } else {
++ false
++ }
++ },
++ hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(arg.hir_id).map_or(false, |method_id| {
++ matches!(
++ cx.tcx.fn_sig(method_id).output().skip_binder().kind,
++ ty::Ref(ty::ReStatic, ..)
++ )
++ }),
++ hir::ExprKind::Path(ref p) => match cx.tables.qpath_res(p, arg.hir_id) {
++ hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true,
++ _ => false,
++ },
++ _ => false,
++ }
++ }
++
++ fn generate_format_arg_snippet(
++ cx: &LateContext<'_, '_>,
++ a: &hir::Expr<'_>,
++ applicability: &mut Applicability,
++ ) -> Vec<String> {
++ if_chain! {
++ if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
++ if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
++ if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
++
++ then {
++ format_arg_expr_tup
++ .iter()
++ .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
++ .collect()
++ } else {
++ unreachable!()
++ }
++ }
++ }
++
++ fn is_call(node: &hir::ExprKind<'_>) -> bool {
++ match node {
++ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
++ is_call(&expr.kind)
++ },
++ hir::ExprKind::Call(..)
++ | hir::ExprKind::MethodCall(..)
++ // These variants are debatable or require further examination
++ | hir::ExprKind::Match(..)
++ | hir::ExprKind::Block{ .. } => true,
++ _ => false,
++ }
++ }
++
++ if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
++ return;
++ }
++
++ let receiver_type = cx.tables.expr_ty_adjusted(&args[0]);
++ let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) {
++ "||"
++ } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) {
++ "|_|"
++ } else {
++ return;
++ };
++
++ let arg_root = get_arg_root(cx, &args[1]);
++
++ let span_replace_word = method_span.with_hi(expr.span.hi());
++
++ let mut applicability = Applicability::MachineApplicable;
++
++ //Special handling for `format!` as arg_root
++ if_chain! {
++ if let hir::ExprKind::Block(block, None) = &arg_root.kind;
++ if block.stmts.len() == 1;
++ if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
++ if let Some(arg_root) = &local.init;
++ if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
++ if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
++ if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
++ then {
++ let fmt_spec = &format_args[0];
++ let fmt_args = &format_args[1];
++
++ let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
++
++ args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
++
++ let sugg = args.join(", ");
++
++ span_lint_and_sugg(
++ cx,
++ EXPECT_FUN_CALL,
++ span_replace_word,
++ &format!("use of `{}` followed by a function call", name),
++ "try this",
++ format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
++ applicability,
++ );
++
++ return;
++ }
++ }
++
++ let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
++ if requires_to_string(cx, arg_root) {
++ arg_root_snippet.to_mut().push_str(".to_string()");
++ }
++
++ span_lint_and_sugg(
++ cx,
++ EXPECT_FUN_CALL,
++ span_replace_word,
++ &format!("use of `{}` followed by a function call", name),
++ "try this",
++ format!("unwrap_or_else({} {{ panic!({}) }})", closure_args, arg_root_snippet),
++ applicability,
++ );
++}
++
++/// Checks for the `CLONE_ON_COPY` lint.
++fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
++ let ty = cx.tables.expr_ty(expr);
++ if let ty::Ref(_, inner, _) = arg_ty.kind {
++ if let ty::Ref(_, innermost, _) = inner.kind {
++ span_lint_and_then(
++ cx,
++ CLONE_DOUBLE_REF,
++ expr.span,
++ "using `clone` on a double-reference; \
++ this will copy the reference instead of cloning the inner type",
++ |diag| {
++ if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
++ let mut ty = innermost;
++ let mut n = 0;
++ while let ty::Ref(_, inner, _) = ty.kind {
++ ty = inner;
++ n += 1;
++ }
++ let refs: String = iter::repeat('&').take(n + 1).collect();
++ let derefs: String = iter::repeat('*').take(n).collect();
++ let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
++ diag.span_suggestion(
++ expr.span,
++ "try dereferencing it",
++ format!("{}({}{}).clone()", refs, derefs, snip.deref()),
++ Applicability::MaybeIncorrect,
++ );
++ diag.span_suggestion(
++ expr.span,
++ "or try being explicit if you are sure, that you want to clone a reference",
++ explicit,
++ Applicability::MaybeIncorrect,
++ );
++ }
++ },
++ );
++ return; // don't report clone_on_copy
++ }
++ }
++
++ if is_copy(cx, ty) {
++ let snip;
++ if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
++ let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
++ match &cx.tcx.hir().get(parent) {
++ hir::Node::Expr(parent) => match parent.kind {
++ // &*x is a nop, &x.clone() is not
++ hir::ExprKind::AddrOf(..) => return,
++ // (*x).func() is useless, x.clone().func() can work in case func borrows mutably
++ hir::ExprKind::MethodCall(_, _, parent_args) if expr.hir_id == parent_args[0].hir_id => return,
++
++ _ => {},
++ },
++ hir::Node::Stmt(stmt) => {
++ if let hir::StmtKind::Local(ref loc) = stmt.kind {
++ if let hir::PatKind::Ref(..) = loc.pat.kind {
++ // let ref y = *x borrows x, let ref y = x.clone() does not
++ return;
++ }
++ }
++ },
++ _ => {},
++ }
++
++ // x.clone() might have dereferenced x, possibly through Deref impls
++ if cx.tables.expr_ty(arg) == ty {
++ snip = Some(("try removing the `clone` call", format!("{}", snippet)));
++ } else {
++ let deref_count = cx
++ .tables
++ .expr_adjustments(arg)
++ .iter()
++ .filter(|adj| {
++ if let ty::adjustment::Adjust::Deref(_) = adj.kind {
++ true
++ } else {
++ false
++ }
++ })
++ .count();
++ let derefs: String = iter::repeat('*').take(deref_count).collect();
++ snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
++ }
++ } else {
++ snip = None;
++ }
++ span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
++ if let Some((text, snip)) = snip {
++ diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified);
++ }
++ });
++ }
++}
++
++fn lint_clone_on_ref_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(arg));
++
++ if let ty::Adt(_, subst) = obj_ty.kind {
++ let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
++ "Rc"
++ } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) {
++ "Arc"
++ } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) {
++ "Weak"
++ } else {
++ return;
++ };
++
++ span_lint_and_sugg(
++ cx,
++ CLONE_ON_REF_PTR,
++ expr.span,
++ "using `.clone()` on a ref-counted pointer",
++ "try this",
++ format!(
++ "{}::<{}>::clone(&{})",
++ caller_type,
++ subst.type_at(0),
++ snippet(cx, arg.span, "_")
++ ),
++ Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
++ );
++ }
++}
++
++fn lint_string_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ let arg = &args[1];
++ if let Some(arglists) = method_chain_args(arg, &["chars"]) {
++ let target = &arglists[0][0];
++ let self_ty = walk_ptrs_ty(cx.tables.expr_ty(target));
++ let ref_str = if self_ty.kind == ty::Str {
++ ""
++ } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
++ "&"
++ } else {
++ return;
++ };
++
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ STRING_EXTEND_CHARS,
++ expr.span,
++ "calling `.extend(_.chars())`",
++ "try this",
++ format!(
++ "{}.push_str({}{})",
++ snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
++ ref_str,
++ snippet_with_applicability(cx, target.span, "_", &mut applicability)
++ ),
++ applicability,
++ );
++ }
++}
++
++fn lint_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
++ if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
++ lint_string_extend(cx, expr, args);
++ }
++}
++
++fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) {
++ if_chain! {
++ let source_type = cx.tables.expr_ty(source);
++ if let ty::Adt(def, substs) = source_type.kind;
++ if cx.tcx.is_diagnostic_item(sym!(result_type), def.did);
++ if match_type(cx, substs.type_at(0), &paths::CSTRING);
++ then {
++ span_lint_and_then(
++ cx,
++ TEMPORARY_CSTRING_AS_PTR,
++ expr.span,
++ "you are getting the inner pointer of a temporary `CString`",
++ |diag| {
++ diag.note("that pointer will be invalid outside this expression");
++ diag.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
++ });
++ }
++ }
++}
++
++fn lint_iter_cloned_collect<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &hir::Expr<'_>,
++ iter_args: &'tcx [hir::Expr<'_>],
++) {
++ if_chain! {
++ if is_type_diagnostic_item(cx, cx.tables.expr_ty(expr), sym!(vec_type));
++ if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0]));
++ if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
++
++ then {
++ span_lint_and_sugg(
++ cx,
++ ITER_CLONED_COLLECT,
++ to_replace,
++ "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
++ more readable",
++ "try",
++ ".to_vec()".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++}
++
++fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
++ fn check_fold_with_op(
++ cx: &LateContext<'_, '_>,
++ expr: &hir::Expr<'_>,
++ fold_args: &[hir::Expr<'_>],
++ fold_span: Span,
++ op: hir::BinOpKind,
++ replacement_method_name: &str,
++ replacement_has_args: bool,
++ ) {
++ if_chain! {
++ // Extract the body of the closure passed to fold
++ if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
++ let closure_body = cx.tcx.hir().body(body_id);
++ let closure_expr = remove_blocks(&closure_body.value);
++
++ // Check if the closure body is of the form `acc <op> some_expr(x)`
++ if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
++ if bin_op.node == op;
++
++ // Extract the names of the two arguments to the closure
++ if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat);
++ if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat);
++
++ if match_var(&*left_expr, first_arg_ident);
++ if replacement_has_args || match_var(&*right_expr, second_arg_ident);
++
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let sugg = if replacement_has_args {
++ format!(
++ "{replacement}(|{s}| {r})",
++ replacement = replacement_method_name,
++ s = second_arg_ident,
++ r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
++ )
++ } else {
++ format!(
++ "{replacement}()",
++ replacement = replacement_method_name,
++ )
++ };
++
++ span_lint_and_sugg(
++ cx,
++ UNNECESSARY_FOLD,
++ fold_span.with_hi(expr.span.hi()),
++ // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
++ "this `.fold` can be written more succinctly using another method",
++ "try",
++ sugg,
++ applicability,
++ );
++ }
++ }
++ }
++
++ // Check that this is a call to Iterator::fold rather than just some function called fold
++ if !match_trait_method(cx, expr, &paths::ITERATOR) {
++ return;
++ }
++
++ assert!(
++ fold_args.len() == 3,
++ "Expected fold_args to have three entries - the receiver, the initial value and the closure"
++ );
++
++ // Check if the first argument to .fold is a suitable literal
++ if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
++ match lit.node {
++ ast::LitKind::Bool(false) => {
++ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
++ },
++ ast::LitKind::Bool(true) => {
++ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
++ },
++ ast::LitKind::Int(0, _) => {
++ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
++ },
++ ast::LitKind::Int(1, _) => {
++ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
++ },
++ _ => (),
++ }
++ }
++}
++
++fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &args[1]) {
++ span_lint(
++ cx,
++ ITERATOR_STEP_BY_ZERO,
++ expr.span,
++ "Iterator::step_by(0) will panic at runtime",
++ );
++ }
++ }
++}
++
++fn lint_iter_nth<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &hir::Expr<'_>,
++ nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]],
++ is_mut: bool,
++) {
++ let iter_args = nth_and_iter_args[1];
++ let mut_str = if is_mut { "_mut" } else { "" };
++ let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
++ "slice"
++ } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vec_type)) {
++ "Vec"
++ } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vecdeque_type)) {
++ "VecDeque"
++ } else {
++ let nth_args = nth_and_iter_args[0];
++ lint_iter_nth_zero(cx, expr, &nth_args);
++ return; // caller is not a type that we want to lint
++ };
++
++ span_lint_and_help(
++ cx,
++ ITER_NTH,
++ expr.span,
++ &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type),
++ None,
++ &format!("calling `.get{}()` is both faster and more readable", mut_str),
++ );
++}
++
++fn lint_iter_nth_zero<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
++ if_chain! {
++ if match_trait_method(cx, expr, &paths::ITERATOR);
++ if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &nth_args[1]);
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ ITER_NTH_ZERO,
++ expr.span,
++ "called `.nth(0)` on a `std::iter::Iterator`",
++ "try calling",
++ format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)),
++ applicability,
++ );
++ }
++ }
++}
++
++fn lint_get_unwrap<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &hir::Expr<'_>,
++ get_args: &'tcx [hir::Expr<'_>],
++ is_mut: bool,
++) {
++ // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
++ // because they do not implement `IndexMut`
++ let mut applicability = Applicability::MachineApplicable;
++ let expr_ty = cx.tables.expr_ty(&get_args[0]);
++ let get_args_str = if get_args.len() > 1 {
++ snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability)
++ } else {
++ return; // not linting on a .get().unwrap() chain or variant
++ };
++ let mut needs_ref;
++ let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
++ needs_ref = get_args_str.parse::<usize>().is_ok();
++ "slice"
++ } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) {
++ needs_ref = get_args_str.parse::<usize>().is_ok();
++ "Vec"
++ } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) {
++ needs_ref = get_args_str.parse::<usize>().is_ok();
++ "VecDeque"
++ } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym!(hashmap_type)) {
++ needs_ref = true;
++ "HashMap"
++ } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) {
++ needs_ref = true;
++ "BTreeMap"
++ } else {
++ return; // caller is not a type that we want to lint
++ };
++
++ let mut span = expr.span;
++
++ // Handle the case where the result is immediately dereferenced
++ // by not requiring ref and pulling the dereference into the
++ // suggestion.
++ if_chain! {
++ if needs_ref;
++ if let Some(parent) = get_parent_expr(cx, expr);
++ if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.kind;
++ then {
++ needs_ref = false;
++ span = parent.span;
++ }
++ }
++
++ let mut_str = if is_mut { "_mut" } else { "" };
++ let borrow_str = if !needs_ref {
++ ""
++ } else if is_mut {
++ "&mut "
++ } else {
++ "&"
++ };
++
++ span_lint_and_sugg(
++ cx,
++ GET_UNWRAP,
++ span,
++ &format!(
++ "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
++ mut_str, caller_type
++ ),
++ "try this",
++ format!(
++ "{}{}[{}]",
++ borrow_str,
++ snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability),
++ get_args_str
++ ),
++ applicability,
++ );
++}
++
++fn lint_iter_skip_next(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
++ // lint if caller of skip is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ span_lint_and_help(
++ cx,
++ ITER_SKIP_NEXT,
++ expr.span,
++ "called `skip(x).next()` on an iterator",
++ None,
++ "this is more succinctly expressed by calling `nth(x)`",
++ );
++ }
++}
++
++fn derefs_to_slice<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'tcx>,
++ ty: Ty<'tcx>,
++) -> Option<&'tcx hir::Expr<'tcx>> {
++ fn may_slice<'a>(cx: &LateContext<'_, 'a>, ty: Ty<'a>) -> bool {
++ match ty.kind {
++ ty::Slice(_) => true,
++ ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
++ ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
++ ty::Array(_, size) => {
++ if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) {
++ size < 32
++ } else {
++ false
++ }
++ },
++ ty::Ref(_, inner, _) => may_slice(cx, inner),
++ _ => false,
++ }
++ }
++
++ if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind {
++ if path.ident.name == sym!(iter) && may_slice(cx, cx.tables.expr_ty(&args[0])) {
++ Some(&args[0])
++ } else {
++ None
++ }
++ } else {
++ match ty.kind {
++ ty::Slice(_) => Some(expr),
++ ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
++ ty::Ref(_, inner, _) => {
++ if may_slice(cx, inner) {
++ Some(expr)
++ } else {
++ None
++ }
++ },
++ _ => None,
++ }
++ }
++}
++
++/// lint use of `unwrap()` for `Option`s and `Result`s
++fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0]));
++
++ let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
++ Some((OPTION_UNWRAP_USED, "an Option", "None"))
++ } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
++ Some((RESULT_UNWRAP_USED, "a Result", "Err"))
++ } else {
++ None
++ };
++
++ if let Some((lint, kind, none_value)) = mess {
++ span_lint_and_help(
++ cx,
++ lint,
++ expr.span,
++ &format!("used `unwrap()` on `{}` value", kind,),
++ None,
++ &format!(
++ "if you don't want to handle the `{}` case gracefully, consider \
++ using `expect()` to provide a better panic message",
++ none_value,
++ ),
++ );
++ }
++}
++
++/// lint use of `expect()` for `Option`s and `Result`s
++fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0]));
++
++ let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
++ Some((OPTION_EXPECT_USED, "an Option", "None"))
++ } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
++ Some((RESULT_EXPECT_USED, "a Result", "Err"))
++ } else {
++ None
++ };
++
++ if let Some((lint, kind, none_value)) = mess {
++ span_lint_and_help(
++ cx,
++ lint,
++ expr.span,
++ &format!("used `expect()` on `{}` value", kind,),
++ None,
++ &format!("if this value is an `{}`, it will panic", none_value,),
++ );
++ }
++}
++
++/// lint use of `ok().expect()` for `Result`s
++fn lint_ok_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
++ if_chain! {
++ // lint if the caller of `ok()` is a `Result`
++ if is_type_diagnostic_item(cx, cx.tables.expr_ty(&ok_args[0]), sym!(result_type));
++ let result_type = cx.tables.expr_ty(&ok_args[0]);
++ if let Some(error_type) = get_error_type(cx, result_type);
++ if has_debug_impl(error_type, cx);
++
++ then {
++ span_lint_and_help(
++ cx,
++ OK_EXPECT,
++ expr.span,
++ "called `ok().expect()` on a `Result` value",
++ None,
++ "you can call `expect()` directly on the `Result`",
++ );
++ }
++ }
++}
++
++/// lint use of `map().flatten()` for `Iterators` and 'Options'
++fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
++ // lint if caller of `.map().flatten()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `map(..).flatten()` on an `Iterator`. \
++ This is more succinctly expressed by calling `.flat_map(..)`";
++ let self_snippet = snippet(cx, map_args[0].span, "..");
++ let func_snippet = snippet(cx, map_args[1].span, "..");
++ let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
++ span_lint_and_sugg(
++ cx,
++ MAP_FLATTEN,
++ expr.span,
++ msg,
++ "try using `flat_map` instead",
++ hint,
++ Applicability::MachineApplicable,
++ );
++ }
++
++ // lint if caller of `.map().flatten()` is an Option
++ if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) {
++ let msg = "called `map(..).flatten()` on an `Option`. \
++ This is more succinctly expressed by calling `.and_then(..)`";
++ let self_snippet = snippet(cx, map_args[0].span, "..");
++ let func_snippet = snippet(cx, map_args[1].span, "..");
++ let hint = format!("{0}.and_then({1})", self_snippet, func_snippet);
++ span_lint_and_sugg(
++ cx,
++ MAP_FLATTEN,
++ expr.span,
++ msg,
++ "try using `and_then` instead",
++ hint,
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
++fn lint_map_unwrap_or_else<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ map_args: &'tcx [hir::Expr<'_>],
++ unwrap_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if the caller of `map()` is an `Option`
++ let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type));
++ let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(result_type));
++
++ if is_option || is_result {
++ // Don't make a suggestion that may fail to compile due to mutably borrowing
++ // the same variable twice.
++ let map_mutated_vars = mutated_variables(&map_args[0], cx);
++ let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
++ if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
++ if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
++ return;
++ }
++ } else {
++ return;
++ }
++
++ // lint message
++ let msg = if is_option {
++ "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \
++ `map_or_else(g, f)` instead"
++ } else {
++ "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \
++ `.map_or_else(g, f)` instead"
++ };
++ // get snippets for args to map() and unwrap_or_else()
++ let map_snippet = snippet(cx, map_args[1].span, "..");
++ let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
++ // lint, with note if neither arg is > 1 line and both map() and
++ // unwrap_or_else() have the same span
++ let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
++ let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
++ if same_span && !multiline {
++ span_lint_and_note(
++ cx,
++ if is_option {
++ OPTION_MAP_UNWRAP_OR_ELSE
++ } else {
++ RESULT_MAP_UNWRAP_OR_ELSE
++ },
++ expr.span,
++ msg,
++ None,
++ &format!(
++ "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
++ map_snippet, unwrap_snippet,
++ ),
++ );
++ } else if same_span && multiline {
++ span_lint(
++ cx,
++ if is_option {
++ OPTION_MAP_UNWRAP_OR_ELSE
++ } else {
++ RESULT_MAP_UNWRAP_OR_ELSE
++ },
++ expr.span,
++ msg,
++ );
++ };
++ }
++}
++
++/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
++fn lint_map_or_none<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ map_or_args: &'tcx [hir::Expr<'_>],
++) {
++ let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(option_type));
++ let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(result_type));
++
++ // There are two variants of this `map_or` lint:
++ // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
++ // (2) using `map_or` as a combinator instead of `and_then`
++ //
++ // (For this lint) we don't care if any other type calls `map_or`
++ if !is_option && !is_result {
++ return;
++ }
++
++ let (lint_name, msg, instead, hint) = {
++ let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
++ match_qpath(qpath, &paths::OPTION_NONE)
++ } else {
++ return;
++ };
++
++ if !default_arg_is_none {
++ // nothing to lint!
++ return;
++ }
++
++ let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind {
++ match_qpath(qpath, &paths::OPTION_SOME)
++ } else {
++ false
++ };
++
++ if is_option {
++ let self_snippet = snippet(cx, map_or_args[0].span, "..");
++ let func_snippet = snippet(cx, map_or_args[2].span, "..");
++ let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
++ `and_then(f)` instead";
++ (
++ OPTION_MAP_OR_NONE,
++ msg,
++ "try using `and_then` instead",
++ format!("{0}.and_then({1})", self_snippet, func_snippet),
++ )
++ } else if f_arg_is_some {
++ let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
++ `ok()` instead";
++ let self_snippet = snippet(cx, map_or_args[0].span, "..");
++ (
++ RESULT_MAP_OR_INTO_OPTION,
++ msg,
++ "try using `ok` instead",
++ format!("{0}.ok()", self_snippet),
++ )
++ } else {
++ // nothing to lint!
++ return;
++ }
++ };
++
++ span_lint_and_sugg(
++ cx,
++ lint_name,
++ expr.span,
++ msg,
++ instead,
++ hint,
++ Applicability::MachineApplicable,
++ );
++}
++
++/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
++fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
++ const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op";
++
++ let ty = cx.tables.expr_ty(&args[0]);
++ if !is_type_diagnostic_item(cx, ty, sym!(option_type)) {
++ return;
++ }
++
++ match args[1].kind {
++ hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
++ let closure_body = cx.tcx.hir().body(body_id);
++ let closure_expr = remove_blocks(&closure_body.value);
++ if_chain! {
++ if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind;
++ if let hir::ExprKind::Path(ref qpath) = some_expr.kind;
++ if match_qpath(qpath, &paths::OPTION_SOME);
++ if some_args.len() == 1;
++ then {
++ let inner_expr = &some_args[0];
++
++ if contains_return(inner_expr) {
++ return;
++ }
++
++ let some_inner_snip = if inner_expr.span.from_expansion() {
++ snippet_with_macro_callsite(cx, inner_expr.span, "_")
++ } else {
++ snippet(cx, inner_expr.span, "_")
++ };
++
++ let closure_args_snip = snippet(cx, closure_args_span, "..");
++ let option_snip = snippet(cx, args[0].span, "..");
++ let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip);
++ span_lint_and_sugg(
++ cx,
++ OPTION_AND_THEN_SOME,
++ expr.span,
++ LINT_MSG,
++ "try this",
++ note,
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ },
++ // `_.and_then(Some)` case, which is no-op.
++ hir::ExprKind::Path(ref qpath) => {
++ if match_qpath(qpath, &paths::OPTION_SOME) {
++ let option_snip = snippet(cx, args[0].span, "..");
++ let note = format!("{}", option_snip);
++ span_lint_and_sugg(
++ cx,
++ OPTION_AND_THEN_SOME,
++ expr.span,
++ NO_OP_MSG,
++ "use the expression directly",
++ note,
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ _ => {},
++ }
++}
++
++/// lint use of `filter().next()` for `Iterators`
++fn lint_filter_next<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ filter_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter().next()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
++ `.find(p)` instead.";
++ let filter_snippet = snippet(cx, filter_args[1].span, "..");
++ if filter_snippet.lines().count() <= 1 {
++ // add note if not multi-line
++ span_lint_and_note(
++ cx,
++ FILTER_NEXT,
++ expr.span,
++ msg,
++ None,
++ &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
++ );
++ } else {
++ span_lint(cx, FILTER_NEXT, expr.span, msg);
++ }
++ }
++}
++
++/// lint use of `skip_while().next()` for `Iterators`
++fn lint_skip_while_next<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _skip_while_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.skip_while().next()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ span_lint_and_help(
++ cx,
++ SKIP_WHILE_NEXT,
++ expr.span,
++ "called `skip_while(p).next()` on an `Iterator`",
++ None,
++ "this is more succinctly expressed by calling `.find(!p)` instead",
++ );
++ }
++}
++
++/// lint use of `filter().map()` for `Iterators`
++fn lint_filter_map<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _filter_args: &'tcx [hir::Expr<'_>],
++ _map_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter().map()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter(p).map(q)` on an `Iterator`";
++ let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
++ span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++ }
++}
++
++/// lint use of `filter_map().next()` for `Iterators`
++fn lint_filter_map_next<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ filter_args: &'tcx [hir::Expr<'_>],
++) {
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
++ `.find_map(p)` instead.";
++ let filter_snippet = snippet(cx, filter_args[1].span, "..");
++ if filter_snippet.lines().count() <= 1 {
++ span_lint_and_note(
++ cx,
++ FILTER_MAP_NEXT,
++ expr.span,
++ msg,
++ None,
++ &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet),
++ );
++ } else {
++ span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
++ }
++ }
++}
++
++/// lint use of `find().map()` for `Iterators`
++fn lint_find_map<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _find_args: &'tcx [hir::Expr<'_>],
++ map_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter().map()` is an Iterator
++ if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
++ let msg = "called `find(p).map(q)` on an `Iterator`";
++ let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
++ span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
++ }
++}
++
++/// lint use of `filter_map().map()` for `Iterators`
++fn lint_filter_map_map<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _filter_args: &'tcx [hir::Expr<'_>],
++ _map_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter().map()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter_map(p).map(q)` on an `Iterator`";
++ let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
++ span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++ }
++}
++
++/// lint use of `filter().flat_map()` for `Iterators`
++fn lint_filter_flat_map<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _filter_args: &'tcx [hir::Expr<'_>],
++ _map_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter().flat_map()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter(p).flat_map(q)` on an `Iterator`";
++ let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
++ and filtering by returning `iter::empty()`";
++ span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++ }
++}
++
++/// lint use of `filter_map().flat_map()` for `Iterators`
++fn lint_filter_map_flat_map<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ _filter_args: &'tcx [hir::Expr<'_>],
++ _map_args: &'tcx [hir::Expr<'_>],
++) {
++ // lint if caller of `.filter_map().flat_map()` is an Iterator
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`";
++ let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
++ and filtering by returning `iter::empty()`";
++ span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++ }
++}
++
++/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
++fn lint_flat_map_identity<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ flat_map_args: &'tcx [hir::Expr<'_>],
++ flat_map_span: Span,
++) {
++ if match_trait_method(cx, expr, &paths::ITERATOR) {
++ let arg_node = &flat_map_args[1].kind;
++
++ 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, _, _) = arg_node;
++ 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(_, ref path)) = body.value.kind;
++
++ if path.segments.len() == 1;
++ if path.segments[0].ident.as_str() == binding_ident.as_str();
++
++ then {
++ apply_lint("called `flat_map(|x| x)` on an `Iterator`");
++ }
++ }
++
++ if_chain! {
++ if let hir::ExprKind::Path(ref qpath) = arg_node;
++
++ if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
++
++ then {
++ apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
++ }
++ }
++ }
++}
++
++/// lint searching an Iterator followed by `is_some()`
++fn lint_search_is_some<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx hir::Expr<'_>,
++ search_method: &str,
++ search_args: &'tcx [hir::Expr<'_>],
++ is_some_args: &'tcx [hir::Expr<'_>],
++ method_span: Span,
++) {
++ // lint if caller of search is an Iterator
++ if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
++ let msg = format!(
++ "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
++ expressed by calling `any()`.",
++ search_method
++ );
++ let search_snippet = snippet(cx, search_args[1].span, "..");
++ if search_snippet.lines().count() <= 1 {
++ // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
++ // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
++ let any_search_snippet = if_chain! {
++ if search_method == "find";
++ if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
++ let closure_body = cx.tcx.hir().body(body_id);
++ if let Some(closure_arg) = closure_body.params.get(0);
++ then {
++ if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
++ Some(search_snippet.replacen('&', "", 1))
++ } else if let Some(name) = get_arg_name(&closure_arg.pat) {
++ Some(search_snippet.replace(&format!("*{}", name), &name.as_str()))
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++ };
++ // add note if not multi-line
++ span_lint_and_sugg(
++ cx,
++ SEARCH_IS_SOME,
++ method_span.with_hi(expr.span.hi()),
++ &msg,
++ "try this",
++ format!(
++ "any({})",
++ any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
++ ),
++ Applicability::MachineApplicable,
++ );
++ } else {
++ span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
++ }
++ }
++}
++
++/// 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:ident, $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!(lint_chars_next_cmp, cx, info);
++ lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info);
++ lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info);
++ lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info);
++}
++
++/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
++fn lint_chars_cmp(
++ cx: &LateContext<'_, '_>,
++ info: &BinaryExprInfo<'_>,
++ chain_methods: &[&str],
++ lint: &'static Lint,
++ suggest: &str,
++) -> bool {
++ if_chain! {
++ if let Some(args) = method_chain_args(info.chain, chain_methods);
++ if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
++ if arg_char.len() == 1;
++ if let hir::ExprKind::Path(ref qpath) = fun.kind;
++ if let Some(segment) = single_segment_path(qpath);
++ if segment.ident.name == sym!(Some);
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
++
++ if self_ty.kind != ty::Str {
++ return false;
++ }
++
++ span_lint_and_sugg(
++ cx,
++ lint,
++ info.expr.span,
++ &format!("you should use the `{}` method", suggest),
++ "like this",
++ format!("{}{}.{}({})",
++ if info.eq { "" } else { "!" },
++ snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
++ suggest,
++ snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)),
++ applicability,
++ );
++
++ return true;
++ }
++ }
++
++ false
++}
++
++/// Checks for the `CHARS_NEXT_CMP` lint.
++fn lint_chars_next_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool {
++ lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
++}
++
++/// Checks for the `CHARS_LAST_CMP` lint.
++fn lint_chars_last_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool {
++ if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
++ true
++ } else {
++ lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
++ }
++}
++
++/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
++fn lint_chars_cmp_with_unwrap<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ info: &BinaryExprInfo<'_>,
++ chain_methods: &[&str],
++ lint: &'static Lint,
++ suggest: &str,
++) -> bool {
++ if_chain! {
++ if let Some(args) = method_chain_args(info.chain, chain_methods);
++ if let hir::ExprKind::Lit(ref lit) = info.other.kind;
++ if let ast::LitKind::Char(c) = lit.node;
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ lint,
++ info.expr.span,
++ &format!("you should use the `{}` method", suggest),
++ "like this",
++ format!("{}{}.{}('{}')",
++ if info.eq { "" } else { "!" },
++ snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
++ suggest,
++ c),
++ applicability,
++ );
++
++ true
++ } else {
++ false
++ }
++ }
++}
++
++/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
++fn lint_chars_next_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool {
++ lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
++}
++
++/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
++fn lint_chars_last_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo<'_>) -> bool {
++ if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
++ true
++ } else {
++ lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
++ }
++}
++
++/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
++fn lint_single_char_pattern<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ _expr: &'tcx hir::Expr<'_>,
++ arg: &'tcx hir::Expr<'_>,
++) {
++ if_chain! {
++ if let hir::ExprKind::Lit(lit) = &arg.kind;
++ if let ast::LitKind::Str(r, style) = lit.node;
++ if r.as_str().len() == 1;
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
++ let ch = if let ast::StrStyle::Raw(nhash) = style {
++ let nhash = nhash as usize;
++ // for raw string: r##"a"##
++ &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
++ } else {
++ // for regular string: "a"
++ &snip[1..(snip.len() - 1)]
++ };
++ let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
++ span_lint_and_sugg(
++ cx,
++ SINGLE_CHAR_PATTERN,
++ arg.span,
++ "single-character string constant used as pattern",
++ "try using a `char` instead",
++ hint,
++ applicability,
++ );
++ }
++ }
++}
++
++/// Checks for the `USELESS_ASREF` lint.
++fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
++ // when we get here, we've already checked that the call name is "as_ref" or "as_mut"
++ // check if the call is to the actual `AsRef` or `AsMut` trait
++ if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
++ // check if the type after `as_ref` or `as_mut` is the same as before
++ let recvr = &as_ref_args[0];
++ let rcv_ty = cx.tables.expr_ty(recvr);
++ let res_ty = cx.tables.expr_ty(expr);
++ let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
++ let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
++ if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
++ // allow the `as_ref` or `as_mut` if it is followed by another method call
++ if_chain! {
++ if let Some(parent) = get_parent_expr(cx, expr);
++ if let hir::ExprKind::MethodCall(_, ref span, _) = parent.kind;
++ if span != &expr.span;
++ then {
++ return;
++ }
++ }
++
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ USELESS_ASREF,
++ expr.span,
++ &format!("this call to `{}` does nothing", call_name),
++ "try this",
++ snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(),
++ applicability,
++ );
++ }
++ }
++}
++
++fn ty_has_iter_method(cx: &LateContext<'_, '_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> {
++ has_iter_method(cx, self_ref_ty).map(|ty_name| {
++ let mutbl = match self_ref_ty.kind {
++ ty::Ref(_, _, mutbl) => mutbl,
++ _ => unreachable!(),
++ };
++ let method_name = match mutbl {
++ hir::Mutability::Not => "iter",
++ hir::Mutability::Mut => "iter_mut",
++ };
++ (ty_name, method_name)
++ })
++}
++
++fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) {
++ if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
++ return;
++ }
++ if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
++ span_lint_and_sugg(
++ cx,
++ INTO_ITER_ON_REF,
++ method_span,
++ &format!(
++ "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`",
++ method_name, kind,
++ ),
++ "call directly",
++ method_name.to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
++fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) {
++ if_chain! {
++ if let hir::ExprKind::Call(ref callee, ref args) = expr.kind;
++ if args.is_empty();
++ if let hir::ExprKind::Path(ref path) = callee.kind;
++ if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
++ if !is_maybe_uninit_ty_valid(cx, cx.tables.expr_ty_adjusted(outer));
++ then {
++ span_lint(
++ cx,
++ UNINIT_ASSUMED_INIT,
++ outer.span,
++ "this call for this type may be undefined behavior"
++ );
++ }
++ }
++}
++
++fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++ match ty.kind {
++ ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
++ ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
++ ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
++ _ => false,
++ }
++}
++
++fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
++ span_lint_and_help(
++ cx,
++ SUSPICIOUS_MAP,
++ expr.span,
++ "this call to `map()` won't have an effect on the call to `count()`",
++ None,
++ "make sure you did not confuse `map` with `filter` or `for_each`",
++ );
++}
++
++/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
++fn lint_option_as_ref_deref<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &hir::Expr<'_>,
++ as_ref_args: &[hir::Expr<'_>],
++ map_args: &[hir::Expr<'_>],
++ is_mut: bool,
++) {
++ let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
++
++ let option_ty = cx.tables.expr_ty(&as_ref_args[0]);
++ if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) {
++ return;
++ }
++
++ let deref_aliases: [&[&str]; 9] = [
++ &paths::DEREF_TRAIT_METHOD,
++ &paths::DEREF_MUT_TRAIT_METHOD,
++ &paths::CSTRING_AS_C_STR,
++ &paths::OS_STRING_AS_OS_STR,
++ &paths::PATH_BUF_AS_PATH,
++ &paths::STRING_AS_STR,
++ &paths::STRING_AS_MUT_STR,
++ &paths::VEC_AS_SLICE,
++ &paths::VEC_AS_MUT_SLICE,
++ ];
++
++ let is_deref = match map_args[1].kind {
++ hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)),
++ hir::ExprKind::Closure(_, _, body_id, _, _) => {
++ let closure_body = cx.tcx.hir().body(body_id);
++ let closure_expr = remove_blocks(&closure_body.value);
++
++ match &closure_expr.kind {
++ hir::ExprKind::MethodCall(_, _, args) => {
++ if_chain! {
++ if args.len() == 1;
++ if let hir::ExprKind::Path(qpath) = &args[0].kind;
++ if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, args[0].hir_id);
++ if closure_body.params[0].pat.hir_id == local_id;
++ let adj = cx.tables.expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::<Box<[_]>>();
++ if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
++ then {
++ let method_did = cx.tables.type_dependent_def_id(closure_expr.hir_id).unwrap();
++ deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
++ } else {
++ false
++ }
++ }
++ },
++ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => {
++ if_chain! {
++ if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner1) = inner.kind;
++ if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner2) = inner1.kind;
++ if let hir::ExprKind::Path(ref qpath) = inner2.kind;
++ if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, inner2.hir_id);
++ then {
++ closure_body.params[0].pat.hir_id == local_id
++ } else {
++ false
++ }
++ }
++ },
++ _ => false,
++ }
++ },
++ _ => false,
++ };
++
++ if is_deref {
++ let current_method = if is_mut {
++ format!(".as_mut().map({})", snippet(cx, map_args[1].span, ".."))
++ } else {
++ format!(".as_ref().map({})", snippet(cx, map_args[1].span, ".."))
++ };
++ let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
++ let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint);
++ let suggestion = format!("try using {} instead", method_hint);
++
++ let msg = format!(
++ "called `{0}` on an Option value. This can be done more directly \
++ by calling `{1}` instead",
++ current_method, hint
++ );
++ span_lint_and_sugg(
++ cx,
++ OPTION_AS_REF_DEREF,
++ expr.span,
++ &msg,
++ &suggestion,
++ hint,
++ Applicability::MachineApplicable,
++ );
++ }
++}
++
++/// Given a `Result<T, E>` type, return its error type (`E`).
++fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
++ match ty.kind {
++ ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1),
++ _ => None,
++ }
++}
++
++/// This checks whether a given type is known to implement Debug.
++fn has_debug_impl<'a, 'b>(ty: Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
++ cx.tcx
++ .get_diagnostic_item(sym::debug_trait)
++ .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
++}
++
++enum Convention {
++ Eq(&'static str),
++ StartsWith(&'static str),
++}
++
++#[rustfmt::skip]
++const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [
++ (Convention::Eq("new"), &[SelfKind::No]),
++ (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
++ (Convention::StartsWith("from_"), &[SelfKind::No]),
++ (Convention::StartsWith("into_"), &[SelfKind::Value]),
++ (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]),
++ (Convention::Eq("to_mut"), &[SelfKind::RefMut]),
++ (Convention::StartsWith("to_"), &[SelfKind::Ref]),
++];
++
++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,
++};
++
++#[rustfmt::skip]
++const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [
++ ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"),
++ ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
++ ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
++ ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
++ ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
++ ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
++ ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
++ ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
++ ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
++ ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
++ ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"),
++ ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
++ ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
++ ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"),
++ ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
++ ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
++ ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
++ ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"),
++ ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
++ ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
++ ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
++ ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
++ ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"),
++ ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"),
++ ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
++ ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"),
++ ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"),
++ ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"),
++ ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"),
++ ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"),
++];
++
++#[rustfmt::skip]
++const PATTERN_METHODS: [(&str, usize); 17] = [
++ ("contains", 1),
++ ("starts_with", 1),
++ ("ends_with", 1),
++ ("find", 1),
++ ("rfind", 1),
++ ("split", 1),
++ ("rsplit", 1),
++ ("split_terminator", 1),
++ ("rsplit_terminator", 1),
++ ("splitn", 2),
++ ("rsplitn", 2),
++ ("matches", 1),
++ ("rmatches", 1),
++ ("match_indices", 1),
++ ("rmatch_indices", 1),
++ ("trim_start_matches", 1),
++ ("trim_end_matches", 1),
++];
++
++#[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",
++ }
++ }
++}
++
++impl Convention {
++ #[must_use]
++ fn check(&self, other: &str) -> bool {
++ match *self {
++ Self::Eq(this) => this == other,
++ Self::StartsWith(this) => other.starts_with(this) && this != other,
++ }
++ }
++}
++
++impl fmt::Display for Convention {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
++ match *self {
++ Self::Eq(this) => this.fmt(f),
++ Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
++ }
++ }
++}
++
++#[derive(Clone, Copy)]
++enum OutType {
++ Unit,
++ Bool,
++ Any,
++ Ref,
++}
++
++impl OutType {
++ fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FnRetTy<'_>) -> bool {
++ let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[]));
++ match (self, ty) {
++ (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
++ (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true,
++ (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true,
++ (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true,
++ (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
++ _ => false,
++ }
++ }
++}
++
++fn is_bool(ty: &hir::Ty<'_>) -> bool {
++ if let hir::TyKind::Path(ref p) = ty.kind {
++ match_qpath(p, &["bool"])
++ } else {
++ false
++ }
++}
++
++// Returns `true` if `expr` contains a return expression
++fn contains_return(expr: &hir::Expr<'_>) -> bool {
++ struct RetCallFinder {
++ found: bool,
++ }
++
++ impl<'tcx> 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 {
++ intravisit::walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++ }
++
++ let mut visitor = RetCallFinder { found: false };
++ visitor.visit_expr(expr);
++ visitor.found
++}
++
++fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ if_chain! {
++ if args.len() == 2;
++ if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind;
++ if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
++ if layout.is_zst();
++ then {
++ span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
++ }
++ }
++}
++
++fn lint_filetype_is_file(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ let ty = cx.tables.expr_ty(&args[0]);
++
++ if !match_type(cx, ty, &paths::FILE_TYPE) {
++ return;
++ }
++
++ let span: Span;
++ let verb: &str;
++ let lint_unary: &str;
++ let help_unary: &str;
++ if_chain! {
++ if let Some(parent) = get_parent_expr(cx, expr);
++ if let hir::ExprKind::Unary(op, _) = parent.kind;
++ if op == hir::UnOp::UnNot;
++ then {
++ lint_unary = "!";
++ verb = "denies";
++ help_unary = "";
++ span = parent.span;
++ } else {
++ lint_unary = "";
++ verb = "covers";
++ help_unary = "!";
++ span = expr.span;
++ }
++ }
++ let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb);
++ let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary);
++ span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg);
++}
++
++fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
++ expected.constness == actual.constness
++ && expected.unsafety == actual.unsafety
++ && expected.asyncness == actual.asyncness
++}
--- /dev/null
--- /dev/null
++use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then};
++use crate::utils::{is_copy, is_type_diagnostic_item};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
++use rustc_hir::{self, HirId, Path};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_span::source_map::Span;
++use rustc_span::symbol::Symbol;
++
++use super::OPTION_MAP_UNWRAP_OR;
++
++/// lint use of `map().unwrap_or()` for `Option`s
++pub(super) fn lint<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &rustc_hir::Expr<'_>,
++ map_args: &'tcx [rustc_hir::Expr<'_>],
++ unwrap_args: &'tcx [rustc_hir::Expr<'_>],
++ map_span: Span,
++) {
++ // lint if the caller of `map()` is an `Option`
++ if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) {
++ if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) {
++ // Do not lint if the `map` argument uses identifiers in the `map`
++ // argument that are also used in the `unwrap_or` argument
++
++ let mut unwrap_visitor = UnwrapVisitor {
++ cx,
++ identifiers: FxHashSet::default(),
++ };
++ unwrap_visitor.visit_expr(&unwrap_args[1]);
++
++ let mut map_expr_visitor = MapExprVisitor {
++ cx,
++ identifiers: unwrap_visitor.identifiers,
++ found_identifier: false,
++ };
++ map_expr_visitor.visit_expr(&map_args[1]);
++
++ if map_expr_visitor.found_identifier {
++ return;
++ }
++ }
++
++ if differing_macro_contexts(unwrap_args[1].span, map_span) {
++ return;
++ }
++
++ let mut applicability = Applicability::MachineApplicable;
++ // get snippet for unwrap_or()
++ let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability);
++ // lint message
++ // comparing the snippet from source to raw text ("None") below is safe
++ // because we already have checked the type.
++ let arg = if unwrap_snippet == "None" { "None" } else { "a" };
++ let unwrap_snippet_none = unwrap_snippet == "None";
++ let suggest = if unwrap_snippet_none {
++ "and_then(f)"
++ } else {
++ "map_or(a, f)"
++ };
++ let msg = &format!(
++ "called `map(f).unwrap_or({})` on an `Option` value. \
++ This can be done more directly by calling `{}` instead",
++ arg, suggest
++ );
++
++ span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |diag| {
++ let map_arg_span = map_args[1].span;
++
++ let mut suggestion = vec![
++ (
++ map_span,
++ String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
++ ),
++ (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")),
++ ];
++
++ if !unwrap_snippet_none {
++ suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet)));
++ }
++
++ diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
++ });
++ }
++}
++
++struct UnwrapVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ identifiers: FxHashSet<Symbol>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++ self.identifiers.insert(ident(path));
++ walk_path(self, path);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::All(self.cx.tcx.hir())
++ }
++}
++
++struct MapExprVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ identifiers: FxHashSet<Symbol>,
++ found_identifier: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++ if self.identifiers.contains(&ident(path)) {
++ self.found_identifier = true;
++ return;
++ }
++ walk_path(self, path);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::All(self.cx.tcx.hir())
++ }
++}
++
++fn ident(path: &Path<'_>) -> Symbol {
++ path.segments
++ .last()
++ .expect("segments should be composed of at least 1 element")
++ .ident
++ .name
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths;
++use crate::utils::usage::mutated_variables;
++use crate::utils::{match_qpath, match_trait_method, span_lint};
++use rustc_hir as hir;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++
++use if_chain::if_chain;
++
++use super::UNNECESSARY_FILTER_MAP;
++
++pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++ if !match_trait_method(cx, expr, &paths::ITERATOR) {
++ return;
++ }
++
++ if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind {
++ let body = cx.tcx.hir().body(body_id);
++ let arg_id = body.params[0].pat.hir_id;
++ let mutates_arg =
++ mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
++
++ let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value);
++
++ let mut return_visitor = ReturnVisitor::new(&cx, arg_id);
++ return_visitor.visit_expr(&body.value);
++ found_mapping |= return_visitor.found_mapping;
++ found_filtering |= return_visitor.found_filtering;
++
++ if !found_filtering {
++ span_lint(
++ cx,
++ UNNECESSARY_FILTER_MAP,
++ expr.span,
++ "this `.filter_map` can be written more simply using `.map`",
++ );
++ return;
++ }
++
++ if !found_mapping && !mutates_arg {
++ span_lint(
++ cx,
++ UNNECESSARY_FILTER_MAP,
++ expr.span,
++ "this `.filter_map` can be written more simply using `.filter`",
++ );
++ return;
++ }
++ }
++}
++
++// returns (found_mapping, found_filtering)
++fn check_expression<'a, 'tcx>(
++ cx: &'a LateContext<'a, 'tcx>,
++ arg_id: hir::HirId,
++ expr: &'tcx hir::Expr<'_>,
++) -> (bool, bool) {
++ match &expr.kind {
++ hir::ExprKind::Call(ref func, ref args) => {
++ if_chain! {
++ if let hir::ExprKind::Path(ref path) = func.kind;
++ then {
++ if match_qpath(path, &paths::OPTION_SOME) {
++ if_chain! {
++ if let hir::ExprKind::Path(path) = &args[0].kind;
++ if let Res::Local(ref local) = cx.tables.qpath_res(path, args[0].hir_id);
++ then {
++ if arg_id == *local {
++ return (false, false)
++ }
++ }
++ }
++ return (true, false);
++ } else {
++ // We don't know. It might do anything.
++ return (true, true);
++ }
++ }
++ }
++ (true, true)
++ },
++ hir::ExprKind::Block(ref block, _) => {
++ if let Some(expr) = &block.expr {
++ check_expression(cx, arg_id, &expr)
++ } else {
++ (false, false)
++ }
++ },
++ hir::ExprKind::Match(_, arms, _) => {
++ let mut found_mapping = false;
++ let mut found_filtering = false;
++ for arm in *arms {
++ let (m, f) = check_expression(cx, arg_id, &arm.body);
++ found_mapping |= m;
++ found_filtering |= f;
++ }
++ (found_mapping, found_filtering)
++ },
++ hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
++ _ => (true, true),
++ }
++}
++
++struct ReturnVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ arg_id: hir::HirId,
++ // Found a non-None return that isn't Some(input)
++ found_mapping: bool,
++ // Found a return that isn't Some
++ found_filtering: bool,
++}
++
++impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
++ fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
++ ReturnVisitor {
++ cx,
++ arg_id,
++ found_mapping: false,
++ found_filtering: false,
++ }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
++ let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
++ self.found_mapping |= found_mapping;
++ self.found_filtering |= found_filtering;
++ } else {
++ walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant_simple, Constant};
++use crate::utils::{match_def_path, paths, span_lint};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::cmp::Ordering;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for expressions where `std::cmp::min` and `max` are
++ /// used to clamp values, but switched so that the result is constant.
++ ///
++ /// **Why is this bad?** This is in all probability not the intended outcome. At
++ /// the least it hurts readability of the code.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// min(0, max(100, x))
++ /// ```
++ /// It will always be equal to `0`. Probably the author meant to clamp the value
++ /// between 0 and 100, but has erroneously swapped `min` and `max`.
++ pub MIN_MAX,
++ correctness,
++ "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
++}
++
++declare_lint_pass!(MinMaxPass => [MIN_MAX]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
++ if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) {
++ if outer_max == inner_max {
++ return;
++ }
++ match (
++ outer_max,
++ Constant::partial_cmp(cx.tcx, cx.tables.expr_ty(ie), &outer_c, &inner_c),
++ ) {
++ (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
++ _ => {
++ span_lint(
++ cx,
++ MIN_MAX,
++ expr.span,
++ "this `min`/`max` combination leads to constant result",
++ );
++ },
++ }
++ }
++ }
++ }
++}
++
++#[derive(PartialEq, Eq, Debug)]
++enum MinMax {
++ Min,
++ Max,
++}
++
++fn min_max<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
++ if let ExprKind::Call(ref path, ref args) = expr.kind {
++ if let ExprKind::Path(ref qpath) = path.kind {
++ cx.tables.qpath_res(qpath, path.hir_id).opt_def_id().and_then(|def_id| {
++ if match_def_path(cx, def_id, &paths::CMP_MIN) {
++ fetch_const(cx, args, MinMax::Min)
++ } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
++ fetch_const(cx, args, MinMax::Max)
++ } else {
++ None
++ }
++ })
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++}
++
++fn fetch_const<'a>(
++ cx: &LateContext<'_, '_>,
++ args: &'a [Expr<'a>],
++ m: MinMax,
++) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
++ if args.len() != 2 {
++ return None;
++ }
++ if let Some(c) = constant_simple(cx, cx.tables, &args[0]) {
++ if constant_simple(cx, cx.tables, &args[1]).is_none() {
++ // otherwise ignore
++ Some((m, c, &args[1]))
++ } else {
++ None
++ }
++ } else if let Some(c) = constant_simple(cx, cx.tables, &args[1]) {
++ Some((m, c, &args[0]))
++ } else {
++ None
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{
++ def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty,
++ TyKind, UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::hygiene::DesugaringKind;
++use rustc_span::source_map::{ExpnKind, Span};
++
++use crate::consts::{constant, Constant};
++use crate::utils::sugg::Sugg;
++use crate::utils::{
++ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
++ last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
++ span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for function arguments and let bindings denoted as
++ /// `ref`.
++ ///
++ /// **Why is this bad?** The `ref` declaration makes the function take an owned
++ /// value, but turns the argument into a reference (which means that the value
++ /// is destroyed when exiting the function). This adds not much value: either
++ /// take a reference type, or take an owned value and create references in the
++ /// body.
++ ///
++ /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
++ /// type of `x` is more obvious with the former.
++ ///
++ /// **Known problems:** If the argument is dereferenced within the function,
++ /// removing the `ref` will lead to errors. This can be fixed by removing the
++ /// dereferences, e.g., changing `*x` to `x` within the function.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(ref x: u8) -> bool {
++ /// true
++ /// }
++ /// ```
++ pub TOPLEVEL_REF_ARG,
++ style,
++ "an entire binding declared as `ref`, in a function argument or a `let` statement"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons to NaN.
++ ///
++ /// **Why is this bad?** NaN does not compare meaningfully to anything – not
++ /// even itself – so those comparisons are simply wrong.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1.0;
++ ///
++ /// if x == f32::NAN { }
++ /// ```
++ pub CMP_NAN,
++ correctness,
++ "comparisons to `NAN`, which will always return false, probably not intended"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for (in-)equality comparisons on floating-point
++ /// values (apart from zero), except in functions called `*eq*` (which probably
++ /// implement equality for a type involving floats).
++ ///
++ /// **Why is this bad?** Floating point calculations are usually imprecise, so
++ /// asking if two values are *exactly* equal is asking for trouble. For a good
++ /// guide on what to do, see [the floating point
++ /// guide](http://www.floating-point-gui.de/errors/comparison).
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 1.2331f64;
++ /// let y = 1.2332f64;
++ /// if y == 1.23f64 { }
++ /// if y != x {} // where both are floats
++ /// ```
++ pub FLOAT_CMP,
++ correctness,
++ "using `==` or `!=` on float values instead of comparing difference with an epsilon"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for conversions to owned values just for the sake
++ /// of a comparison.
++ ///
++ /// **Why is this bad?** The comparison can operate on a reference, so creating
++ /// an owned value effectively throws it away directly afterwards, which is
++ /// needlessly consuming code and heap space.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = "foo";
++ /// # let y = String::from("foo");
++ /// if x.to_owned() == y {}
++ /// ```
++ /// Could be written as
++ /// ```rust
++ /// # let x = "foo";
++ /// # let y = String::from("foo");
++ /// if x == y {}
++ /// ```
++ pub CMP_OWNED,
++ perf,
++ "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for getting the remainder of a division by one.
++ ///
++ /// **Why is this bad?** The result can only ever be zero. No one will write
++ /// such code deliberately, unless trying to win an Underhanded Rust
++ /// Contest. Even for that contest, it's probably a bad idea. Use something more
++ /// underhanded.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// let a = x % 1;
++ /// ```
++ pub MODULO_ONE,
++ correctness,
++ "taking a number modulo 1, which always returns 0"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the use of bindings with a single leading
++ /// underscore.
++ ///
++ /// **Why is this bad?** A single leading underscore is usually used to indicate
++ /// that a binding will not be used. Using such a binding breaks this
++ /// expectation.
++ ///
++ /// **Known problems:** The lint does not work properly with desugaring and
++ /// macro, it has been allowed in the mean time.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let _x = 0;
++ /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
++ /// // underscore. We should rename `_x` to `x`
++ /// ```
++ pub USED_UNDERSCORE_BINDING,
++ pedantic,
++ "using a binding which is prefixed with an underscore"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the use of short circuit boolean conditions as
++ /// a
++ /// statement.
++ ///
++ /// **Why is this bad?** Using a short circuit boolean condition as a statement
++ /// may hide the fact that the second part is executed or not depending on the
++ /// outcome of the first part.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// f() && g(); // We should write `if f() { g(); }`.
++ /// ```
++ pub SHORT_CIRCUIT_STATEMENT,
++ complexity,
++ "using a short circuit boolean condition as a statement"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Catch casts from `0` to some pointer type
++ ///
++ /// **Why is this bad?** This generally means `null` and is better expressed as
++ /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let a = 0 as *const u32;
++ /// ```
++ pub ZERO_PTR,
++ style,
++ "using `0 as *{const, mut} T`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for (in-)equality comparisons on floating-point
++ /// value and constant, except in functions called `*eq*` (which probably
++ /// implement equality for a type involving floats).
++ ///
++ /// **Why is this bad?** Floating point calculations are usually imprecise, so
++ /// asking if two values are *exactly* equal is asking for trouble. For a good
++ /// guide on what to do, see [the floating point
++ /// guide](http://www.floating-point-gui.de/errors/comparison).
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x: f64 = 1.0;
++ /// const ONE: f64 = 1.00;
++ /// x == ONE; // where both are floats
++ /// ```
++ pub FLOAT_CMP_CONST,
++ restriction,
++ "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
++}
++
++declare_lint_pass!(MiscLints => [
++ TOPLEVEL_REF_ARG,
++ CMP_NAN,
++ FLOAT_CMP,
++ CMP_OWNED,
++ MODULO_ONE,
++ USED_UNDERSCORE_BINDING,
++ SHORT_CIRCUIT_STATEMENT,
++ ZERO_PTR,
++ FLOAT_CMP_CONST
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ k: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ _: Span,
++ _: HirId,
++ ) {
++ if let FnKind::Closure(_) = k {
++ // Does not apply to closures
++ return;
++ }
++ for arg in iter_input_pats(decl, body) {
++ match arg.pat.kind {
++ PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => {
++ span_lint(
++ cx,
++ TOPLEVEL_REF_ARG,
++ arg.pat.span,
++ "`ref` directly on a function argument is ignored. Consider using a reference type \
++ instead.",
++ );
++ },
++ _ => {},
++ }
++ }
++ }
++
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++ if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let PatKind::Binding(an, .., name, None) = local.pat.kind;
++ if let Some(ref init) = local.init;
++ if !higher::is_from_for_desugar(local);
++ then {
++ if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
++ let sugg_init = if init.span.from_expansion() {
++ Sugg::hir_with_macro_callsite(cx, init, "..")
++ } else {
++ Sugg::hir(cx, init, "..")
++ };
++ let (mutopt, initref) = if an == BindingAnnotation::RefMut {
++ ("mut ", sugg_init.mut_addr())
++ } else {
++ ("", sugg_init.addr())
++ };
++ let tyopt = if let Some(ref ty) = local.ty {
++ format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
++ } else {
++ String::new()
++ };
++ span_lint_hir_and_then(
++ cx,
++ TOPLEVEL_REF_ARG,
++ init.hir_id,
++ local.pat.span,
++ "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
++ |diag| {
++ diag.span_suggestion(
++ stmt.span,
++ "try",
++ format!(
++ "let {name}{tyopt} = {initref};",
++ name=snippet(cx, name.span, "_"),
++ tyopt=tyopt,
++ initref=initref,
++ ),
++ Applicability::MachineApplicable,
++ );
++ }
++ );
++ }
++ }
++ };
++ if_chain! {
++ if let StmtKind::Semi(ref expr) = stmt.kind;
++ if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind;
++ if binop.node == BinOpKind::And || binop.node == BinOpKind::Or;
++ if let Some(sugg) = Sugg::hir_opt(cx, a);
++ then {
++ span_lint_and_then(cx,
++ SHORT_CIRCUIT_STATEMENT,
++ stmt.span,
++ "boolean short circuit operator in statement may be clearer using an explicit test",
++ |diag| {
++ let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
++ diag.span_suggestion(
++ stmt.span,
++ "replace it with",
++ format!(
++ "if {} {{ {}; }}",
++ sugg,
++ &snippet(cx, b.span, ".."),
++ ),
++ Applicability::MachineApplicable, // snippet
++ );
++ });
++ }
++ };
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ match expr.kind {
++ ExprKind::Cast(ref e, ref ty) => {
++ check_cast(cx, expr.span, e, ty);
++ return;
++ },
++ ExprKind::Binary(ref cmp, ref left, ref right) => {
++ let op = cmp.node;
++ if op.is_comparison() {
++ check_nan(cx, left, expr);
++ check_nan(cx, right, expr);
++ check_to_owned(cx, left, right);
++ check_to_owned(cx, right, left);
++ }
++ if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
++ if is_allowed(cx, left) || is_allowed(cx, right) {
++ return;
++ }
++
++ // Allow comparing the results of signum()
++ if is_signum(cx, left) && is_signum(cx, right) {
++ return;
++ }
++
++ if let Some(name) = get_item_name(cx, expr) {
++ let name = name.as_str();
++ if name == "eq"
++ || name == "ne"
++ || name == "is_nan"
++ || name.starts_with("eq_")
++ || name.ends_with("_eq")
++ {
++ return;
++ }
++ }
++ let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
++ let (lint, msg) = get_lint_and_message(
++ is_named_constant(cx, left) || is_named_constant(cx, right),
++ is_comparing_arrays,
++ );
++ span_lint_and_then(cx, lint, expr.span, msg, |diag| {
++ let lhs = Sugg::hir(cx, left, "..");
++ let rhs = Sugg::hir(cx, right, "..");
++
++ if !is_comparing_arrays {
++ diag.span_suggestion(
++ expr.span,
++ "consider comparing them within some error",
++ format!(
++ "({}).abs() {} error",
++ lhs - rhs,
++ if op == BinOpKind::Eq { '<' } else { '>' }
++ ),
++ Applicability::HasPlaceholders, // snippet
++ );
++ }
++ diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
++ });
++ } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
++ span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
++ }
++ },
++ _ => {},
++ }
++ if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
++ // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
++ return;
++ }
++ let binding = match expr.kind {
++ ExprKind::Path(ref qpath) => {
++ let binding = last_path_segment(qpath).ident.as_str();
++ if binding.starts_with('_') &&
++ !binding.starts_with("__") &&
++ binding != "_result" && // FIXME: #944
++ is_used(cx, expr) &&
++ // don't lint if the declaration is in a macro
++ non_macro_local(cx, cx.tables.qpath_res(qpath, expr.hir_id))
++ {
++ Some(binding)
++ } else {
++ None
++ }
++ },
++ ExprKind::Field(_, ident) => {
++ let name = ident.as_str();
++ if name.starts_with('_') && !name.starts_with("__") {
++ Some(name)
++ } else {
++ None
++ }
++ },
++ _ => None,
++ };
++ if let Some(binding) = binding {
++ span_lint(
++ cx,
++ USED_UNDERSCORE_BINDING,
++ expr.span,
++ &format!(
++ "used binding `{}` which is prefixed with an underscore. A leading \
++ underscore signals that a binding will not be used.",
++ binding
++ ),
++ );
++ }
++ }
++}
++
++fn get_lint_and_message(
++ is_comparing_constants: bool,
++ is_comparing_arrays: bool,
++) -> (&'static rustc_lint::Lint, &'static str) {
++ if is_comparing_constants {
++ (
++ FLOAT_CMP_CONST,
++ if is_comparing_arrays {
++ "strict comparison of `f32` or `f64` constant arrays"
++ } else {
++ "strict comparison of `f32` or `f64` constant"
++ },
++ )
++ } else {
++ (
++ FLOAT_CMP,
++ if is_comparing_arrays {
++ "strict comparison of `f32` or `f64` arrays"
++ } else {
++ "strict comparison of `f32` or `f64`"
++ },
++ )
++ }
++}
++
++fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
++ if_chain! {
++ if !in_constant(cx, cmp_expr.hir_id);
++ if let Some((value, _)) = constant(cx, cx.tables, expr);
++ then {
++ let needs_lint = match value {
++ Constant::F32(num) => num.is_nan(),
++ Constant::F64(num) => num.is_nan(),
++ _ => false,
++ };
++
++ if needs_lint {
++ span_lint(
++ cx,
++ CMP_NAN,
++ cmp_expr.span,
++ "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
++ );
++ }
++ }
++ }
++}
++
++fn is_named_constant<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool {
++ if let Some((_, res)) = constant(cx, cx.tables, expr) {
++ res
++ } else {
++ false
++ }
++}
++
++fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool {
++ match constant(cx, cx.tables, expr) {
++ Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
++ Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
++ Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
++ Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
++ Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
++ _ => false,
++ }),
++ _ => false,
++ }
++}
++
++// Return true if `expr` is the result of `signum()` invoked on a float value.
++fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ // The negation of a signum is still a signum
++ if let ExprKind::Unary(UnOp::UnNeg, ref child_expr) = expr.kind {
++ return is_signum(cx, &child_expr);
++ }
++
++ if_chain! {
++ if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.kind;
++ if sym!(signum) == method_name.ident.name;
++ // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
++ // the method call)
++ then {
++ return is_float(cx, &expressions[0]);
++ }
++ }
++ false
++}
++
++fn is_float(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ let value = &walk_ptrs_ty(cx.tables.expr_ty(expr)).kind;
++
++ if let ty::Array(arr_ty, _) = value {
++ return matches!(arr_ty.kind, ty::Float(_));
++ };
++
++ matches!(value, ty::Float(_))
++}
++
++fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _))
++}
++
++fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
++ let (arg_ty, snip) = match expr.kind {
++ ExprKind::MethodCall(.., ref args) if args.len() == 1 => {
++ if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
++ (cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
++ } else {
++ return;
++ }
++ },
++ ExprKind::Call(ref path, ref v) if v.len() == 1 => {
++ if let ExprKind::Path(ref path) = path.kind {
++ if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
++ (cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
++ } else {
++ return;
++ }
++ } else {
++ return;
++ }
++ },
++ _ => return,
++ };
++
++ let other_ty = cx.tables.expr_ty_adjusted(other);
++ let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
++ Some(id) => id,
++ None => return,
++ };
++
++ let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| {
++ implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()])
++ });
++ let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| {
++ implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()])
++ });
++ let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
++
++ if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other {
++ return;
++ }
++
++ let other_gets_derefed = match other.kind {
++ ExprKind::Unary(UnOp::UnDeref, _) => true,
++ _ => false,
++ };
++
++ let lint_span = if other_gets_derefed {
++ expr.span.to(other.span)
++ } else {
++ expr.span
++ };
++
++ span_lint_and_then(
++ cx,
++ CMP_OWNED,
++ lint_span,
++ "this creates an owned instance just for comparison",
++ |diag| {
++ // This also catches `PartialEq` implementations that call `to_owned`.
++ if other_gets_derefed {
++ diag.span_label(lint_span, "try implementing the comparison without allocating");
++ return;
++ }
++
++ let try_hint = if deref_arg_impl_partial_eq_other {
++ // suggest deref on the left
++ format!("*{}", snip)
++ } else {
++ // suggest dropping the to_owned on the left
++ snip.to_string()
++ };
++
++ diag.span_suggestion(
++ lint_span,
++ "try",
++ try_hint,
++ Applicability::MachineApplicable, // snippet
++ );
++ },
++ );
++}
++
++/// Heuristic to see if an expression is used. Should be compatible with
++/// `unused_variables`'s idea
++/// of what it means for an expression to be "used".
++fn is_used(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ if let Some(parent) = get_parent_expr(cx, expr) {
++ match parent.kind {
++ ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => {
++ SpanlessEq::new(cx).eq_expr(rhs, expr)
++ },
++ _ => is_used(cx, parent),
++ }
++ } else {
++ true
++ }
++}
++
++/// Tests whether an expression is in a macro expansion (e.g., something
++/// generated by `#[derive(...)]` or the like).
++fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
++ use rustc_span::hygiene::MacroKind;
++ if expr.span.from_expansion() {
++ let data = expr.span.ctxt().outer_expn_data();
++
++ if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind {
++ true
++ } else {
++ false
++ }
++ } else {
++ false
++ }
++}
++
++/// Tests whether `res` is a variable defined outside a macro.
++fn non_macro_local(cx: &LateContext<'_, '_>, res: def::Res) -> bool {
++ if let def::Res::Local(id) = res {
++ !cx.tcx.hir().span(id).from_expansion()
++ } else {
++ false
++ }
++}
++
++fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
++ if_chain! {
++ if let TyKind::Ptr(ref mut_ty) = ty.kind;
++ if let ExprKind::Lit(ref lit) = e.kind;
++ if let LitKind::Int(0, _) = lit.node;
++ if !in_constant(cx, e.hir_id);
++ then {
++ let (msg, sugg_fn) = match mut_ty.mutbl {
++ Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
++ Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
++ };
++
++ let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
++ (format!("{}()", sugg_fn), Applicability::MachineApplicable)
++ } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
++ (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable)
++ } else {
++ // `MaybeIncorrect` as type inference may not work with the suggested code
++ (format!("{}()", sugg_fn), Applicability::MaybeIncorrect)
++ };
++ span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg,
++ span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{
++ BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
++ NodeId, Pat, PatKind, StmtKind, UnOp,
++};
++use rustc_ast::visit::{walk_expr, FnKind, Visitor};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for structure field patterns bound to wildcards.
++ ///
++ /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
++ /// the fields that are actually bound.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let { a: _, b: ref b, c: _ } = ..
++ /// ```
++ pub UNNEEDED_FIELD_PATTERN,
++ restriction,
++ "struct fields bound to a wildcard instead of using `..`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for function arguments having the similar names
++ /// differing by an underscore.
++ ///
++ /// **Why is this bad?** It affects code readability.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(a: i32, _a: i32) {}
++ /// ```
++ pub DUPLICATE_UNDERSCORE_ARGUMENT,
++ style,
++ "function arguments having names which only differ by an underscore"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Detects closures called in the same expression where they
++ /// are defined.
++ ///
++ /// **Why is this bad?** It is unnecessarily adding to the expression's
++ /// complexity.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// (|| 42)()
++ /// ```
++ pub REDUNDANT_CLOSURE_CALL,
++ complexity,
++ "throwaway closures called in the expression they are defined"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Detects expressions of the form `--x`.
++ ///
++ /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
++ /// decremented.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let mut x = 3;
++ /// --x;
++ /// ```
++ pub DOUBLE_NEG,
++ style,
++ "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns on hexadecimal literals with mixed-case letter
++ /// digits.
++ ///
++ /// **Why is this bad?** It looks confusing.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let y = 0x1a9BAcD;
++ /// ```
++ pub MIXED_CASE_HEX_LITERALS,
++ style,
++ "hex literals whose letter digits are not consistently upper- or lowercased"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if literal suffixes are not separated by an
++ /// underscore.
++ ///
++ /// **Why is this bad?** It is much less readable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let y = 123832i32;
++ /// ```
++ pub UNSEPARATED_LITERAL_SUFFIX,
++ pedantic,
++ "literals whose suffix is not separated by an underscore"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if an integral constant literal starts with `0`.
++ ///
++ /// **Why is this bad?** In some languages (including the infamous C language
++ /// and most of its
++ /// family), this marks an octal constant. In Rust however, this is a decimal
++ /// constant. This could
++ /// be confusing for both the writer and a reader of the constant.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// In Rust:
++ /// ```rust
++ /// fn main() {
++ /// let a = 0123;
++ /// println!("{}", a);
++ /// }
++ /// ```
++ ///
++ /// prints `123`, while in C:
++ ///
++ /// ```c
++ /// #include <stdio.h>
++ ///
++ /// int main() {
++ /// int a = 0123;
++ /// printf("%d\n", a);
++ /// }
++ /// ```
++ ///
++ /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
++ pub ZERO_PREFIXED_LITERAL,
++ complexity,
++ "integer literals starting with `0`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if a generic shadows a built-in type.
++ ///
++ /// **Why is this bad?** This gives surprising type errors.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```ignore
++ /// impl<u32> Foo<u32> {
++ /// fn impl_func(&self) -> u32 {
++ /// 42
++ /// }
++ /// }
++ /// ```
++ pub BUILTIN_TYPE_SHADOW,
++ style,
++ "shadowing a builtin type"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for patterns in the form `name @ _`.
++ ///
++ /// **Why is this bad?** It's almost always more readable to just use direct
++ /// bindings.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let v = Some("abc");
++ ///
++ /// match v {
++ /// Some(x) => (),
++ /// y @ _ => (), // easier written as `y`,
++ /// }
++ /// ```
++ pub REDUNDANT_PATTERN,
++ style,
++ "using `name @ _` in a pattern"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for tuple patterns with a wildcard
++ /// pattern (`_`) is next to a rest pattern (`..`).
++ ///
++ /// _NOTE_: While `_, ..` means there is at least one element left, `..`
++ /// means there are 0 or more elements left. This can make a difference
++ /// when refactoring, but shouldn't result in errors in the refactored code,
++ /// since the wildcard pattern isn't used anyway.
++ /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
++ /// can match that element as well.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # struct TupleStruct(u32, u32, u32);
++ /// # let t = TupleStruct(1, 2, 3);
++ ///
++ /// match t {
++ /// TupleStruct(0, .., _) => (),
++ /// _ => (),
++ /// }
++ /// ```
++ /// can be written as
++ /// ```rust
++ /// # struct TupleStruct(u32, u32, u32);
++ /// # let t = TupleStruct(1, 2, 3);
++ ///
++ /// match t {
++ /// TupleStruct(0, ..) => (),
++ /// _ => (),
++ /// }
++ /// ```
++ pub UNNEEDED_WILDCARD_PATTERN,
++ complexity,
++ "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
++}
++
++declare_lint_pass!(MiscEarlyLints => [
++ UNNEEDED_FIELD_PATTERN,
++ DUPLICATE_UNDERSCORE_ARGUMENT,
++ REDUNDANT_CLOSURE_CALL,
++ DOUBLE_NEG,
++ MIXED_CASE_HEX_LITERALS,
++ UNSEPARATED_LITERAL_SUFFIX,
++ ZERO_PREFIXED_LITERAL,
++ BUILTIN_TYPE_SHADOW,
++ REDUNDANT_PATTERN,
++ UNNEEDED_WILDCARD_PATTERN,
++]);
++
++// Used to find `return` statements or equivalents e.g., `?`
++struct ReturnVisitor {
++ found_return: bool,
++}
++
++impl ReturnVisitor {
++ #[must_use]
++ fn new() -> Self {
++ Self { found_return: false }
++ }
++}
++
++impl<'ast> Visitor<'ast> for ReturnVisitor {
++ fn visit_expr(&mut self, ex: &'ast Expr) {
++ if let ExprKind::Ret(_) = ex.kind {
++ self.found_return = true;
++ } else if let ExprKind::Try(_) = ex.kind {
++ self.found_return = true;
++ }
++
++ walk_expr(self, ex)
++ }
++}
++
++impl EarlyLintPass for MiscEarlyLints {
++ fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
++ for param in &gen.params {
++ if let GenericParamKind::Type { .. } = param.kind {
++ let name = param.ident.as_str();
++ if constants::BUILTIN_TYPES.contains(&&*name) {
++ span_lint(
++ cx,
++ BUILTIN_TYPE_SHADOW,
++ param.ident.span,
++ &format!("This generic shadows the built-in type `{}`", name),
++ );
++ }
++ }
++ }
++ }
++
++ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
++ if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
++ let mut wilds = 0;
++ let type_name = npat
++ .segments
++ .last()
++ .expect("A path must have at least one segment")
++ .ident
++ .name;
++
++ for field in pfields {
++ if let PatKind::Wild = field.pat.kind {
++ wilds += 1;
++ }
++ }
++ if !pfields.is_empty() && wilds == pfields.len() {
++ span_lint_and_help(
++ cx,
++ UNNEEDED_FIELD_PATTERN,
++ pat.span,
++ "All the struct fields are matched to a wildcard pattern, consider using `..`.",
++ None,
++ &format!("Try with `{} {{ .. }}` instead", type_name),
++ );
++ return;
++ }
++ if wilds > 0 {
++ for field in pfields {
++ if let PatKind::Wild = field.pat.kind {
++ wilds -= 1;
++ if wilds > 0 {
++ span_lint(
++ cx,
++ UNNEEDED_FIELD_PATTERN,
++ field.span,
++ "You matched a field with a wildcard pattern. Consider using `..` instead",
++ );
++ } else {
++ let mut normal = vec![];
++
++ for field in pfields {
++ match field.pat.kind {
++ PatKind::Wild => {},
++ _ => {
++ if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
++ normal.push(n);
++ }
++ },
++ }
++ }
++
++ span_lint_and_help(
++ cx,
++ UNNEEDED_FIELD_PATTERN,
++ field.span,
++ "You matched a field with a wildcard pattern. Consider using `..` \
++ instead",
++ None,
++ &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
++ );
++ }
++ }
++ }
++ }
++ }
++
++ if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
++ let left_binding = match left {
++ BindingMode::ByRef(Mutability::Mut) => "ref mut ",
++ BindingMode::ByRef(Mutability::Not) => "ref ",
++ _ => "",
++ };
++
++ if let PatKind::Wild = right.kind {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_PATTERN,
++ pat.span,
++ &format!(
++ "the `{} @ _` pattern can be written as just `{}`",
++ ident.name, ident.name,
++ ),
++ "try",
++ format!("{}{}", left_binding, ident.name),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++
++ check_unneeded_wildcard_pattern(cx, pat);
++ }
++
++ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
++ let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
++
++ for arg in &fn_kind.decl().inputs {
++ if let PatKind::Ident(_, ident, None) = arg.pat.kind {
++ let arg_name = ident.to_string();
++
++ if arg_name.starts_with('_') {
++ if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
++ span_lint(
++ cx,
++ DUPLICATE_UNDERSCORE_ARGUMENT,
++ *correspondence,
++ &format!(
++ "`{}` already exists, having another argument having almost the same \
++ name makes code comprehension and documentation more difficult",
++ arg_name[1..].to_owned()
++ ),
++ );
++ }
++ } else {
++ registered_names.insert(arg_name, arg.pat.span);
++ }
++ }
++ }
++ }
++
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++ match expr.kind {
++ ExprKind::Call(ref paren, _) => {
++ if let ExprKind::Paren(ref closure) = paren.kind {
++ if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind {
++ let mut visitor = ReturnVisitor::new();
++ visitor.visit_expr(block);
++ if !visitor.found_return {
++ span_lint_and_then(
++ cx,
++ REDUNDANT_CLOSURE_CALL,
++ expr.span,
++ "Try not to call a closure in the expression where it is declared.",
++ |diag| {
++ if decl.inputs.is_empty() {
++ let mut app = Applicability::MachineApplicable;
++ let hint =
++ snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
++ diag.span_suggestion(expr.span, "Try doing something like: ", hint, app);
++ }
++ },
++ );
++ }
++ }
++ }
++ },
++ ExprKind::Unary(UnOp::Neg, ref inner) => {
++ if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
++ span_lint(
++ cx,
++ DOUBLE_NEG,
++ expr.span,
++ "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
++ );
++ }
++ },
++ ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
++ _ => (),
++ }
++ }
++
++ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
++ for w in block.stmts.windows(2) {
++ if_chain! {
++ if let StmtKind::Local(ref local) = w[0].kind;
++ if let Option::Some(ref t) = local.init;
++ if let ExprKind::Closure(..) = t.kind;
++ if let PatKind::Ident(_, ident, _) = local.pat.kind;
++ if let StmtKind::Semi(ref second) = w[1].kind;
++ if let ExprKind::Assign(_, ref call, _) = second.kind;
++ if let ExprKind::Call(ref closure, _) = call.kind;
++ if let ExprKind::Path(_, ref path) = closure.kind;
++ then {
++ if ident == path.segments[0].ident {
++ span_lint(
++ cx,
++ REDUNDANT_CLOSURE_CALL,
++ second.span,
++ "Closure called just once immediately after it was declared",
++ );
++ }
++ }
++ }
++ }
++ }
++}
++
++impl MiscEarlyLints {
++ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
++ // We test if first character in snippet is a number, because the snippet could be an expansion
++ // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
++ // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
++ // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
++ // FIXME: Find a better way to detect those cases.
++ let lit_snip = match snippet_opt(cx, lit.span) {
++ Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
++ _ => return,
++ };
++
++ if let LitKind::Int(value, lit_int_type) = lit.kind {
++ let suffix = match lit_int_type {
++ LitIntType::Signed(ty) => ty.name_str(),
++ LitIntType::Unsigned(ty) => ty.name_str(),
++ LitIntType::Unsuffixed => "",
++ };
++
++ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++ val
++ } else {
++ return; // It's useless so shouldn't lint.
++ };
++ // Do not lint when literal is unsuffixed.
++ if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
++ span_lint_and_sugg(
++ cx,
++ UNSEPARATED_LITERAL_SUFFIX,
++ lit.span,
++ "integer type suffix should be separated by an underscore",
++ "add an underscore",
++ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++ Applicability::MachineApplicable,
++ );
++ }
++
++ if lit_snip.starts_with("0x") {
++ if maybe_last_sep_idx <= 2 {
++ // It's meaningless or causes range error.
++ return;
++ }
++ let mut seen = (false, false);
++ for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
++ match ch {
++ b'a'..=b'f' => seen.0 = true,
++ b'A'..=b'F' => seen.1 = true,
++ _ => {},
++ }
++ if seen.0 && seen.1 {
++ span_lint(
++ cx,
++ MIXED_CASE_HEX_LITERALS,
++ lit.span,
++ "inconsistent casing in hexadecimal literal",
++ );
++ break;
++ }
++ }
++ } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
++ /* nothing to do */
++ } else if value != 0 && lit_snip.starts_with('0') {
++ span_lint_and_then(
++ cx,
++ ZERO_PREFIXED_LITERAL,
++ lit.span,
++ "this is a decimal constant",
++ |diag| {
++ diag.span_suggestion(
++ lit.span,
++ "if you mean to use a decimal constant, remove the `0` to avoid confusion",
++ lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
++ Applicability::MaybeIncorrect,
++ );
++ diag.span_suggestion(
++ lit.span,
++ "if you mean to use an octal constant, use `0o`",
++ format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
++ Applicability::MaybeIncorrect,
++ );
++ },
++ );
++ }
++ } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
++ let suffix = float_ty.name_str();
++ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++ val
++ } else {
++ return; // It's useless so shouldn't lint.
++ };
++ if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
++ span_lint_and_sugg(
++ cx,
++ UNSEPARATED_LITERAL_SUFFIX,
++ lit.span,
++ "float type suffix should be separated by an underscore",
++ "add an underscore",
++ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++}
++
++fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
++ if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
++ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
++ span_lint_and_sugg(
++ cx,
++ UNNEEDED_WILDCARD_PATTERN,
++ span,
++ if only_one {
++ "this pattern is unneeded as the `..` pattern can match that element"
++ } else {
++ "these patterns are unneeded as the `..` pattern can match those elements"
++ },
++ if only_one { "remove it" } else { "remove them" },
++ "".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++
++ #[allow(clippy::trivially_copy_pass_by_ref)]
++ fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
++ if let PatKind::Wild = pat.kind {
++ true
++ } else {
++ false
++ }
++ }
++
++ if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
++ if let Some((left_index, left_pat)) = patterns[..rest_index]
++ .iter()
++ .rev()
++ .take_while(is_wild)
++ .enumerate()
++ .last()
++ {
++ span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
++ }
++
++ if let Some((right_index, right_pat)) =
++ patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
++ {
++ span_lint(
++ cx,
++ patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
++ right_index == 0,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
++use rustc_hir as hir;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++use rustc_typeck::hir_ty_to_ty;
++
++declare_clippy_lint! {
++ /// **What it does:**
++ ///
++ /// Suggests the use of `const` in functions and methods where possible.
++ ///
++ /// **Why is this bad?**
++ ///
++ /// Not having the function const prevents callers of the function from being const as well.
++ ///
++ /// **Known problems:**
++ ///
++ /// Const functions are currently still being worked on, with some features only being available
++ /// on nightly. This lint does not consider all edge cases currently and the suggestions may be
++ /// incorrect if you are using this lint on stable.
++ ///
++ /// Also, the lint only runs one pass over the code. Consider these two non-const functions:
++ ///
++ /// ```rust
++ /// fn a() -> i32 {
++ /// 0
++ /// }
++ /// fn b() -> i32 {
++ /// a()
++ /// }
++ /// ```
++ ///
++ /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
++ /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
++ /// will suggest to make `b` const, too.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// # struct Foo {
++ /// # random_number: usize,
++ /// # }
++ /// # impl Foo {
++ /// fn new() -> Self {
++ /// Self { random_number: 42 }
++ /// }
++ /// # }
++ /// ```
++ ///
++ /// Could be a const fn:
++ ///
++ /// ```rust
++ /// # struct Foo {
++ /// # random_number: usize,
++ /// # }
++ /// # impl Foo {
++ /// const fn new() -> Self {
++ /// Self { random_number: 42 }
++ /// }
++ /// # }
++ /// ```
++ pub MISSING_CONST_FOR_FN,
++ nursery,
++ "Lint functions definitions that could be made `const fn`"
++}
++
++declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'_, '_>,
++ kind: FnKind<'_>,
++ _: &FnDecl<'_>,
++ _: &Body<'_>,
++ span: Span,
++ hir_id: HirId,
++ ) {
++ let def_id = cx.tcx.hir().local_def_id(hir_id);
++
++ if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
++ return;
++ }
++
++ // Building MIR for `fn`s with unsatisfiable preds results in ICE.
++ if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
++ return;
++ }
++
++ // Perform some preliminary checks that rule out constness on the Clippy side. This way we
++ // can skip the actual const check and return early.
++ match kind {
++ FnKind::ItemFn(_, generics, header, ..) => {
++ let has_const_generic_params = generics
++ .params
++ .iter()
++ .any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
++
++ if already_const(header) || has_const_generic_params {
++ return;
++ }
++ },
++ FnKind::Method(_, sig, ..) => {
++ if trait_ref_of_method(cx, hir_id).is_some()
++ || already_const(sig.header)
++ || method_accepts_dropable(cx, sig.decl.inputs)
++ {
++ return;
++ }
++ },
++ _ => return,
++ }
++
++ let mir = cx.tcx.optimized_mir(def_id);
++
++ if let Err((span, err)) = is_min_const_fn(cx.tcx, def_id.to_def_id(), &mir) {
++ if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
++ cx.tcx.sess.span_err(span, &err);
++ }
++ } else {
++ span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
++ }
++ }
++}
++
++/// Returns true if any of the method parameters is a type that implements `Drop`. The method
++/// can't be made const then, because `drop` can't be const-evaluated.
++fn method_accepts_dropable(cx: &LateContext<'_, '_>, param_tys: &[hir::Ty<'_>]) -> bool {
++ // If any of the params are dropable, return true
++ param_tys.iter().any(|hir_ty| {
++ let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
++ has_drop(cx, ty_ty)
++ })
++}
++
++// We don't have to lint on something that's already `const`
++#[must_use]
++fn already_const(header: hir::FnHeader) -> bool {
++ header.constness == Constness::Const
++}
--- /dev/null
--- /dev/null
++// Note: More specifically this lint is largely inspired (aka copied) from
++// *rustc*'s
++// [`missing_doc`].
++//
++// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
++//
++
++use crate::utils::span_lint;
++use if_chain::if_chain;
++use rustc_ast::ast::{self, MetaItem, MetaItemKind};
++use rustc_ast::attr;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::ty;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Warns if there is missing doc for any documentable item
++ /// (public or private).
++ ///
++ /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
++ /// allowed-by-default lint for
++ /// public members, but has no way to enforce documentation of private items.
++ /// This lint fixes that.
++ ///
++ /// **Known problems:** None.
++ pub MISSING_DOCS_IN_PRIVATE_ITEMS,
++ restriction,
++ "detects missing documentation for public and private members"
++}
++
++pub struct MissingDoc {
++ /// Stack of whether #[doc(hidden)] is set
++ /// at each level which has lint attributes.
++ doc_hidden_stack: Vec<bool>,
++}
++
++impl Default for MissingDoc {
++ #[must_use]
++ fn default() -> Self {
++ Self::new()
++ }
++}
++
++impl MissingDoc {
++ #[must_use]
++ pub fn new() -> Self {
++ Self {
++ doc_hidden_stack: vec![false],
++ }
++ }
++
++ fn doc_hidden(&self) -> bool {
++ *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
++ }
++
++ fn has_include(meta: Option<MetaItem>) -> bool {
++ if_chain! {
++ if let Some(meta) = meta;
++ if let MetaItemKind::List(list) = meta.kind;
++ if let Some(meta) = list.get(0);
++ if let Some(name) = meta.ident();
++ then {
++ name.as_str() == "include"
++ } else {
++ false
++ }
++ }
++ }
++
++ fn check_missing_docs_attrs(
++ &self,
++ cx: &LateContext<'_, '_>,
++ attrs: &[ast::Attribute],
++ sp: Span,
++ desc: &'static str,
++ ) {
++ // If we're building a test harness, then warning about
++ // documentation is probably not really relevant right now.
++ if cx.sess().opts.test {
++ return;
++ }
++
++ // `#[doc(hidden)]` disables missing_docs check.
++ if self.doc_hidden() {
++ return;
++ }
++
++ if sp.from_expansion() {
++ return;
++ }
++
++ let has_doc = attrs
++ .iter()
++ .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta()));
++ if !has_doc {
++ span_lint(
++ cx,
++ MISSING_DOCS_IN_PRIVATE_ITEMS,
++ sp,
++ &format!("missing documentation for {}", desc),
++ );
++ }
++ }
++}
++
++impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
++ fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
++ let doc_hidden = self.doc_hidden()
++ || attrs.iter().any(|attr| {
++ attr.check_name(sym!(doc))
++ && match attr.meta_item_list() {
++ None => false,
++ Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
++ }
++ });
++ self.doc_hidden_stack.push(doc_hidden);
++ }
++
++ fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
++ self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
++ }
++
++ fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
++ self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate");
++ }
++
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
++ let desc = match it.kind {
++ hir::ItemKind::Const(..) => "a constant",
++ hir::ItemKind::Enum(..) => "an enum",
++ hir::ItemKind::Fn(..) => {
++ // ignore main()
++ if it.ident.name == sym!(main) {
++ let def_id = it.hir_id.owner;
++ let def_key = cx.tcx.hir().def_key(def_id);
++ if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
++ return;
++ }
++ }
++ "a function"
++ },
++ hir::ItemKind::Mod(..) => "a module",
++ hir::ItemKind::Static(..) => "a static",
++ hir::ItemKind::Struct(..) => "a struct",
++ hir::ItemKind::Trait(..) => "a trait",
++ hir::ItemKind::TraitAlias(..) => "a trait alias",
++ hir::ItemKind::TyAlias(..) => "a type alias",
++ hir::ItemKind::Union(..) => "a union",
++ hir::ItemKind::OpaqueTy(..) => "an existential type",
++ hir::ItemKind::ExternCrate(..)
++ | hir::ItemKind::ForeignMod(..)
++ | hir::ItemKind::GlobalAsm(..)
++ | hir::ItemKind::Impl { .. }
++ | hir::ItemKind::Use(..) => return,
++ };
++
++ self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
++ let desc = match trait_item.kind {
++ hir::TraitItemKind::Const(..) => "an associated constant",
++ hir::TraitItemKind::Fn(..) => "a trait method",
++ hir::TraitItemKind::Type(..) => "an associated type",
++ };
++
++ self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
++ // If the method is an impl for a trait, don't doc.
++ let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++ match cx.tcx.associated_item(def_id).container {
++ ty::TraitContainer(_) => return,
++ ty::ImplContainer(cid) => {
++ if cx.tcx.impl_trait_ref(cid).is_some() {
++ return;
++ }
++ },
++ }
++
++ let desc = match impl_item.kind {
++ hir::ImplItemKind::Const(..) => "an associated constant",
++ hir::ImplItemKind::Fn(..) => "a method",
++ hir::ImplItemKind::TyAlias(_) => "an associated type",
++ hir::ImplItemKind::OpaqueTy(_) => "an existential type",
++ };
++ self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
++ }
++
++ fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField<'_>) {
++ if !sf.is_positional() {
++ self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
++ }
++ }
++
++ fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant<'_>) {
++ self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_ast::ast;
++use rustc_hir as hir;
++use rustc_lint::{self, LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** it lints if an exported function, method, trait method with default impl,
++ /// or trait method impl is not `#[inline]`.
++ ///
++ /// **Why is this bad?** In general, it is not. Functions can be inlined across
++ /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
++ /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
++ /// might intend for most of the methods in their public API to be able to be inlined across
++ /// crates even when LTO is disabled. For these types of crates, enabling this lint might make
++ /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
++ /// then opt out for specific methods where this might not make sense.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// pub fn foo() {} // missing #[inline]
++ /// fn ok() {} // ok
++ /// #[inline] pub fn bar() {} // ok
++ /// #[inline(always)] pub fn baz() {} // ok
++ ///
++ /// pub trait Bar {
++ /// fn bar(); // ok
++ /// fn def_bar() {} // missing #[inline]
++ /// }
++ ///
++ /// struct Baz;
++ /// impl Baz {
++ /// fn private() {} // ok
++ /// }
++ ///
++ /// impl Bar for Baz {
++ /// fn bar() {} // ok - Baz is not exported
++ /// }
++ ///
++ /// pub struct PubBaz;
++ /// impl PubBaz {
++ /// fn private() {} // ok
++ /// pub fn not_ptrivate() {} // missing #[inline]
++ /// }
++ ///
++ /// impl Bar for PubBaz {
++ /// fn bar() {} // missing #[inline]
++ /// fn def_bar() {} // missing #[inline]
++ /// }
++ /// ```
++ pub MISSING_INLINE_IN_PUBLIC_ITEMS,
++ restriction,
++ "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
++}
++
++fn check_missing_inline_attrs(cx: &LateContext<'_, '_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
++ let has_inline = attrs.iter().any(|a| a.check_name(sym!(inline)));
++ if !has_inline {
++ span_lint(
++ cx,
++ MISSING_INLINE_IN_PUBLIC_ITEMS,
++ sp,
++ &format!("missing `#[inline]` for {}", desc),
++ );
++ }
++}
++
++fn is_executable(cx: &LateContext<'_, '_>) -> bool {
++ use rustc_session::config::CrateType;
++
++ cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| match t {
++ CrateType::Executable => true,
++ _ => false,
++ })
++}
++
++declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
++ if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable(cx) {
++ return;
++ }
++
++ if !cx.access_levels.is_exported(it.hir_id) {
++ return;
++ }
++ match it.kind {
++ hir::ItemKind::Fn(..) => {
++ let desc = "a function";
++ check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
++ },
++ hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _bounds, trait_items) => {
++ // note: we need to check if the trait is exported so we can't use
++ // `LateLintPass::check_trait_item` here.
++ for tit in trait_items {
++ let tit_ = cx.tcx.hir().trait_item(tit.id);
++ match tit_.kind {
++ hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
++ hir::TraitItemKind::Fn(..) => {
++ if tit.defaultness.has_value() {
++ // trait method with default body needs inline in case
++ // an impl is not provided
++ let desc = "a default trait method";
++ let item = cx.tcx.hir().expect_trait_item(tit.id.hir_id);
++ check_missing_inline_attrs(cx, &item.attrs, item.span, desc);
++ }
++ },
++ }
++ }
++ },
++ hir::ItemKind::Const(..)
++ | hir::ItemKind::Enum(..)
++ | hir::ItemKind::Mod(..)
++ | hir::ItemKind::Static(..)
++ | hir::ItemKind::Struct(..)
++ | hir::ItemKind::TraitAlias(..)
++ | hir::ItemKind::GlobalAsm(..)
++ | hir::ItemKind::TyAlias(..)
++ | hir::ItemKind::Union(..)
++ | hir::ItemKind::OpaqueTy(..)
++ | hir::ItemKind::ExternCrate(..)
++ | hir::ItemKind::ForeignMod(..)
++ | hir::ItemKind::Impl { .. }
++ | hir::ItemKind::Use(..) => {},
++ };
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
++ use rustc_middle::ty::{ImplContainer, TraitContainer};
++ if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable(cx) {
++ return;
++ }
++
++ // If the item being implemented is not exported, then we don't need #[inline]
++ if !cx.access_levels.is_exported(impl_item.hir_id) {
++ return;
++ }
++
++ let desc = match impl_item.kind {
++ hir::ImplItemKind::Fn(..) => "a method",
++ hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => return,
++ };
++
++ let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++ let trait_def_id = match cx.tcx.associated_item(def_id).container {
++ TraitContainer(cid) => Some(cid),
++ ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
++ };
++
++ if let Some(trait_def_id) = trait_def_id {
++ if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.hir_id) {
++ // If a trait is being implemented for an item, and the
++ // trait is not exported, we don't need #[inline]
++ return;
++ }
++ }
++
++ check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, Constant};
++use crate::utils::{sext, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt::Display;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for modulo arithemtic.
++ ///
++ /// **Why is this bad?** The results of modulo (%) operation might differ
++ /// depending on the language, when negative numbers are involved.
++ /// If you interop with different languages it might be beneficial
++ /// to double check all places that use modulo arithmetic.
++ ///
++ /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = -17 % 3;
++ /// ```
++ pub MODULO_ARITHMETIC,
++ restriction,
++ "any modulo arithmetic statement"
++}
++
++declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
++
++struct OperandInfo {
++ string_representation: Option<String>,
++ is_negative: bool,
++ is_integral: bool,
++}
++
++fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<OperandInfo> {
++ match constant(cx, cx.tables, operand) {
++ Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind {
++ ty::Int(ity) => {
++ let value = sext(cx.tcx, v, ity);
++ return Some(OperandInfo {
++ string_representation: Some(value.to_string()),
++ is_negative: value < 0,
++ is_integral: true,
++ });
++ },
++ ty::Uint(_) => {
++ return Some(OperandInfo {
++ string_representation: None,
++ is_negative: false,
++ is_integral: true,
++ });
++ },
++ _ => {},
++ },
++ Some((Constant::F32(f), _)) => {
++ return Some(floating_point_operand_info(&f));
++ },
++ Some((Constant::F64(f), _)) => {
++ return Some(floating_point_operand_info(&f));
++ },
++ _ => {},
++ }
++ None
++}
++
++fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo {
++ OperandInfo {
++ string_representation: Some(format!("{:.3}", *f)),
++ is_negative: *f < 0.0.into(),
++ is_integral: false,
++ }
++}
++
++fn might_have_negative_value(t: &ty::TyS<'_>) -> bool {
++ t.is_signed() || t.is_floating_point()
++}
++
++fn check_const_operands<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++ lhs_operand: &OperandInfo,
++ rhs_operand: &OperandInfo,
++) {
++ if lhs_operand.is_negative ^ rhs_operand.is_negative {
++ span_lint_and_then(
++ cx,
++ MODULO_ARITHMETIC,
++ expr.span,
++ &format!(
++ "you are using modulo operator on constants with different signs: `{} % {}`",
++ lhs_operand.string_representation.as_ref().unwrap(),
++ rhs_operand.string_representation.as_ref().unwrap()
++ ),
++ |diag| {
++ diag.note("double check for expected result especially when interoperating with different languages");
++ if lhs_operand.is_integral {
++ diag.note("or consider using `rem_euclid` or similar function");
++ }
++ },
++ );
++ }
++}
++
++fn check_non_const_operands<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, operand: &Expr<'_>) {
++ let operand_type = cx.tables.expr_ty(operand);
++ if might_have_negative_value(operand_type) {
++ span_lint_and_then(
++ cx,
++ MODULO_ARITHMETIC,
++ expr.span,
++ "you are using modulo operator on types that might have different signs",
++ |diag| {
++ diag.note("double check for expected result especially when interoperating with different languages");
++ if operand_type.is_integral() {
++ diag.note("or consider using `rem_euclid` or similar function");
++ }
++ },
++ );
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ModuloArithmetic {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ match &expr.kind {
++ ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
++ if let BinOpKind::Rem = op.node {
++ let lhs_operand = analyze_operand(lhs, cx, expr);
++ let rhs_operand = analyze_operand(rhs, cx, expr);
++ if_chain! {
++ if let Some(lhs_operand) = lhs_operand;
++ if let Some(rhs_operand) = rhs_operand;
++ then {
++ check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
++ }
++ else {
++ check_non_const_operands(cx, expr, lhs);
++ }
++ }
++ };
++ },
++ _ => {},
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! lint on multiple versions of a crate being used
++
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{Crate, CRATE_HIR_ID};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++use itertools::Itertools;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks to see if multiple versions of a crate are being
++ /// used.
++ ///
++ /// **Why is this bad?** This bloats the size of targets, and can lead to
++ /// confusing error messages when structs or traits are used interchangeably
++ /// between different versions of a crate.
++ ///
++ /// **Known problems:** Because this can be caused purely by the dependencies
++ /// themselves, it's not always possible to fix this issue.
++ ///
++ /// **Example:**
++ /// ```toml
++ /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
++ /// [dependencies]
++ /// ctrlc = "=3.1.0"
++ /// ansi_term = "=0.11.0"
++ /// ```
++ pub MULTIPLE_CRATE_VERSIONS,
++ cargo,
++ "multiple versions of the same crate being used"
++}
++
++declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]);
++
++impl LateLintPass<'_, '_> for MultipleCrateVersions {
++ fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++ if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
++ return;
++ }
++
++ let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() {
++ metadata
++ } else {
++ span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
++
++ return;
++ };
++
++ let mut packages = metadata.packages;
++ packages.sort_by(|a, b| a.name.cmp(&b.name));
++
++ for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) {
++ let group: Vec<cargo_metadata::Package> = group.collect();
++
++ if group.len() > 1 {
++ let versions = group.into_iter().map(|p| p.version).join(", ");
++
++ span_lint(
++ cx,
++ MULTIPLE_CRATE_VERSIONS,
++ DUMMY_SP,
++ &format!("multiple versions for dependency `{}`: {}", name, versions),
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty};
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++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;
++
++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:** We don't currently account for `Rc` or `Arc`, so
++ /// this may yield false positives.
++ ///
++ /// **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,
++ correctness,
++ "Check for mutable `Map`/`Set` key type"
++}
++
++declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType {
++ fn check_item(&mut self, cx: &LateContext<'a, '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<'a, '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<'a, '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.tables.pat_ty(&*local.pat));
++ }
++}
++
++fn check_sig<'a, 'tcx>(cx: &LateContext<'a, '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 decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) {
++ 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<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) {
++ let ty = walk_ptrs_ty(ty);
++ 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<'a, 'tcx>(cx: &LateContext<'a, '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.is_freeze(cx.tcx, cx.param_env, span),
++ _ => false,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{higher, span_lint};
++use rustc_hir as hir;
++use rustc_hir::intravisit;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for instances of `mut mut` references.
++ ///
++ /// **Why is this bad?** Multiple `mut`s don't add anything meaningful to the
++ /// source. This is either a copy'n'paste error, or it shows a fundamental
++ /// misunderstanding of references.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let mut y = 1;
++ /// let x = &mut &mut y;
++ /// ```
++ pub MUT_MUT,
++ pedantic,
++ "usage of double-mut refs, e.g., `&mut &mut ...`"
++}
++
++declare_lint_pass!(MutMut => [MUT_MUT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutMut {
++ fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) {
++ intravisit::walk_block(&mut MutVisitor { cx }, block);
++ }
++
++ fn check_ty(&mut self, cx: &LateContext<'a, 'tcx>, ty: &'tcx hir::Ty<'_>) {
++ use rustc_hir::intravisit::Visitor;
++
++ MutVisitor { cx }.visit_ty(ty);
++ }
++}
++
++pub struct MutVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++ if in_external_macro(self.cx.sess(), expr.span) {
++ return;
++ }
++
++ if let Some((_, arg, body)) = higher::for_loop(expr) {
++ // A `for` loop lowers to:
++ // ```rust
++ // match ::std::iter::Iterator::next(&mut iter) {
++ // // ^^^^
++ // ```
++ // Let's ignore the generated code.
++ intravisit::walk_expr(self, arg);
++ intravisit::walk_expr(self, body);
++ } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind {
++ if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
++ span_lint(
++ self.cx,
++ MUT_MUT,
++ expr.span,
++ "generally you want to avoid `&mut &mut _` if possible",
++ );
++ } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.tables.expr_ty(e).kind {
++ span_lint(
++ self.cx,
++ MUT_MUT,
++ expr.span,
++ "this expression mutably borrows a mutable reference. Consider reborrowing",
++ );
++ }
++ }
++ }
++
++ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
++ if let hir::TyKind::Rptr(
++ _,
++ hir::MutTy {
++ ty: ref pty,
++ mutbl: hir::Mutability::Mut,
++ },
++ ) = ty.kind
++ {
++ if let hir::TyKind::Rptr(
++ _,
++ hir::MutTy {
++ mutbl: hir::Mutability::Mut,
++ ..
++ },
++ ) = pty.kind
++ {
++ span_lint(
++ self.cx,
++ MUT_MUT,
++ ty.span,
++ "generally you want to avoid `&mut &mut _` if possible",
++ );
++ }
++ }
++
++ intravisit::walk_ty(self, ty);
++ }
++ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++ intravisit::NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::subst::Subst;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Detects giving a mutable reference to a function that only
++ /// requires an immutable reference.
++ ///
++ /// **Why is this bad?** The immutable reference rules out all other references
++ /// to the value. Also the code misleads about the intent of the call site.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// my_vec.push(&mut value)
++ /// ```
++ pub UNNECESSARY_MUT_PASSED,
++ style,
++ "an argument passed as a mutable reference although the callee only demands an immutable reference"
++}
++
++declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ match e.kind {
++ ExprKind::Call(ref fn_expr, ref arguments) => {
++ if let ExprKind::Path(ref path) = fn_expr.kind {
++ check_arguments(
++ cx,
++ arguments,
++ cx.tables.expr_ty(fn_expr),
++ &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
++ );
++ }
++ },
++ ExprKind::MethodCall(ref path, _, ref arguments) => {
++ let def_id = cx.tables.type_dependent_def_id(e.hir_id).unwrap();
++ let substs = cx.tables.node_substs(e.hir_id);
++ let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
++ check_arguments(cx, arguments, method_type, &path.ident.as_str())
++ },
++ _ => (),
++ }
++ }
++}
++
++fn check_arguments<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ arguments: &[Expr<'_>],
++ type_definition: Ty<'tcx>,
++ name: &str,
++) {
++ match type_definition.kind {
++ ty::FnDef(..) | ty::FnPtr(_) => {
++ let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
++ for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
++ match parameter.kind {
++ ty::Ref(_, _, Mutability::Not)
++ | ty::RawPtr(ty::TypeAndMut {
++ mutbl: Mutability::Not, ..
++ }) => {
++ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind {
++ span_lint(
++ cx,
++ UNNECESSARY_MUT_PASSED,
++ argument.span,
++ &format!("The function/method `{}` doesn't need a mutable reference", name),
++ );
++ }
++ },
++ _ => (),
++ }
++ }
++ },
++ _ => (),
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_direct_expn_of, span_lint};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for function/method calls with a mutable
++ /// parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.
++ ///
++ /// **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the
++ /// compiler.
++ /// Therefore mutating something in a `debug_assert!` macro results in different behaviour
++ /// between a release and debug build.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// debug_assert_eq!(vec![3].pop(), Some(3));
++ /// // or
++ /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
++ /// debug_assert!(take_a_mut_parameter(&mut 5));
++ /// ```
++ pub DEBUG_ASSERT_WITH_MUT_CALL,
++ nursery,
++ "mutable arguments in `debug_assert{,_ne,_eq}!`"
++}
++
++declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
++
++const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"];
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DebugAssertWithMutCall {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ for dmn in &DEBUG_MACRO_NAMES {
++ if is_direct_expn_of(e.span, dmn).is_some() {
++ if let Some(span) = extract_call(cx, e) {
++ span_lint(
++ cx,
++ DEBUG_ASSERT_WITH_MUT_CALL,
++ span,
++ &format!("do not call a function with mutable arguments inside of `{}!`", dmn),
++ );
++ }
++ }
++ }
++ }
++}
++
++//HACK(hellow554): remove this when #4694 is implemented
++fn extract_call<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option<Span> {
++ if_chain! {
++ if let ExprKind::Block(ref block, _) = e.kind;
++ if block.stmts.len() == 1;
++ if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind;
++ then {
++ // debug_assert
++ if_chain! {
++ if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind;
++ if let ExprKind::DropTemps(ref droptmp) = ifclause.kind;
++ if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind;
++ then {
++ let mut visitor = MutArgVisitor::new(cx);
++ visitor.visit_expr(condition);
++ return visitor.expr_span();
++ }
++ }
++
++ // debug_assert_{eq,ne}
++ if_chain! {
++ if let ExprKind::Block(ref matchblock, _) = matchexpr.kind;
++ if let Some(ref matchheader) = matchblock.expr;
++ if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind;
++ if let ExprKind::Tup(ref conditions) = headerexpr.kind;
++ if conditions.len() == 2;
++ then {
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind {
++ let mut visitor = MutArgVisitor::new(cx);
++ visitor.visit_expr(lhs);
++ if let Some(span) = visitor.expr_span() {
++ return Some(span);
++ }
++ }
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind {
++ let mut visitor = MutArgVisitor::new(cx);
++ visitor.visit_expr(rhs);
++ if let Some(span) = visitor.expr_span() {
++ return Some(span);
++ }
++ }
++ }
++ }
++ }
++ }
++
++ None
++}
++
++struct MutArgVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ expr_span: Option<Span>,
++ found: bool,
++}
++
++impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> {
++ fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++ Self {
++ cx,
++ expr_span: None,
++ found: false,
++ }
++ }
++
++ fn expr_span(&self) -> Option<Span> {
++ if self.found {
++ self.expr_span
++ } else {
++ None
++ }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ match expr.kind {
++ ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => {
++ self.found = true;
++ return;
++ },
++ ExprKind::Path(_) => {
++ if let Some(adj) = self.cx.tables.adjustments().get(expr.hir_id) {
++ if adj
++ .iter()
++ .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut)))
++ {
++ self.found = true;
++ return;
++ }
++ }
++ },
++ // Don't check await desugars
++ ExprKind::Match(_, _, MatchSource::AwaitDesugar) => return,
++ _ if !self.found => self.expr_span = Some(expr.span),
++ _ => return,
++ }
++ walk_expr(self, expr)
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for uses of mutex where an atomic value could be used
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{is_type_diagnostic_item, span_lint};
++use rustc_ast::ast;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usages of `Mutex<X>` where an atomic will do.
++ ///
++ /// **Why is this bad?** Using a mutex just to make access to a plain bool or
++ /// reference sequential is shooting flies with cannons.
++ /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
++ /// faster.
++ ///
++ /// **Known problems:** This lint cannot detect if the mutex is actually used
++ /// for waiting before a critical section.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::sync::Mutex;
++ /// # let y = 1;
++ /// let x = Mutex::new(&y);
++ /// ```
++ pub MUTEX_ATOMIC,
++ perf,
++ "using a mutex where an atomic value could be used instead"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral
++ /// type.
++ ///
++ /// **Why is this bad?** Using a mutex just to make access to a plain integer
++ /// sequential is
++ /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
++ ///
++ /// **Known problems:** This lint cannot detect if the mutex is actually used
++ /// for waiting before a critical section.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::sync::Mutex;
++ /// let x = Mutex::new(0usize);
++ /// ```
++ pub MUTEX_INTEGER,
++ nursery,
++ "using a mutex for an integer type"
++}
++
++declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Mutex {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ let ty = cx.tables.expr_ty(expr);
++ if let ty::Adt(_, subst) = ty.kind {
++ if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) {
++ let mutex_param = subst.type_at(0);
++ if let Some(atomic_name) = get_atomic_name(mutex_param) {
++ let msg = format!(
++ "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \
++ behavior and not the internal type, consider using `Mutex<()>`.",
++ atomic_name
++ );
++ match mutex_param.kind {
++ ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
++ ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
++ _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg),
++ };
++ }
++ }
++ }
++ }
++}
++
++fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
++ match ty.kind {
++ ty::Bool => Some("AtomicBool"),
++ ty::Uint(_) => Some("AtomicUsize"),
++ ty::Int(_) => Some("AtomicIsize"),
++ ty::RawPtr(_) => Some("AtomicPtr"),
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for needless boolean results of if-else expressions
++//!
++//! This lint is **warn** by default
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for expressions of the form `if c { true } else {
++ /// false }`
++ /// (or vice versa) and suggest using the condition directly.
++ ///
++ /// **Why is this bad?** Redundant code.
++ ///
++ /// **Known problems:** Maybe false positives: Sometimes, the two branches are
++ /// painstakingly documented (which we, of course, do not detect), so they *may*
++ /// have some value. Even then, the documentation can be rewritten to match the
++ /// shorter code.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// if x {
++ /// false
++ /// } else {
++ /// true
++ /// }
++ /// ```
++ /// Could be written as
++ /// ```rust,ignore
++ /// !x
++ /// ```
++ pub NEEDLESS_BOOL,
++ complexity,
++ "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for expressions of the form `x == true`,
++ /// `x != true` and order comparisons such as `x < true` (or vice versa) and
++ /// suggest using the variable directly.
++ ///
++ /// **Why is this bad?** Unnecessary code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// if x == true {}
++ /// if y == false {}
++ /// ```
++ /// use `x` directly:
++ /// ```rust,ignore
++ /// if x {}
++ /// if !y {}
++ /// ```
++ pub BOOL_COMPARISON,
++ complexity,
++ "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
++}
++
++declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ use self::Expression::{Bool, RetBool};
++ if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) {
++ let reduce = |ret, not| {
++ let mut applicability = Applicability::MachineApplicable;
++ let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
++ let mut snip = if not { !snip } else { snip };
++
++ if ret {
++ snip = snip.make_return();
++ }
++
++ if parent_node_is_if_expr(&e, &cx) {
++ snip = snip.blockify()
++ }
++
++ span_lint_and_sugg(
++ cx,
++ NEEDLESS_BOOL,
++ e.span,
++ "this if-then-else expression returns a bool literal",
++ "you can reduce it to",
++ snip.to_string(),
++ applicability,
++ );
++ };
++ if let ExprKind::Block(ref then_block, _) = then_block.kind {
++ match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
++ (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
++ span_lint(
++ cx,
++ NEEDLESS_BOOL,
++ e.span,
++ "this if-then-else expression will always return true",
++ );
++ },
++ (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
++ span_lint(
++ cx,
++ NEEDLESS_BOOL,
++ e.span,
++ "this if-then-else expression will always return false",
++ );
++ },
++ (RetBool(true), RetBool(false)) => reduce(true, false),
++ (Bool(true), Bool(false)) => reduce(false, false),
++ (RetBool(false), RetBool(true)) => reduce(true, true),
++ (Bool(false), Bool(true)) => reduce(false, true),
++ _ => (),
++ }
++ } else {
++ panic!("IfExpr `then` node is not an `ExprKind::Block`");
++ }
++ }
++ }
++}
++
++declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if e.span.from_expansion() {
++ return;
++ }
++
++ if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
++ let ignore_case = None::<(fn(_) -> _, &str)>;
++ let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
++ match node {
++ BinOpKind::Eq => {
++ let true_case = Some((|h| h, "equality checks against true are unnecessary"));
++ let false_case = Some((
++ |h: Sugg<'_>| !h,
++ "equality checks against false can be replaced by a negation",
++ ));
++ check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
++ },
++ BinOpKind::Ne => {
++ let true_case = Some((
++ |h: Sugg<'_>| !h,
++ "inequality checks against true can be replaced by a negation",
++ ));
++ let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
++ check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
++ },
++ BinOpKind::Lt => check_comparison(
++ cx,
++ e,
++ ignore_case,
++ Some((|h| h, "greater than checks against false are unnecessary")),
++ Some((
++ |h: Sugg<'_>| !h,
++ "less than comparison against true can be replaced by a negation",
++ )),
++ ignore_case,
++ Some((
++ |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
++ "order comparisons between booleans can be simplified",
++ )),
++ ),
++ BinOpKind::Gt => check_comparison(
++ cx,
++ e,
++ Some((
++ |h: Sugg<'_>| !h,
++ "less than comparison against true can be replaced by a negation",
++ )),
++ ignore_case,
++ ignore_case,
++ Some((|h| h, "greater than checks against false are unnecessary")),
++ Some((
++ |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
++ "order comparisons between booleans can be simplified",
++ )),
++ ),
++ _ => (),
++ }
++ }
++ }
++}
++
++struct ExpressionInfoWithSpan {
++ one_side_is_unary_not: bool,
++ left_span: Span,
++ right_span: Span,
++}
++
++fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
++ if_chain! {
++ if let ExprKind::Unary(unop, operand) = e.kind;
++ if let UnOp::UnNot = unop;
++ then {
++ return (true, operand.span);
++ }
++ };
++ (false, e.span)
++}
++
++fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
++ let left = is_unary_not(left_side);
++ let right = is_unary_not(right_side);
++
++ ExpressionInfoWithSpan {
++ one_side_is_unary_not: left.0 != right.0,
++ left_span: left.1,
++ right_span: right.1,
++ }
++}
++
++fn check_comparison<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ e: &'tcx Expr<'_>,
++ left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++ left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++ right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++ right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++ no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
++) {
++ use self::Expression::{Bool, Other};
++
++ if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind {
++ let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
++ if l_ty.is_bool() && r_ty.is_bool() {
++ let mut applicability = Applicability::MachineApplicable;
++
++ if let BinOpKind::Eq = op.node {
++ let expression_info = one_side_is_unary_not(&left_side, &right_side);
++ if expression_info.one_side_is_unary_not {
++ span_lint_and_sugg(
++ cx,
++ BOOL_COMPARISON,
++ e.span,
++ "This comparison might be written more concisely",
++ "try simplifying it as shown",
++ format!(
++ "{} != {}",
++ snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
++ snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
++ ),
++ applicability,
++ )
++ }
++ }
++
++ match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
++ (Bool(true), Other) => left_true.map_or((), |(h, m)| {
++ suggest_bool_comparison(cx, e, right_side, applicability, m, h)
++ }),
++ (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
++ suggest_bool_comparison(cx, e, left_side, applicability, m, h)
++ }),
++ (Bool(false), Other) => left_false.map_or((), |(h, m)| {
++ suggest_bool_comparison(cx, e, right_side, applicability, m, h)
++ }),
++ (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
++ suggest_bool_comparison(cx, e, left_side, applicability, m, h)
++ }),
++ (Other, Other) => no_literal.map_or((), |(h, m)| {
++ let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
++ let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
++ span_lint_and_sugg(
++ cx,
++ BOOL_COMPARISON,
++ e.span,
++ m,
++ "try simplifying it as shown",
++ h(left_side, right_side).to_string(),
++ applicability,
++ )
++ }),
++ _ => (),
++ }
++ }
++ }
++}
++
++fn suggest_bool_comparison<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ e: &'tcx Expr<'_>,
++ expr: &Expr<'_>,
++ mut applicability: Applicability,
++ message: &str,
++ conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
++) {
++ let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
++ span_lint_and_sugg(
++ cx,
++ BOOL_COMPARISON,
++ e.span,
++ message,
++ "try simplifying it as shown",
++ conv_hint(hint).to_string(),
++ applicability,
++ );
++}
++
++enum Expression {
++ Bool(bool),
++ RetBool(bool),
++ Other,
++}
++
++fn fetch_bool_block(block: &Block<'_>) -> Expression {
++ match (&*block.stmts, block.expr.as_ref()) {
++ (&[], Some(e)) => fetch_bool_expr(&**e),
++ (&[ref e], None) => {
++ if let StmtKind::Semi(ref e) = e.kind {
++ if let ExprKind::Ret(_) = e.kind {
++ fetch_bool_expr(&**e)
++ } else {
++ Expression::Other
++ }
++ } else {
++ Expression::Other
++ }
++ },
++ _ => Expression::Other,
++ }
++}
++
++fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
++ match expr.kind {
++ ExprKind::Block(ref block, _) => fetch_bool_block(block),
++ ExprKind::Lit(ref lit_ptr) => {
++ if let LitKind::Bool(value) = lit_ptr.node {
++ Expression::Bool(value)
++ } else {
++ Expression::Other
++ }
++ },
++ ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
++ Expression::Bool(value) => Expression::RetBool(value),
++ _ => Expression::Other,
++ },
++ _ => Expression::Other,
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for needless address of operations (`&`)
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{snippet_opt, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_middle::ty::adjustment::{Adjust, Adjustment};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for address of operations (`&`) that are going to
++ /// be dereferenced immediately by the compiler.
++ ///
++ /// **Why is this bad?** Suggests that the receiver of the expression borrows
++ /// the expression.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x: &i32 = &&&&&&5;
++ /// ```
++ ///
++ /// **Known problems:** None.
++ pub NEEDLESS_BORROW,
++ nursery,
++ "taking a reference that is going to be automatically dereferenced"
++}
++
++#[derive(Default)]
++pub struct NeedlessBorrow {
++ derived_item: Option<HirId>,
++}
++
++impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if e.span.from_expansion() || self.derived_item.is_some() {
++ return;
++ }
++ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
++ if let ty::Ref(..) = cx.tables.expr_ty(inner).kind {
++ for adj3 in cx.tables.expr_adjustments(e).windows(3) {
++ if let [Adjustment {
++ kind: Adjust::Deref(_), ..
++ }, Adjustment {
++ kind: Adjust::Deref(_), ..
++ }, Adjustment {
++ kind: Adjust::Borrow(_),
++ ..
++ }] = *adj3
++ {
++ span_lint_and_then(
++ cx,
++ NEEDLESS_BORROW,
++ e.span,
++ "this expression borrows a reference that is immediately dereferenced \
++ by the compiler",
++ |diag| {
++ if let Some(snippet) = snippet_opt(cx, inner.span) {
++ diag.span_suggestion(
++ e.span,
++ "change this to",
++ snippet,
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ );
++ }
++ }
++ }
++ }
++ }
++ fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++ if pat.span.from_expansion() || self.derived_item.is_some() {
++ return;
++ }
++ if_chain! {
++ if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind;
++ if let ty::Ref(_, tam, mutbl) = cx.tables.pat_ty(pat).kind;
++ if mutbl == Mutability::Not;
++ if let ty::Ref(_, _, mutbl) = tam.kind;
++ // only lint immutable refs, because borrowed `&mut T` cannot be moved out
++ if mutbl == Mutability::Not;
++ then {
++ span_lint_and_then(
++ cx,
++ NEEDLESS_BORROW,
++ pat.span,
++ "this pattern creates a reference to a reference",
++ |diag| {
++ if let Some(snippet) = snippet_opt(cx, name.span) {
++ diag.span_suggestion(
++ pat.span,
++ "change this to",
++ snippet,
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ )
++ }
++ }
++ }
++
++ fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if item.attrs.iter().any(|a| a.check_name(sym!(automatically_derived))) {
++ debug_assert!(self.derived_item.is_none());
++ self.derived_item = Some(item.hir_id);
++ }
++ }
++
++ fn check_item_post(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let Some(id) = self.derived_item {
++ if item.hir_id == id {
++ self.derived_item = None;
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for useless borrowed references.
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{snippet_with_applicability, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for useless borrowed references.
++ ///
++ /// **Why is this bad?** It is mostly useless and make the code look more
++ /// complex than it
++ /// actually is.
++ ///
++ /// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
++ /// For instance in the following snippet:
++ /// ```rust,ignore
++ /// enum Animal {
++ /// Cat(u64),
++ /// Dog(u64),
++ /// }
++ ///
++ /// fn foo(a: &Animal, b: &Animal) {
++ /// match (a, b) {
++ /// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error
++ /// (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
++ /// }
++ /// }
++ /// ```
++ /// There is a lifetime mismatch error for `k` (indeed a and b have distinct
++ /// lifetime).
++ /// This can be fixed by using the `&ref` pattern.
++ /// However, the code can also be fixed by much cleaner ways
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let mut v = Vec::<String>::new();
++ /// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++ /// ```
++ /// This closure takes a reference on something that has been matched as a
++ /// reference and
++ /// de-referenced.
++ /// As such, it could just be |a| a.is_empty()
++ pub NEEDLESS_BORROWED_REFERENCE,
++ complexity,
++ "taking a needless borrowed reference"
++}
++
++declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrowedRef {
++ fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++ if pat.span.from_expansion() {
++ // OK, simple enough, lints doesn't check in macro.
++ return;
++ }
++
++ if_chain! {
++ // Only lint immutable refs, because `&mut ref T` may be useful.
++ if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind;
++
++ // Check sub_pat got a `ref` keyword (excluding `ref mut`).
++ if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind;
++ let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
++ if let Some(parent_node) = cx.tcx.hir().find(parent_id);
++ then {
++ // do not recurse within patterns, as they may have other references
++ // XXXManishearth we can relax this constraint if we only check patterns
++ // with a single ref pattern inside them
++ if let Node::Pat(_) = parent_node {
++ return;
++ }
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
++ "this pattern takes a reference on something that is being de-referenced",
++ |diag| {
++ let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
++ diag.span_suggestion(
++ pat.span,
++ "try removing the `&ref` part and just keep",
++ hint,
++ applicability,
++ );
++ });
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for continue statements in loops that are redundant.
++//!
++//! For example, the lint would catch
++//!
++//! ```rust
++//! let mut a = 1;
++//! let x = true;
++//!
++//! while a < 5 {
++//! a = 6;
++//! if x {
++//! // ...
++//! } else {
++//! continue;
++//! }
++//! println!("Hello, world");
++//! }
++//! ```
++//!
++//! And suggest something like this:
++//!
++//! ```rust
++//! let mut a = 1;
++//! let x = true;
++//!
++//! while a < 5 {
++//! a = 6;
++//! if x {
++//! // ...
++//! println!("Hello, world");
++//! }
++//! }
++//! ```
++//!
++//! This lint is **warn** by default.
++use rustc_ast::ast;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{original_sp, DUMMY_SP};
++use rustc_span::Span;
++
++use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};
++
++declare_clippy_lint! {
++ /// **What it does:** The lint checks for `if`-statements appearing in loops
++ /// that contain a `continue` statement in either their main blocks or their
++ /// `else`-blocks, when omitting the `else`-block possibly with some
++ /// rearrangement of code can make the code easier to understand.
++ ///
++ /// **Why is this bad?** Having explicit `else` blocks for `if` statements
++ /// containing `continue` in their THEN branch adds unnecessary branching and
++ /// nesting to the code. Having an else block containing just `continue` can
++ /// also be better written by grouping the statements following the whole `if`
++ /// statement within the THEN block and omitting the else block completely.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # fn condition() -> bool { false }
++ /// # fn update_condition() {}
++ /// # let x = false;
++ /// while condition() {
++ /// update_condition();
++ /// if x {
++ /// // ...
++ /// } else {
++ /// continue;
++ /// }
++ /// println!("Hello, world");
++ /// }
++ /// ```
++ ///
++ /// Could be rewritten as
++ ///
++ /// ```rust
++ /// # fn condition() -> bool { false }
++ /// # fn update_condition() {}
++ /// # let x = false;
++ /// while condition() {
++ /// update_condition();
++ /// if x {
++ /// // ...
++ /// println!("Hello, world");
++ /// }
++ /// }
++ /// ```
++ ///
++ /// As another example, the following code
++ ///
++ /// ```rust
++ /// # fn waiting() -> bool { false }
++ /// loop {
++ /// if waiting() {
++ /// continue;
++ /// } else {
++ /// // Do something useful
++ /// }
++ /// # break;
++ /// }
++ /// ```
++ /// Could be rewritten as
++ ///
++ /// ```rust
++ /// # fn waiting() -> bool { false }
++ /// loop {
++ /// if waiting() {
++ /// continue;
++ /// }
++ /// // Do something useful
++ /// # break;
++ /// }
++ /// ```
++ pub NEEDLESS_CONTINUE,
++ pedantic,
++ "`continue` statements that can be replaced by a rearrangement of code"
++}
++
++declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);
++
++impl EarlyLintPass for NeedlessContinue {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
++ if !expr.span.from_expansion() {
++ check_and_warn(cx, expr);
++ }
++ }
++}
++
++/* This lint has to mainly deal with two cases of needless continue
++ * statements. */
++// Case 1 [Continue inside else block]:
++//
++// loop {
++// // region A
++// if cond {
++// // region B
++// } else {
++// continue;
++// }
++// // region C
++// }
++//
++// This code can better be written as follows:
++//
++// loop {
++// // region A
++// if cond {
++// // region B
++// // region C
++// }
++// }
++//
++// Case 2 [Continue inside then block]:
++//
++// loop {
++// // region A
++// if cond {
++// continue;
++// // potentially more code here.
++// } else {
++// // region B
++// }
++// // region C
++// }
++//
++//
++// This snippet can be refactored to:
++//
++// loop {
++// // region A
++// if !cond {
++// // region B
++// // region C
++// }
++// }
++//
++
++/// Given an expression, returns true if either of the following is true
++///
++/// - The expression is a `continue` node.
++/// - The expression node is a block with the first statement being a
++/// `continue`.
++fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
++ match else_expr.kind {
++ ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
++ ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
++ _ => false,
++ }
++}
++
++fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
++ block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
++ ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
++ if let ast::ExprKind::Continue(ref l) = e.kind {
++ compare_labels(label, l.as_ref())
++ } else {
++ false
++ }
++ },
++ _ => false,
++ })
++}
++
++/// If the `continue` has a label, check it matches the label of the loop.
++fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
++ match (loop_label, continue_label) {
++ // `loop { continue; }` or `'a loop { continue; }`
++ (_, None) => true,
++ // `loop { continue 'a; }`
++ (None, _) => false,
++ // `'a loop { continue 'a; }` or `'a loop { continue 'b; }`
++ (Some(x), Some(y)) => x.ident == y.ident,
++ }
++}
++
++/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
++/// the AST object representing the loop block of `expr`.
++fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
++where
++ F: FnMut(&ast::Block, Option<&ast::Label>),
++{
++ if let ast::ExprKind::While(_, loop_block, label)
++ | ast::ExprKind::ForLoop(_, _, loop_block, label)
++ | ast::ExprKind::Loop(loop_block, label) = &expr.kind
++ {
++ func(loop_block, label.as_ref());
++ }
++}
++
++/// If `stmt` is an if expression node with an `else` branch, calls func with
++/// the
++/// following:
++///
++/// - The `if` expression itself,
++/// - The `if` condition expression,
++/// - The `then` block, and
++/// - The `else` expression.
++fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
++where
++ F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
++{
++ match stmt.kind {
++ ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
++ if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
++ func(e, cond, if_block, else_expr);
++ }
++ },
++ _ => {},
++ }
++}
++
++/// A type to distinguish between the two distinct cases this lint handles.
++#[derive(Copy, Clone, Debug)]
++enum LintType {
++ ContinueInsideElseBlock,
++ ContinueInsideThenBlock,
++}
++
++/// Data we pass around for construction of help messages.
++struct LintData<'a> {
++ /// The `if` expression encountered in the above loop.
++ if_expr: &'a ast::Expr,
++ /// The condition expression for the above `if`.
++ if_cond: &'a ast::Expr,
++ /// The `then` block of the `if` statement.
++ if_block: &'a ast::Block,
++ /// The `else` block of the `if` statement.
++ /// Note that we only work with `if` exprs that have an `else` branch.
++ else_expr: &'a ast::Expr,
++ /// The 0-based index of the `if` statement in the containing loop block.
++ stmt_idx: usize,
++ /// The statements of the loop block.
++ block_stmts: &'a [ast::Stmt],
++}
++
++const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";
++
++const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
++ expression";
++
++const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
++ follows (in the loop) with the `if` block";
++
++const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
++
++fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
++ // snip is the whole *help* message that appears after the warning.
++ // message is the warning message.
++ // expr is the expression which the lint warning message refers to.
++ let (snip, message, expr) = match typ {
++ LintType::ContinueInsideElseBlock => (
++ suggestion_snippet_for_continue_inside_else(cx, data),
++ MSG_REDUNDANT_ELSE_BLOCK,
++ data.else_expr,
++ ),
++ LintType::ContinueInsideThenBlock => (
++ suggestion_snippet_for_continue_inside_if(cx, data),
++ MSG_ELSE_BLOCK_NOT_NEEDED,
++ data.if_expr,
++ ),
++ };
++ span_lint_and_help(
++ cx,
++ NEEDLESS_CONTINUE,
++ expr.span,
++ message,
++ None,
++ &format!("{}\n{}", header, snip),
++ );
++}
++
++fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++ let cond_code = snippet(cx, data.if_cond.span, "..");
++
++ let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
++
++ let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span));
++
++ let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
++ format!(
++ "{indent}if {} {}\n{indent}{}",
++ cond_code,
++ continue_code,
++ else_code,
++ indent = " ".repeat(indent_if),
++ )
++}
++
++fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++ let cond_code = snippet(cx, data.if_cond.span, "..");
++
++ // Region B
++ let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)));
++
++ // Region C
++ // These is the code in the loop block that follows the if/else construction
++ // we are complaining about. We want to pull all of this code into the
++ // `then` block of the `if` statement.
++ let indent = span_of_first_expr_in_block(data.if_block)
++ .and_then(|span| indent_of(cx, span))
++ .unwrap_or(0);
++ let to_annex = data.block_stmts[data.stmt_idx + 1..]
++ .iter()
++ .map(|stmt| original_sp(stmt.span, DUMMY_SP))
++ .map(|span| {
++ let snip = snippet_block(cx, span, "..", None).into_owned();
++ snip.lines()
++ .map(|line| format!("{}{}", " ".repeat(indent), line))
++ .collect::<Vec<_>>()
++ .join("\n")
++ })
++ .collect::<Vec<_>>()
++ .join("\n");
++
++ let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
++ format!(
++ "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
++ cond_code,
++ block_code,
++ to_annex,
++ indent = " ".repeat(indent),
++ indent_if = " ".repeat(indent_if),
++ )
++}
++
++fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) {
++ with_loop_block(expr, |loop_block, label| {
++ for (i, stmt) in loop_block.stmts.iter().enumerate() {
++ with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
++ let data = &LintData {
++ stmt_idx: i,
++ if_expr,
++ if_cond: cond,
++ if_block: then_block,
++ else_expr,
++ block_stmts: &loop_block.stmts,
++ };
++ if needless_continue_in_else(else_expr, label) {
++ emit_warning(
++ cx,
++ data,
++ DROP_ELSE_BLOCK_AND_MERGE_MSG,
++ LintType::ContinueInsideElseBlock,
++ );
++ } else if is_first_block_stmt_continue(then_block, label) {
++ emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
++ }
++ });
++ }
++ });
++}
++
++/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating
++/// till a non-whitespace character is found. e.g., the string. If no closing `}` is present, the
++/// string will be preserved.
++///
++/// ```rust
++/// {
++/// let x = 5;
++/// }
++/// ```
++///
++/// is transformed to
++///
++/// ```ignore
++/// {
++/// let x = 5;
++/// ```
++#[must_use]
++fn erode_from_back(s: &str) -> String {
++ let mut ret = s.to_string();
++ while ret.pop().map_or(false, |c| c != '}') {}
++ while let Some(c) = ret.pop() {
++ if !c.is_whitespace() {
++ ret.push(c);
++ break;
++ }
++ }
++ if ret.is_empty() {
++ s.to_string()
++ } else {
++ ret
++ }
++}
++
++fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
++ block.stmts.iter().next().map(|stmt| stmt.span)
++}
++
++#[cfg(test)]
++mod test {
++ use super::erode_from_back;
++
++ #[test]
++ #[rustfmt::skip]
++ fn test_erode_from_back() {
++ let input = "\
++{
++ let x = 5;
++ let y = format!(\"{}\", 42);
++}";
++
++ let expected = "\
++{
++ let x = 5;
++ let y = format!(\"{}\", 42);";
++
++ let got = erode_from_back(input);
++ assert_eq!(expected, got);
++ }
++
++ #[test]
++ #[rustfmt::skip]
++ fn test_erode_from_back_no_brace() {
++ let input = "\
++let x = 5;
++let y = something();
++";
++ let expected = input;
++ let got = erode_from_back(input);
++ assert_eq!(expected, got);
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::ptr::get_spans;
++use crate::utils::{
++ get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
++ snippet_opt, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::Attribute;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, TypeFoldable};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++use rustc_target::spec::abi::Abi;
++use rustc_trait_selection::traits;
++use rustc_trait_selection::traits::misc::can_type_implement_copy;
++use rustc_typeck::expr_use_visitor as euv;
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for functions taking arguments by value, but not
++ /// consuming them in its
++ /// body.
++ ///
++ /// **Why is this bad?** Taking arguments by reference is more flexible and can
++ /// sometimes avoid
++ /// unnecessary allocations.
++ ///
++ /// **Known problems:**
++ /// * This lint suggests taking an argument by reference,
++ /// however sometimes it is better to let users decide the argument type
++ /// (by using `Borrow` trait, for example), depending on how the function is used.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(v: Vec<i32>) {
++ /// assert_eq!(v.len(), 42);
++ /// }
++ /// ```
++ ///
++ /// ```rust
++ /// // should be
++ /// fn foo(v: &[i32]) {
++ /// assert_eq!(v.len(), 42);
++ /// }
++ /// ```
++ pub NEEDLESS_PASS_BY_VALUE,
++ pedantic,
++ "functions taking arguments by value, but not consuming them in its body"
++}
++
++declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);
++
++macro_rules! need {
++ ($e: expr) => {
++ if let Some(x) = $e {
++ x
++ } else {
++ return;
++ }
++ };
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
++ #[allow(clippy::too_many_lines)]
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ span: Span,
++ hir_id: HirId,
++ ) {
++ if span.from_expansion() {
++ return;
++ }
++
++ match kind {
++ FnKind::ItemFn(.., header, _, attrs) => {
++ if header.abi != Abi::Rust || requires_exact_signature(attrs) {
++ return;
++ }
++ },
++ FnKind::Method(..) => (),
++ _ => return,
++ }
++
++ // Exclude non-inherent impls
++ if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
++ if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
++ ItemKind::Trait(..))
++ {
++ return;
++ }
++ }
++
++ // Allow `Borrow` or functions to be taken by value
++ let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
++ let whitelisted_traits = [
++ need!(cx.tcx.lang_items().fn_trait()),
++ need!(cx.tcx.lang_items().fn_once_trait()),
++ need!(cx.tcx.lang_items().fn_mut_trait()),
++ need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
++ ];
++
++ let sized_trait = need!(cx.tcx.lang_items().sized_trait());
++
++ let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++
++ let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.iter().copied())
++ .filter(|p| !p.is_global())
++ .filter_map(|obligation| {
++ if let ty::Predicate::Trait(poly_trait_ref, _) = obligation.predicate {
++ if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars()
++ {
++ return None;
++ }
++ Some(poly_trait_ref)
++ } else {
++ None
++ }
++ })
++ .collect::<Vec<_>>();
++
++ // Collect moved variables and spans which will need dereferencings from the
++ // function body.
++ let MovedVariablesCtxt {
++ moved_vars,
++ spans_need_deref,
++ ..
++ } = {
++ let mut ctx = MovedVariablesCtxt::default();
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
++ });
++ ctx
++ };
++
++ let fn_sig = cx.tcx.fn_sig(fn_def_id);
++ let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
++
++ for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
++ // All spans generated from a proc-macro invocation are the same...
++ if span == input.span {
++ return;
++ }
++
++ // Ignore `self`s.
++ if idx == 0 {
++ if let PatKind::Binding(.., ident, _) = arg.pat.kind {
++ if ident.as_str() == "self" {
++ continue;
++ }
++ }
++ }
++
++ //
++ // * Exclude a type that is specifically bounded by `Borrow`.
++ // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
++ // `serde::Serialize`)
++ let (implements_borrow_trait, all_borrowable_trait) = {
++ let preds = preds
++ .iter()
++ .filter(|t| t.skip_binder().self_ty() == ty)
++ .collect::<Vec<_>>();
++
++ (
++ preds.iter().any(|t| t.def_id() == borrow_trait),
++ !preds.is_empty() && {
++ let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
++ preds.iter().all(|t| {
++ let ty_params = &t
++ .skip_binder()
++ .trait_ref
++ .substs
++ .iter()
++ .skip(1)
++ .cloned()
++ .collect::<Vec<_>>();
++ implements_trait(cx, ty_empty_region, t.def_id(), ty_params)
++ })
++ },
++ )
++ };
++
++ if_chain! {
++ if !is_self(arg);
++ if !ty.is_mutable_ptr();
++ if !is_copy(cx, ty);
++ if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
++ if !implements_borrow_trait;
++ if !all_borrowable_trait;
++
++ if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
++ if !moved_vars.contains(&canonical_id);
++ then {
++ if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
++ continue;
++ }
++
++ // Dereference suggestion
++ let sugg = |diag: &mut DiagnosticBuilder<'_>| {
++ if let ty::Adt(def, ..) = ty.kind {
++ if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
++ if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
++ diag.span_help(span, "consider marking this type as `Copy`");
++ }
++ }
++ }
++
++ let deref_span = spans_need_deref.get(&canonical_id);
++ if_chain! {
++ if is_type_diagnostic_item(cx, ty, sym!(vec_type));
++ if let Some(clone_spans) =
++ get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
++ if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
++ if let Some(elem_ty) = path.segments.iter()
++ .find(|seg| seg.ident.name == sym!(Vec))
++ .and_then(|ps| ps.args.as_ref())
++ .map(|params| params.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ }).unwrap());
++ then {
++ let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
++ diag.span_suggestion(
++ input.span,
++ "consider changing the type to",
++ slice_ty,
++ Applicability::Unspecified,
++ );
++
++ for (span, suggestion) in clone_spans {
++ diag.span_suggestion(
++ span,
++ &snippet_opt(cx, span)
++ .map_or(
++ "change the call to".into(),
++ |x| Cow::from(format!("change `{}` to", x)),
++ ),
++ suggestion.into(),
++ Applicability::Unspecified,
++ );
++ }
++
++ // cannot be destructured, no need for `*` suggestion
++ assert!(deref_span.is_none());
++ return;
++ }
++ }
++
++ if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++ if let Some(clone_spans) =
++ get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
++ diag.span_suggestion(
++ input.span,
++ "consider changing the type to",
++ "&str".to_string(),
++ Applicability::Unspecified,
++ );
++
++ for (span, suggestion) in clone_spans {
++ diag.span_suggestion(
++ span,
++ &snippet_opt(cx, span)
++ .map_or(
++ "change the call to".into(),
++ |x| Cow::from(format!("change `{}` to", x))
++ ),
++ suggestion.into(),
++ Applicability::Unspecified,
++ );
++ }
++
++ assert!(deref_span.is_none());
++ return;
++ }
++ }
++
++ let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
++
++ // Suggests adding `*` to dereference the added reference.
++ if let Some(deref_span) = deref_span {
++ spans.extend(
++ deref_span
++ .iter()
++ .cloned()
++ .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
++ );
++ spans.sort_by_key(|&(span, _)| span);
++ }
++ multispan_sugg(diag, "consider taking a reference instead".to_string(), spans);
++ };
++
++ span_lint_and_then(
++ cx,
++ NEEDLESS_PASS_BY_VALUE,
++ input.span,
++ "this argument is passed by value, but not consumed in the function body",
++ sugg,
++ );
++ }
++ }
++ }
++ }
++}
++
++/// Functions marked with these attributes must have the exact signature.
++fn requires_exact_signature(attrs: &[Attribute]) -> bool {
++ attrs.iter().any(|attr| {
++ [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
++ .iter()
++ .any(|&allow| attr.check_name(allow))
++ })
++}
++
++#[derive(Default)]
++struct MovedVariablesCtxt {
++ moved_vars: FxHashSet<HirId>,
++ /// Spans which need to be prefixed with `*` for dereferencing the
++ /// suggested additional reference.
++ spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
++}
++
++impl MovedVariablesCtxt {
++ fn move_common(&mut self, cmt: &euv::Place<'_>) {
++ if let euv::PlaceBase::Local(vid) = cmt.base {
++ self.moved_vars.insert(vid);
++ }
++ }
++}
++
++impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
++ fn consume(&mut self, cmt: &euv::Place<'tcx>, mode: euv::ConsumeMode) {
++ if let euv::ConsumeMode::Move = mode {
++ self.move_common(cmt);
++ }
++ }
++
++ fn borrow(&mut self, _: &euv::Place<'tcx>, _: ty::BorrowKind) {}
++
++ fn mutate(&mut self, _: &euv::Place<'tcx>) {}
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for needlessly including a base struct on update
++ /// when all fields are changed anyway.
++ ///
++ /// **Why is this bad?** This will cost resources (because the base has to be
++ /// somewhere), and make the code less readable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # struct Point {
++ /// # x: i32,
++ /// # y: i32,
++ /// # z: i32,
++ /// # }
++ /// # let zero_point = Point { x: 0, y: 0, z: 0 };
++ /// Point {
++ /// x: 1,
++ /// y: 1,
++ /// ..zero_point
++ /// };
++ /// ```
++ pub NEEDLESS_UPDATE,
++ complexity,
++ "using `Foo { ..base }` when there are no missing fields"
++}
++
++declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessUpdate {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
++ let ty = cx.tables.expr_ty(expr);
++ if let ty::Adt(def, _) = ty.kind {
++ if fields.len() == def.non_enum_variant().fields.len() {
++ span_lint(
++ cx,
++ NEEDLESS_UPDATE,
++ base.span,
++ "struct update has no effect, all the fields in the struct have already been specified",
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{self, paths, span_lint};
++
++declare_clippy_lint! {
++ /// **What it does:**
++ /// Checks for the usage of negated comparison operators on types which only implement
++ /// `PartialOrd` (e.g., `f64`).
++ ///
++ /// **Why is this bad?**
++ /// These operators make it easy to forget that the underlying types actually allow not only three
++ /// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
++ /// especially easy to miss if the operator based comparison result is negated.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// use std::cmp::Ordering;
++ ///
++ /// // Bad
++ /// let a = 1.0;
++ /// let b = f64::NAN;
++ ///
++ /// let _not_less_or_equal = !(a <= b);
++ ///
++ /// // Good
++ /// let a = 1.0;
++ /// let b = f64::NAN;
++ ///
++ /// let _not_less_or_equal = match a.partial_cmp(&b) {
++ /// None | Some(Ordering::Greater) => true,
++ /// _ => false,
++ /// };
++ /// ```
++ pub NEG_CMP_OP_ON_PARTIAL_ORD,
++ complexity,
++ "The use of negated comparison operators on partially ordered types may produce confusing code."
++}
++
++declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoNegCompOpForPartialOrd {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++
++ if !in_external_macro(cx.sess(), expr.span);
++ if let ExprKind::Unary(UnOp::UnNot, ref inner) = expr.kind;
++ if let ExprKind::Binary(ref op, ref left, _) = inner.kind;
++ if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node;
++
++ then {
++
++ let ty = cx.tables.expr_ty(left);
++
++ let implements_ord = {
++ if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) {
++ utils::implements_trait(cx, ty, id, &[])
++ } else {
++ return;
++ }
++ };
++
++ let implements_partial_ord = {
++ if let Some(id) = cx.tcx.lang_items().partial_ord_trait() {
++ utils::implements_trait(cx, ty, id, &[])
++ } else {
++ return;
++ }
++ };
++
++ if implements_partial_ord && !implements_ord {
++ span_lint(
++ cx,
++ NEG_CMP_OP_ON_PARTIAL_ORD,
++ expr.span,
++ "The use of negated comparison operators on partially ordered \
++ types produces code that is hard to read and refactor. Please \
++ consider using the `partial_cmp` method instead, to make it \
++ clear that the two values could be incomparable."
++ )
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++use crate::consts::{self, Constant};
++use crate::utils::span_lint;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for multiplication by -1 as a form of negation.
++ ///
++ /// **Why is this bad?** It's more readable to just negate.
++ ///
++ /// **Known problems:** This only catches integers (for now).
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// x * -1
++ /// ```
++ pub NEG_MULTIPLY,
++ style,
++ "multiplying integers with `-1`"
++}
++
++declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
++
++#[allow(clippy::match_same_arms)]
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(ref op, ref left, ref right) = e.kind {
++ if BinOpKind::Mul == op.node {
++ match (&left.kind, &right.kind) {
++ (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
++ (&ExprKind::Unary(UnOp::UnNeg, ref lit), _) => check_mul(cx, e.span, lit, right),
++ (_, &ExprKind::Unary(UnOp::UnNeg, ref lit)) => check_mul(cx, e.span, lit, left),
++ _ => {},
++ }
++ }
++ }
++ }
++}
++
++fn check_mul(cx: &LateContext<'_, '_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Lit(ref l) = lit.kind;
++ if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.tables.expr_ty_opt(lit));
++ if cx.tables.expr_ty(exp).is_integral();
++ then {
++ span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`");
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::paths;
++use crate::utils::sugg::DiagnosticBuilderExt;
++use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def_id::DefId;
++use rustc_hir::HirIdSet;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for types with a `fn new() -> Self` method and no
++ /// implementation of
++ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
++ ///
++ /// It detects both the case when a manual
++ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
++ /// implementation is required and also when it can be created with
++ /// `#[derive(Default)]`
++ ///
++ /// **Why is this bad?** The user might expect to be able to use
++ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
++ /// type can be constructed without arguments.
++ ///
++ /// **Known problems:** Hopefully none.
++ ///
++ /// **Example:**
++ ///
++ /// ```ignore
++ /// struct Foo(Bar);
++ ///
++ /// impl Foo {
++ /// fn new() -> Self {
++ /// Foo(Bar::new())
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Instead, use:
++ ///
++ /// ```ignore
++ /// struct Foo(Bar);
++ ///
++ /// impl Default for Foo {
++ /// fn default() -> Self {
++ /// Foo(Bar::new())
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Or, if
++ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
++ /// can be derived by `#[derive(Default)]`:
++ ///
++ /// ```rust
++ /// struct Foo;
++ ///
++ /// impl Foo {
++ /// fn new() -> Self {
++ /// Foo
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Instead, use:
++ ///
++ /// ```rust
++ /// #[derive(Default)]
++ /// struct Foo;
++ ///
++ /// impl Foo {
++ /// fn new() -> Self {
++ /// Foo
++ /// }
++ /// }
++ /// ```
++ ///
++ /// You can also have `new()` call `Default::default()`.
++ pub NEW_WITHOUT_DEFAULT,
++ style,
++ "`fn new() -> Self` method without `Default` implementation"
++}
++
++#[derive(Clone, Default)]
++pub struct NewWithoutDefault {
++ impling_types: Option<HirIdSet>,
++}
++
++impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
++ #[allow(clippy::too_many_lines)]
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ if let hir::ItemKind::Impl {
++ of_trait: None, items, ..
++ } = item.kind
++ {
++ for assoc_item in items {
++ if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind {
++ let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
++ if in_external_macro(cx.sess(), impl_item.span) {
++ return;
++ }
++ if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
++ let name = impl_item.ident.name;
++ let id = impl_item.hir_id;
++ if sig.header.constness == hir::Constness::Const {
++ // can't be implemented by default
++ return;
++ }
++ if sig.header.unsafety == hir::Unsafety::Unsafe {
++ // can't be implemented for unsafe new
++ return;
++ }
++ if impl_item.generics.params.iter().any(|gen| match gen.kind {
++ hir::GenericParamKind::Type { .. } => true,
++ _ => false,
++ }) {
++ // when the result of `new()` depends on a type parameter we should not require
++ // an
++ // impl of `Default`
++ return;
++ }
++ if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
++ let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
++ let self_ty = cx.tcx.type_of(self_did);
++ if_chain! {
++ if same_tys(cx, self_ty, return_ty(cx, id));
++ if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
++ then {
++ if self.impling_types.is_none() {
++ let mut impls = HirIdSet::default();
++ cx.tcx.for_each_impl(default_trait_id, |d| {
++ if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
++ if let Some(local_def_id) = ty_def.did.as_local() {
++ impls.insert(cx.tcx.hir().as_local_hir_id(local_def_id));
++ }
++ }
++ });
++ self.impling_types = Some(impls);
++ }
++
++ // Check if a Default implementation exists for the Self type, regardless of
++ // generics
++ if_chain! {
++ if let Some(ref impling_types) = self.impling_types;
++ if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def();
++ if let Some(self_def_id) = self_def.did.as_local();
++ then {
++ let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id);
++ if impling_types.contains(&self_id) {
++ return;
++ }
++ }
++ }
++
++ if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
++ span_lint_hir_and_then(
++ cx,
++ NEW_WITHOUT_DEFAULT,
++ id,
++ impl_item.span,
++ &format!(
++ "you should consider deriving a `Default` implementation for `{}`",
++ self_ty
++ ),
++ |diag| {
++ diag.suggest_item_with_attr(
++ cx,
++ sp,
++ "try this",
++ "#[derive(Default)]",
++ Applicability::MaybeIncorrect,
++ );
++ });
++ } else {
++ span_lint_hir_and_then(
++ cx,
++ NEW_WITHOUT_DEFAULT,
++ id,
++ impl_item.span,
++ &format!(
++ "you should consider adding a `Default` implementation for `{}`",
++ self_ty
++ ),
++ |diag| {
++ diag.suggest_prepend_item(
++ cx,
++ item.span,
++ "try this",
++ &create_new_without_default_suggest_msg(self_ty),
++ Applicability::MaybeIncorrect,
++ );
++ },
++ );
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++}
++
++fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String {
++ #[rustfmt::skip]
++ format!(
++"impl Default for {} {{
++ fn default() -> Self {{
++ Self::new()
++ }}
++}}", ty)
++}
++
++fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option<Span> {
++ match ty.kind {
++ ty::Adt(adt_def, substs) if adt_def.is_struct() => {
++ for field in adt_def.all_fields() {
++ let f_ty = field.ty(cx.tcx, substs);
++ if !implements_trait(cx, f_ty, default_trait_id, &[]) {
++ return None;
++ }
++ }
++ Some(cx.tcx.def_span(adt_def.did))
++ },
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{has_drop, qpath_res, snippet_opt, span_lint, span_lint_and_sugg};
++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.tables.expr_ty(expr)),
++ ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
++ has_no_effect(cx, a) && has_no_effect(cx, b)
++ },
++ ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
++ ExprKind::Repeat(ref inner, _)
++ | ExprKind::Cast(ref inner, _)
++ | ExprKind::Type(ref inner, _)
++ | ExprKind::Unary(_, ref inner)
++ | ExprKind::Field(ref inner, _)
++ | ExprKind::AddrOf(_, _, ref inner)
++ | ExprKind::Box(ref inner) => has_no_effect(cx, inner),
++ ExprKind::Struct(_, ref fields, ref base) => {
++ !has_drop(cx, cx.tables.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(ref callee, ref args) => {
++ if let ExprKind::Path(ref qpath) = callee.kind {
++ let res = qpath_res(cx, qpath, callee.hir_id);
++ match res {
++ Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => {
++ !has_drop(cx, cx.tables.expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
++ },
++ _ => false,
++ }
++ } else {
++ false
++ }
++ },
++ ExprKind::Block(ref 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<'a, 'tcx> LateLintPass<'a, 'tcx> for NoEffect {
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++ if let StmtKind::Semi(ref expr) = stmt.kind {
++ if has_no_effect(cx, expr) {
++ span_lint(cx, NO_EFFECT, 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_and_sugg(
++ cx,
++ UNNECESSARY_OPERATION,
++ stmt.span,
++ "statement can be reduced",
++ "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(ref a, ref b) => Some(vec![&**a, &**b]),
++ ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
++ Some(vec![&**a, &**b])
++ },
++ ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()),
++ ExprKind::Repeat(ref inner, _)
++ | ExprKind::Cast(ref inner, _)
++ | ExprKind::Type(ref inner, _)
++ | ExprKind::Unary(_, ref inner)
++ | ExprKind::Field(ref inner, _)
++ | ExprKind::AddrOf(_, _, ref inner)
++ | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
++ ExprKind::Struct(_, ref fields, ref base) => {
++ if has_drop(cx, cx.tables.expr_ty(expr)) {
++ None
++ } else {
++ Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
++ }
++ },
++ ExprKind::Call(ref callee, ref args) => {
++ if let ExprKind::Path(ref qpath) = callee.kind {
++ let res = qpath_res(cx, qpath, callee.hir_id);
++ match res {
++ Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(..), _)
++ if !has_drop(cx, cx.tables.expr_ty(expr)) =>
++ {
++ Some(args.iter().collect())
++ },
++ _ => None,
++ }
++ } else {
++ None
++ }
++ },
++ ExprKind::Block(ref 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
++ _ => reduce_expression(cx, e),
++ }
++ })
++ } else {
++ None
++ }
++ },
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
++//!
++//! This lint is **deny** by default.
++
++use std::ptr;
++
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_middle::ty::adjustment::Adjust;
++use rustc_middle::ty::{Ty, TypeFlags};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{InnerSpan, Span, DUMMY_SP};
++use rustc_typeck::hir_ty_to_ty;
++
++use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for declaration of `const` items which is interior
++ /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
++ ///
++ /// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
++ /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++ /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
++ /// these types in the first place.
++ ///
++ /// The `const` should better be replaced by a `static` item if a global
++ /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
++ ///
++ /// **Known problems:** A "non-constant" const item is a legacy way to supply an
++ /// initialized value to downstream `static` items (e.g., the
++ /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
++ /// and this lint should be suppressed.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++ ///
++ /// // Bad.
++ /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++ /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++ /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++ ///
++ /// // Good.
++ /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
++ /// STATIC_ATOM.store(9, SeqCst);
++ /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++ /// ```
++ pub DECLARE_INTERIOR_MUTABLE_CONST,
++ correctness,
++ "declaring `const` with interior mutability"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks if `const` items which is interior mutable (e.g.,
++ /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
++ ///
++ /// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
++ /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++ /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
++ /// these types in the first place.
++ ///
++ /// The `const` value should be stored inside a `static` item.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++ /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++ ///
++ /// // Bad.
++ /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++ /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++ ///
++ /// // Good.
++ /// static STATIC_ATOM: AtomicUsize = CONST_ATOM;
++ /// STATIC_ATOM.store(9, SeqCst);
++ /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++ /// ```
++ pub BORROW_INTERIOR_MUTABLE_CONST,
++ correctness,
++ "referencing `const` with interior mutability"
++}
++
++#[allow(dead_code)]
++#[derive(Copy, Clone)]
++enum Source {
++ Item { item: Span },
++ Assoc { item: Span, ty: Span },
++ Expr { expr: Span },
++}
++
++impl Source {
++ #[must_use]
++ fn lint(&self) -> (&'static Lint, &'static str, Span) {
++ match self {
++ Self::Item { item } | Self::Assoc { item, .. } => (
++ DECLARE_INTERIOR_MUTABLE_CONST,
++ "a `const` item should never be interior mutable",
++ *item,
++ ),
++ Self::Expr { expr } => (
++ BORROW_INTERIOR_MUTABLE_CONST,
++ "a `const` item with interior mutability should not be borrowed",
++ *expr,
++ ),
++ }
++ }
++}
++
++fn verify_ty_bound<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, source: Source) {
++ if ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) || is_copy(cx, ty) {
++ // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
++ // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
++ // as well.
++ return;
++ }
++
++ let (lint, msg, span) = source.lint();
++ span_lint_and_then(cx, lint, span, msg, |diag| {
++ if span.from_expansion() {
++ return; // Don't give suggestions into macros.
++ }
++ match source {
++ Source::Item { .. } => {
++ let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
++ diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
++ },
++ Source::Assoc { ty: ty_span, .. } => {
++ if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
++ diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
++ }
++ },
++ Source::Expr { .. } => {
++ diag.help("assign this const to a local or static variable, and use the variable here");
++ },
++ }
++ });
++}
++
++declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx Item<'_>) {
++ if let ItemKind::Const(hir_ty, ..) = &it.kind {
++ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++ verify_ty_bound(cx, ty, Source::Item { item: it.span });
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx TraitItem<'_>) {
++ if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
++ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++ verify_ty_bound(
++ cx,
++ ty,
++ Source::Assoc {
++ ty: hir_ty.span,
++ item: trait_item.span,
++ },
++ );
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) {
++ if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
++ let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id);
++ let item = cx.tcx.hir().expect_item(item_hir_id);
++ // Ensure the impl is an inherent impl.
++ if let ItemKind::Impl { of_trait: None, .. } = item.kind {
++ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++ verify_ty_bound(
++ cx,
++ ty,
++ Source::Assoc {
++ ty: hir_ty.span,
++ item: impl_item.span,
++ },
++ );
++ }
++ }
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Path(qpath) = &expr.kind {
++ // Only lint if we use the const item inside a function.
++ if in_constant(cx, expr.hir_id) {
++ return;
++ }
++
++ // Make sure it is a const item.
++ match qpath_res(cx, qpath, expr.hir_id) {
++ Res::Def(DefKind::Const | DefKind::AssocConst, _) => {},
++ _ => return,
++ };
++
++ // Climb up to resolve any field access and explicit referencing.
++ let mut cur_expr = expr;
++ let mut dereferenced_expr = expr;
++ let mut needs_check_adjustment = true;
++ loop {
++ let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id);
++ if parent_id == cur_expr.hir_id {
++ break;
++ }
++ if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find(parent_id) {
++ match &parent_expr.kind {
++ ExprKind::AddrOf(..) => {
++ // `&e` => `e` must be referenced.
++ needs_check_adjustment = false;
++ },
++ ExprKind::Field(..) => {
++ dereferenced_expr = parent_expr;
++ needs_check_adjustment = true;
++ },
++ ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
++ // `e[i]` => desugared to `*Index::index(&e, i)`,
++ // meaning `e` must be referenced.
++ // no need to go further up since a method call is involved now.
++ needs_check_adjustment = false;
++ break;
++ },
++ ExprKind::Unary(UnOp::UnDeref, _) => {
++ // `*e` => desugared to `*Deref::deref(&e)`,
++ // meaning `e` must be referenced.
++ // no need to go further up since a method call is involved now.
++ needs_check_adjustment = false;
++ break;
++ },
++ _ => break,
++ }
++ cur_expr = parent_expr;
++ } else {
++ break;
++ }
++ }
++
++ let ty = if needs_check_adjustment {
++ let adjustments = cx.tables.expr_adjustments(dereferenced_expr);
++ if let Some(i) = adjustments.iter().position(|adj| match adj.kind {
++ Adjust::Borrow(_) | Adjust::Deref(_) => true,
++ _ => false,
++ }) {
++ if i == 0 {
++ cx.tables.expr_ty(dereferenced_expr)
++ } else {
++ adjustments[i - 1].target
++ }
++ } else {
++ // No borrow adjustments means the entire const is moved.
++ return;
++ }
++ } else {
++ cx.tables.expr_ty(dereferenced_expr)
++ };
++
++ verify_ty_bound(cx, ty, Source::Expr { expr: expr.span });
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{span_lint, span_lint_and_then};
++use rustc_ast::ast::{
++ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind,
++};
++use rustc_ast::attr;
++use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::SymbolStr;
++use std::cmp::Ordering;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for names that are very similar and thus confusing.
++ ///
++ /// **Why is this bad?** It's hard to distinguish between names that differ only
++ /// by a single character.
++ ///
++ /// **Known problems:** None?
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let checked_exp = something;
++ /// let checked_expr = something_else;
++ /// ```
++ pub SIMILAR_NAMES,
++ pedantic,
++ "similarly named items and bindings"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for too many variables whose name consists of a
++ /// single character.
++ ///
++ /// **Why is this bad?** It's hard to memorize what a variable means without a
++ /// descriptive name.
++ ///
++ /// **Known problems:** None?
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let (a, b, c, d, e, f, g) = (...);
++ /// ```
++ pub MANY_SINGLE_CHAR_NAMES,
++ style,
++ "too many single character bindings"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks if you have variables whose name consists of just
++ /// underscores and digits.
++ ///
++ /// **Why is this bad?** It's hard to memorize what a variable means without a
++ /// descriptive name.
++ ///
++ /// **Known problems:** None?
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let _1 = 1;
++ /// let ___1 = 1;
++ /// let __1___2 = 11;
++ /// ```
++ pub JUST_UNDERSCORES_AND_DIGITS,
++ style,
++ "unclear name"
++}
++
++#[derive(Copy, Clone)]
++pub struct NonExpressiveNames {
++ pub single_char_binding_names_threshold: u64,
++}
++
++impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);
++
++struct ExistingName {
++ interned: SymbolStr,
++ span: Span,
++ len: usize,
++ whitelist: &'static [&'static str],
++}
++
++struct SimilarNamesLocalVisitor<'a, 'tcx> {
++ names: Vec<ExistingName>,
++ cx: &'a EarlyContext<'tcx>,
++ lint: &'a NonExpressiveNames,
++
++ /// A stack of scopes containing the single-character bindings in each scope.
++ single_char_names: Vec<Vec<Ident>>,
++}
++
++impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
++ fn check_single_char_names(&self) {
++ let num_single_char_names = self.single_char_names.iter().flatten().count();
++ let threshold = self.lint.single_char_binding_names_threshold;
++ if num_single_char_names as u64 > threshold {
++ let span = self
++ .single_char_names
++ .iter()
++ .flatten()
++ .map(|ident| ident.span)
++ .collect::<Vec<_>>();
++ span_lint(
++ self.cx,
++ MANY_SINGLE_CHAR_NAMES,
++ span,
++ &format!(
++ "{} bindings with single-character names in scope",
++ num_single_char_names
++ ),
++ );
++ }
++ }
++}
++
++// this list contains lists of names that are allowed to be similar
++// the assumption is that no name is ever contained in multiple lists.
++#[rustfmt::skip]
++const WHITELIST: &[&[&str]] = &[
++ &["parsed", "parser"],
++ &["lhs", "rhs"],
++ &["tx", "rx"],
++ &["set", "get"],
++ &["args", "arms"],
++ &["qpath", "path"],
++ &["lit", "lint"],
++];
++
++struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
++
++impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
++ fn visit_pat(&mut self, pat: &'tcx Pat) {
++ match pat.kind {
++ PatKind::Ident(_, ident, _) => self.check_ident(ident),
++ PatKind::Struct(_, ref fields, _) => {
++ for field in fields {
++ if !field.is_shorthand {
++ self.visit_pat(&field.pat);
++ }
++ }
++ },
++ // just go through the first pattern, as either all patterns
++ // bind the same bindings or rustc would have errored much earlier
++ PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
++ _ => walk_pat(self, pat),
++ }
++ }
++ fn visit_mac(&mut self, _mac: &MacCall) {
++ // do not check macs
++ }
++}
++
++#[must_use]
++fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
++ for &allow in WHITELIST {
++ if whitelisted(interned_name, allow) {
++ return Some(allow);
++ }
++ }
++ None
++}
++
++#[must_use]
++fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
++ list.iter()
++ .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
++}
++
++impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
++ fn check_short_ident(&mut self, ident: Ident) {
++ // Ignore shadowing
++ if self
++ .0
++ .single_char_names
++ .iter()
++ .flatten()
++ .any(|id| id.name == ident.name)
++ {
++ return;
++ }
++
++ if let Some(scope) = &mut self.0.single_char_names.last_mut() {
++ scope.push(ident);
++ }
++ }
++
++ #[allow(clippy::too_many_lines)]
++ fn check_ident(&mut self, ident: Ident) {
++ let interned_name = ident.name.as_str();
++ if interned_name.chars().any(char::is_uppercase) {
++ return;
++ }
++ if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
++ span_lint(
++ self.0.cx,
++ JUST_UNDERSCORES_AND_DIGITS,
++ ident.span,
++ "consider choosing a more descriptive name",
++ );
++ return;
++ }
++ let count = interned_name.chars().count();
++ if count < 3 {
++ if count == 1 {
++ self.check_short_ident(ident);
++ }
++ return;
++ }
++ for existing_name in &self.0.names {
++ if whitelisted(&interned_name, existing_name.whitelist) {
++ continue;
++ }
++ let mut split_at = None;
++ match existing_name.len.cmp(&count) {
++ Ordering::Greater => {
++ if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
++ continue;
++ }
++ },
++ Ordering::Less => {
++ if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
++ continue;
++ }
++ },
++ Ordering::Equal => {
++ let mut interned_chars = interned_name.chars();
++ let mut existing_chars = existing_name.interned.chars();
++ let first_i = interned_chars.next().expect("we know we have at least one char");
++ let first_e = existing_chars.next().expect("we know we have at least one char");
++ let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
++
++ if eq_or_numeric((first_i, first_e)) {
++ let last_i = interned_chars.next_back().expect("we know we have at least two chars");
++ let last_e = existing_chars.next_back().expect("we know we have at least two chars");
++ if eq_or_numeric((last_i, last_e)) {
++ if interned_chars
++ .zip(existing_chars)
++ .filter(|&ie| !eq_or_numeric(ie))
++ .count()
++ != 1
++ {
++ continue;
++ }
++ } else {
++ let second_last_i = interned_chars
++ .next_back()
++ .expect("we know we have at least three chars");
++ let second_last_e = existing_chars
++ .next_back()
++ .expect("we know we have at least three chars");
++ if !eq_or_numeric((second_last_i, second_last_e))
++ || second_last_i == '_'
++ || !interned_chars.zip(existing_chars).all(eq_or_numeric)
++ {
++ // allowed similarity foo_x, foo_y
++ // or too many chars differ (foo_x, boo_y) or (foox, booy)
++ continue;
++ }
++ split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
++ }
++ } else {
++ let second_i = interned_chars.next().expect("we know we have at least two chars");
++ let second_e = existing_chars.next().expect("we know we have at least two chars");
++ if !eq_or_numeric((second_i, second_e))
++ || second_i == '_'
++ || !interned_chars.zip(existing_chars).all(eq_or_numeric)
++ {
++ // allowed similarity x_foo, y_foo
++ // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
++ continue;
++ }
++ split_at = interned_name.chars().next().map(char::len_utf8);
++ }
++ },
++ }
++ span_lint_and_then(
++ self.0.cx,
++ SIMILAR_NAMES,
++ ident.span,
++ "binding's name is too similar to existing binding",
++ |diag| {
++ diag.span_note(existing_name.span, "existing binding defined here");
++ if let Some(split) = split_at {
++ diag.span_help(
++ ident.span,
++ &format!(
++ "separate the discriminating character by an \
++ underscore like: `{}_{}`",
++ &interned_name[..split],
++ &interned_name[split..]
++ ),
++ );
++ }
++ },
++ );
++ return;
++ }
++ self.0.names.push(ExistingName {
++ whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
++ interned: interned_name,
++ span: ident.span,
++ len: count,
++ });
++ }
++}
++
++impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
++ /// ensure scoping rules work
++ fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
++ let n = self.names.len();
++ let single_char_count = self.single_char_names.len();
++ f(self);
++ self.names.truncate(n);
++ self.single_char_names.truncate(single_char_count);
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
++ fn visit_local(&mut self, local: &'tcx Local) {
++ if let Some(ref init) = local.init {
++ self.apply(|this| walk_expr(this, &**init));
++ }
++ // add the pattern after the expression because the bindings aren't available
++ // yet in the init
++ // expression
++ SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
++ }
++ fn visit_block(&mut self, blk: &'tcx Block) {
++ self.single_char_names.push(vec![]);
++
++ self.apply(|this| walk_block(this, blk));
++
++ self.check_single_char_names();
++ self.single_char_names.pop();
++ }
++ fn visit_arm(&mut self, arm: &'tcx Arm) {
++ self.single_char_names.push(vec![]);
++
++ self.apply(|this| {
++ SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
++ this.apply(|this| walk_expr(this, &arm.body));
++ });
++
++ self.check_single_char_names();
++ self.single_char_names.pop();
++ }
++ fn visit_item(&mut self, _: &Item) {
++ // do not recurse into inner items
++ }
++ fn visit_mac(&mut self, _mac: &MacCall) {
++ // do not check macs
++ }
++}
++
++impl EarlyLintPass for NonExpressiveNames {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
++ do_check(self, cx, &item.attrs, &sig.decl, blk);
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
++ if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
++ do_check(self, cx, &item.attrs, &sig.decl, blk);
++ }
++ }
++}
++
++fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
++ if !attr::contains_name(attrs, sym!(test)) {
++ let mut visitor = SimilarNamesLocalVisitor {
++ names: Vec::new(),
++ cx,
++ lint,
++ single_char_names: vec![vec![]],
++ };
++
++ // initialize with function arguments
++ for arg in &decl.inputs {
++ SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
++ }
++ // walk all other bindings
++ walk_block(&mut visitor, blk);
++
++ visitor.check_single_char_names();
++ }
++}
++
++/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
++#[must_use]
++fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
++ debug_assert!(a_name.chars().count() < b_name.chars().count());
++ let mut a_chars = a_name.chars();
++ let mut b_chars = b_name.chars();
++ while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
++ if a == b {
++ continue;
++ }
++ if let Some(b2) = b_chars.next() {
++ // check if there's just one character inserted
++ return a != b2 || a_chars.ne(b_chars);
++ } else {
++ // tuple
++ // ntuple
++ return true;
++ }
++ }
++ // for item in items
++ true
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{Span, Spanned};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for duplicate open options as well as combinations
++ /// that make no sense.
++ ///
++ /// **Why is this bad?** In the best case, the code will be harder to read than
++ /// necessary. I don't know the worst case.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::fs::OpenOptions;
++ ///
++ /// OpenOptions::new().read(true).truncate(true);
++ /// ```
++ pub NONSENSICAL_OPEN_OPTIONS,
++ correctness,
++ "nonsensical combination of options for opening a file"
++}
++
++declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OpenOptions {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if let ExprKind::MethodCall(ref path, _, ref arguments) = e.kind {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
++ if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
++ let mut options = Vec::new();
++ get_open_options(cx, &arguments[0], &mut options);
++ check_open_options(cx, &options, e.span);
++ }
++ }
++ }
++}
++
++#[derive(Debug, PartialEq, Eq, Clone, Copy)]
++enum Argument {
++ True,
++ False,
++ Unknown,
++}
++
++#[derive(Debug)]
++enum OpenOption {
++ Write,
++ Read,
++ Truncate,
++ Create,
++ Append,
++}
++
++fn get_open_options(cx: &LateContext<'_, '_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
++ if let ExprKind::MethodCall(ref path, _, ref arguments) = argument.kind {
++ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
++
++ // Only proceed if this is a call on some object of type std::fs::OpenOptions
++ if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
++ let argument_option = match arguments[1].kind {
++ ExprKind::Lit(ref span) => {
++ if let Spanned {
++ node: LitKind::Bool(lit),
++ ..
++ } = *span
++ {
++ if lit {
++ Argument::True
++ } else {
++ Argument::False
++ }
++ } else {
++ return; // The function is called with a literal
++ // which is not a boolean literal. This is theoretically
++ // possible, but not very likely.
++ }
++ },
++ _ => Argument::Unknown,
++ };
++
++ match &*path.ident.as_str() {
++ "create" => {
++ options.push((OpenOption::Create, argument_option));
++ },
++ "append" => {
++ options.push((OpenOption::Append, argument_option));
++ },
++ "truncate" => {
++ options.push((OpenOption::Truncate, argument_option));
++ },
++ "read" => {
++ options.push((OpenOption::Read, argument_option));
++ },
++ "write" => {
++ options.push((OpenOption::Write, argument_option));
++ },
++ _ => (),
++ }
++
++ get_open_options(cx, &arguments[0], options);
++ }
++ }
++}
++
++fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument)], span: Span) {
++ let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
++ let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
++ (false, false, false, false, false);
++ // This code is almost duplicated (oh, the irony), but I haven't found a way to
++ // unify it.
++
++ for option in options {
++ match *option {
++ (OpenOption::Create, arg) => {
++ if create {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "the method `create` is called more than once",
++ );
++ } else {
++ create = true
++ }
++ create_arg = create_arg || (arg == Argument::True);
++ },
++ (OpenOption::Append, arg) => {
++ if append {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "the method `append` is called more than once",
++ );
++ } else {
++ append = true
++ }
++ append_arg = append_arg || (arg == Argument::True);
++ },
++ (OpenOption::Truncate, arg) => {
++ if truncate {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "the method `truncate` is called more than once",
++ );
++ } else {
++ truncate = true
++ }
++ truncate_arg = truncate_arg || (arg == Argument::True);
++ },
++ (OpenOption::Read, arg) => {
++ if read {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "the method `read` is called more than once",
++ );
++ } else {
++ read = true
++ }
++ read_arg = read_arg || (arg == Argument::True);
++ },
++ (OpenOption::Write, arg) => {
++ if write {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "the method `write` is called more than once",
++ );
++ } else {
++ write = true
++ }
++ write_arg = write_arg || (arg == Argument::True);
++ },
++ }
++ }
++
++ if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "file opened with `truncate` and `read`",
++ );
++ }
++ if append && truncate && append_arg && truncate_arg {
++ span_lint(
++ cx,
++ NONSENSICAL_OPEN_OPTIONS,
++ span,
++ "file opened with `append` and `truncate`",
++ );
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_direct_expn_of, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `option_env!(...).unwrap()` and
++ /// suggests usage of the `env!` macro.
++ ///
++ /// **Why is this bad?** Unwrapping the result of `option_env!` will panic
++ /// at run-time if the environment variable doesn't exist, whereas `env!`
++ /// catches it at compile-time.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust,no_run
++ /// let _ = option_env!("HOME").unwrap();
++ /// ```
++ ///
++ /// Is better expressed as:
++ ///
++ /// ```rust,no_run
++ /// let _ = env!("HOME");
++ /// ```
++ pub OPTION_ENV_UNWRAP,
++ correctness,
++ "using `option_env!(...).unwrap()` to get environment variable"
++}
++
++declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]);
++
++impl EarlyLintPass for OptionEnvUnwrap {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if_chain! {
++ if let ExprKind::MethodCall(path_segment, args) = &expr.kind;
++ let method_name = path_segment.ident.as_str();
++ if method_name == "expect" || method_name == "unwrap";
++ if let ExprKind::Call(caller, _) = &args[0].kind;
++ if is_direct_expn_of(caller.span, "option_env").is_some();
++ then {
++ span_lint_and_help(
++ cx,
++ OPTION_ENV_UNWRAP,
++ expr.span,
++ "this will panic at run-time if the environment variable doesn't exist at compile-time",
++ None,
++ "consider using the `env!` macro instead"
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{span_lint, SpanlessEq};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Detects classic underflow/overflow checks.
++ ///
++ /// **Why is this bad?** Most classic C underflow/overflow checks will fail in
++ /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let a = 1;
++ /// # let b = 2;
++ /// a + b < a;
++ /// ```
++ pub OVERFLOW_CHECK_CONDITIONAL,
++ complexity,
++ "overflow checks inspired by C which are likely to panic"
++}
++
++declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OverflowCheckConditional {
++ // a + b < a, a > a + b, a < a - b, a - b > a
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r);
++ if_chain! {
++ if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind;
++ if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind;
++ if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
++ if cx.tables.expr_ty(ident1).is_integral();
++ if cx.tables.expr_ty(ident2).is_integral();
++ then {
++ if let BinOpKind::Lt = op.node {
++ if let BinOpKind::Add = op2.node {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++ "You are trying to use classic C overflow conditions that will fail in Rust.");
++ }
++ }
++ if let BinOpKind::Gt = op.node {
++ if let BinOpKind::Sub = op2.node {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++ "You are trying to use classic C underflow conditions that will fail in Rust.");
++ }
++ }
++ }
++ }
++
++ if_chain! {
++ if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind;
++ if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind;
++ if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
++ if cx.tables.expr_ty(ident1).is_integral();
++ if cx.tables.expr_ty(ident2).is_integral();
++ then {
++ if let BinOpKind::Gt = op.node {
++ if let BinOpKind::Add = op2.node {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++ "You are trying to use classic C overflow conditions that will fail in Rust.");
++ }
++ }
++ if let BinOpKind::Lt = op.node {
++ if let BinOpKind::Sub = op2.node {
++ span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++ "You are trying to use classic C underflow conditions that will fail in Rust.");
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, paths, span_lint};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for missing parameters in `panic!`.
++ ///
++ /// **Why is this bad?** Contrary to the `format!` family of macros, there are
++ /// two forms of `panic!`: if there are no parameters given, the first argument
++ /// is not a format string and used literally. So while `format!("{}")` will
++ /// fail to compile, `panic!("{}")` will not.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// panic!("This `panic!` is probably missing a parameter there: {}");
++ /// ```
++ pub PANIC_PARAMS,
++ style,
++ "missing parameters in `panic!` calls"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `panic!`.
++ ///
++ /// **Why is this bad?** `panic!` will stop the execution of the executable
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// panic!("even with a good reason");
++ /// ```
++ pub PANIC,
++ restriction,
++ "usage of the `panic!` macro"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `unimplemented!`.
++ ///
++ /// **Why is this bad?** This macro should not be present in production code
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// unimplemented!();
++ /// ```
++ pub UNIMPLEMENTED,
++ restriction,
++ "`unimplemented!` should not be present in production code"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `todo!`.
++ ///
++ /// **Why is this bad?** This macro should not be present in production code
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// todo!();
++ /// ```
++ pub TODO,
++ restriction,
++ "`todo!` should not be present in production code"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `unreachable!`.
++ ///
++ /// **Why is this bad?** This macro can cause code to panic
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```no_run
++ /// unreachable!();
++ /// ```
++ pub UNREACHABLE,
++ restriction,
++ "`unreachable!` should not be present in production code"
++}
++
++declare_lint_pass!(PanicUnimplemented => [PANIC_PARAMS, UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PanicUnimplemented {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Block(ref block, _) = expr.kind;
++ if let Some(ref ex) = block.expr;
++ if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC);
++ if params.len() == 1;
++ then {
++ if is_expn_of(expr.span, "unimplemented").is_some() {
++ let span = get_outer_span(expr);
++ span_lint(cx, UNIMPLEMENTED, span,
++ "`unimplemented` should not be present in production code");
++ } else if is_expn_of(expr.span, "todo").is_some() {
++ let span = get_outer_span(expr);
++ span_lint(cx, TODO, span,
++ "`todo` should not be present in production code");
++ } else if is_expn_of(expr.span, "unreachable").is_some() {
++ let span = get_outer_span(expr);
++ span_lint(cx, UNREACHABLE, span,
++ "`unreachable` should not be present in production code");
++ } else if is_expn_of(expr.span, "panic").is_some() {
++ let span = get_outer_span(expr);
++ span_lint(cx, PANIC, span,
++ "`panic` should not be present in production code");
++ match_panic(params, expr, cx);
++ }
++ }
++ }
++ }
++}
++
++fn get_outer_span(expr: &Expr<'_>) -> Span {
++ if_chain! {
++ if expr.span.from_expansion();
++ let first = expr.span.ctxt().outer_expn_data();
++ if first.call_site.from_expansion();
++ let second = first.call_site.ctxt().outer_expn_data();
++ then {
++ second.call_site
++ } else {
++ expr.span
++ }
++ }
++}
++
++fn match_panic(params: &[Expr<'_>], expr: &Expr<'_>, cx: &LateContext<'_, '_>) {
++ if_chain! {
++ if let ExprKind::Lit(ref lit) = params[0].kind;
++ if is_direct_expn_of(expr.span, "panic").is_some();
++ if let LitKind::Str(ref string, _) = lit.node;
++ let string = string.as_str().replace("{{", "").replace("}}", "");
++ if let Some(par) = string.find('{');
++ if string[par..].contains('}');
++ if params[0].span.source_callee().is_none();
++ if params[0].span.lo() != params[0].span.hi();
++ then {
++ span_lint(cx, PANIC_PARAMS, params[0].span,
++ "you probably are missing some parameter in your format string");
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_automatically_derived, span_lint_hir};
++use if_chain::if_chain;
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`.
++ ///
++ /// **Why is this bad?** `PartialEq::ne` is required to always return the
++ /// negated result of `PartialEq::eq`, which is exactly what the default
++ /// implementation does. Therefore, there should never be any need to
++ /// re-implement it.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// struct Foo;
++ ///
++ /// impl PartialEq for Foo {
++ /// fn eq(&self, other: &Foo) -> bool { true }
++ /// fn ne(&self, other: &Foo) -> bool { !(self == other) }
++ /// }
++ /// ```
++ pub PARTIALEQ_NE_IMPL,
++ complexity,
++ "re-implementing `PartialEq::ne`"
++}
++
++declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PartialEqNeImpl {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if_chain! {
++ if let ItemKind::Impl{ of_trait: Some(ref trait_ref), items: impl_items, .. } = item.kind;
++ if !is_automatically_derived(&*item.attrs);
++ if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
++ if trait_ref.path.res.def_id() == eq_trait;
++ then {
++ for impl_item in impl_items {
++ if impl_item.ident.name == sym!(ne) {
++ span_lint_hir(
++ cx,
++ PARTIALEQ_NE_IMPL,
++ impl_item.id.hir_id,
++ impl_item.span,
++ "re-implementing `PartialEq::ne` is unnecessary",
++ );
++ }
++ }
++ }
++ };
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::path::{Component, Path};
++
++declare_clippy_lint! {
++ /// **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
++ /// calls on `PathBuf` that can cause overwrites.
++ ///
++ /// **Why is this bad?** Calling `push` with a root path at the start can overwrite the
++ /// previous defined path.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// use std::path::PathBuf;
++ ///
++ /// let mut x = PathBuf::from("/foo");
++ /// x.push("/bar");
++ /// assert_eq!(x, PathBuf::from("/bar"));
++ /// ```
++ /// Could be written:
++ ///
++ /// ```rust
++ /// use std::path::PathBuf;
++ ///
++ /// let mut x = PathBuf::from("/foo");
++ /// x.push("bar");
++ /// assert_eq!(x, PathBuf::from("/foo/bar"));
++ /// ```
++ pub PATH_BUF_PUSH_OVERWRITE,
++ nursery,
++ "calling `push` with file system root on `PathBuf` can overwrite it"
++}
++
++declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathBufPushOverwrite {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++ if path.ident.name == sym!(push);
++ if args.len() == 2;
++ if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::PATH_BUF);
++ if let Some(get_index_arg) = args.get(1);
++ if let ExprKind::Lit(ref lit) = get_index_arg.kind;
++ if let LitKind::Str(ref path_lit, _) = lit.node;
++ if let pushed_path = Path::new(&*path_lit.as_str());
++ if let Some(pushed_path_lit) = pushed_path.to_str();
++ if pushed_path.has_root();
++ if let Some(root) = pushed_path.components().next();
++ if root == Component::RootDir;
++ then {
++ span_lint_and_sugg(
++ cx,
++ PATH_BUF_PUSH_OVERWRITE,
++ lit.span,
++ "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
++ "try",
++ format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
++use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++const ODD_FUNCTIONS_WHITELIST: [&str; 14] = [
++ "asin",
++ "asinh",
++ "atan",
++ "atanh",
++ "cbrt",
++ "fract",
++ "round",
++ "signum",
++ "sin",
++ "sinh",
++ "tan",
++ "tanh",
++ "to_degrees",
++ "to_radians",
++];
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for operations where precedence may be unclear
++ /// and suggests to add parentheses. Currently it catches the following:
++ /// * mixed usage of arithmetic and bit shifting/combining operators without
++ /// parentheses
++ /// * a "negative" numeric literal (which is really a unary `-` followed by a
++ /// numeric literal)
++ /// followed by a method call
++ ///
++ /// **Why is this bad?** Not everyone knows the precedence of those operators by
++ /// heart, so expressions like these may trip others trying to reason about the
++ /// code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
++ /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
++ pub PRECEDENCE,
++ complexity,
++ "operations where precedence may be unclear"
++}
++
++declare_lint_pass!(Precedence => [PRECEDENCE]);
++
++impl EarlyLintPass for Precedence {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
++ let span_sugg = |expr: &Expr, sugg, appl| {
++ span_lint_and_sugg(
++ cx,
++ PRECEDENCE,
++ expr.span,
++ "operator precedence can trip the unwary",
++ "consider parenthesizing your expression",
++ sugg,
++ appl,
++ );
++ };
++
++ if !is_bit_op(op) {
++ return;
++ }
++ let mut applicability = Applicability::MachineApplicable;
++ match (is_arith_expr(left), is_arith_expr(right)) {
++ (true, true) => {
++ let sugg = format!(
++ "({}) {} ({})",
++ snippet_with_applicability(cx, left.span, "..", &mut applicability),
++ op.to_string(),
++ snippet_with_applicability(cx, right.span, "..", &mut applicability)
++ );
++ span_sugg(expr, sugg, applicability);
++ },
++ (true, false) => {
++ let sugg = format!(
++ "({}) {} {}",
++ snippet_with_applicability(cx, left.span, "..", &mut applicability),
++ op.to_string(),
++ snippet_with_applicability(cx, right.span, "..", &mut applicability)
++ );
++ span_sugg(expr, sugg, applicability);
++ },
++ (false, true) => {
++ let sugg = format!(
++ "{} {} ({})",
++ snippet_with_applicability(cx, left.span, "..", &mut applicability),
++ op.to_string(),
++ snippet_with_applicability(cx, right.span, "..", &mut applicability)
++ );
++ span_sugg(expr, sugg, applicability);
++ },
++ (false, false) => (),
++ }
++ }
++
++ if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind {
++ if let ExprKind::MethodCall(ref path_segment, ref args) = rhs.kind {
++ let path_segment_str = path_segment.ident.name.as_str();
++ if let Some(slf) = args.first() {
++ if let ExprKind::Lit(ref lit) = slf.kind {
++ match lit.kind {
++ LitKind::Int(..) | LitKind::Float(..) => {
++ if ODD_FUNCTIONS_WHITELIST
++ .iter()
++ .any(|odd_function| **odd_function == *path_segment_str)
++ {
++ return;
++ }
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ PRECEDENCE,
++ expr.span,
++ "unary minus has lower precedence than method call",
++ "consider adding parentheses to clarify your intent",
++ format!(
++ "-({})",
++ snippet_with_applicability(cx, rhs.span, "..", &mut applicability)
++ ),
++ applicability,
++ );
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++ }
++ }
++}
++
++fn is_arith_expr(expr: &Expr) -> bool {
++ match expr.kind {
++ ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op),
++ _ => false,
++ }
++}
++
++#[must_use]
++fn is_bit_op(op: BinOpKind) -> bool {
++ use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr};
++ match op {
++ BitXor | BitAnd | BitOr | Shl | Shr => true,
++ _ => false,
++ }
++}
++
++#[must_use]
++fn is_arith_op(op: BinOpKind) -> bool {
++ use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub};
++ match op {
++ Add | Sub | Mul | Div | Rem => true,
++ _ => false,
++ }
++}
--- /dev/null
--- /dev/null
++//! Checks for usage of `&Vec[_]` and `&String`.
++
++use crate::utils::ptr::get_spans;
++use crate::utils::{
++ is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
++ span_lint_and_then, walk_ptrs_hir_ty,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{
++ BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind,
++ Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::MultiSpan;
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++ /// **What it does:** This lint checks for function arguments of type `&String`
++ /// or `&Vec` unless the references are mutable. It will also suggest you
++ /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
++ /// calls.
++ ///
++ /// **Why is this bad?** Requiring the argument to be of the specific size
++ /// makes the function less useful for no benefit; slices in the form of `&[T]`
++ /// or `&str` usually suffice and can be obtained from other types, too.
++ ///
++ /// **Known problems:** The lint does not follow data. So if you have an
++ /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
++ /// changing that `.clone()` to `.to_owned()`.
++ ///
++ /// Other functions called from this function taking a `&String` or `&Vec`
++ /// argument may also fail to compile if you change the argument. Applying
++ /// this lint on them will fix the problem, but they may be in other crates.
++ ///
++ /// Also there may be `fn(&Vec)`-typed references pointing to your function.
++ /// If you have them, you will get a compiler error after applying this lint's
++ /// suggestions. You then have the choice to undo your changes or change the
++ /// type of the reference.
++ ///
++ /// Note that if the function is part of your public interface, there may be
++ /// other crates referencing it you may not be aware. Carefully deprecate the
++ /// function before applying the lint suggestions in this case.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// fn foo(&Vec<u32>) { .. }
++ /// ```
++ pub PTR_ARG,
++ style,
++ "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint checks for equality comparisons with `ptr::null`
++ ///
++ /// **Why is this bad?** It's easier and more readable to use the inherent
++ /// `.is_null()`
++ /// method instead
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if x == ptr::null {
++ /// ..
++ /// }
++ /// ```
++ pub CMP_NULL,
++ style,
++ "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint checks for functions that take immutable
++ /// references and return
++ /// mutable ones.
++ ///
++ /// **Why is this bad?** This is trivially unsound, as one can create two
++ /// mutable references
++ /// from the same (immutable!) source. This
++ /// [error](https://github.com/rust-lang/rust/issues/39465)
++ /// actually lead to an interim Rust release 1.15.1.
++ ///
++ /// **Known problems:** To be on the conservative side, if there's at least one
++ /// mutable reference
++ /// with the output lifetime, this lint will not trigger. In practice, this
++ /// case is unlikely anyway.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// fn foo(&Foo) -> &mut Bar { .. }
++ /// ```
++ pub MUT_FROM_REF,
++ correctness,
++ "fns that create mutable refs from immutable ref args"
++}
++
++declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ptr {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
++ check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++ if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
++ let parent_item = cx.tcx.hir().get_parent_item(item.hir_id);
++ if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
++ if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind {
++ return; // ignore trait impls
++ }
++ }
++ check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++ if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
++ let body_id = if let TraitFn::Provided(b) = *trait_method {
++ Some(b)
++ } else {
++ None
++ };
++ check_fn(cx, &sig.decl, item.hir_id, body_id);
++ }
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind {
++ if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
++ span_lint(
++ cx,
++ CMP_NULL,
++ expr.span,
++ "Comparing with null is better expressed by the `.is_null()` method",
++ );
++ }
++ }
++ }
++}
++
++#[allow(clippy::too_many_lines)]
++fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
++ let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
++ let sig = cx.tcx.fn_sig(fn_def_id);
++ let fn_ty = sig.skip_binder();
++
++ for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
++ if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
++ if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
++ let mut ty_snippet = None;
++ if_chain! {
++ if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
++ if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
++ then {
++ let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ }).collect();
++ if types.len() == 1 {
++ ty_snippet = snippet_opt(cx, types[0].span);
++ }
++ }
++ };
++ if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
++ span_lint_and_then(
++ cx,
++ PTR_ARG,
++ arg.span,
++ "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
++ with non-Vec-based slices.",
++ |diag| {
++ if let Some(ref snippet) = ty_snippet {
++ diag.span_suggestion(
++ arg.span,
++ "change this to",
++ format!("&[{}]", snippet),
++ Applicability::Unspecified,
++ );
++ }
++ for (clonespan, suggestion) in spans {
++ diag.span_suggestion(
++ clonespan,
++ &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
++ Cow::Owned(format!("change `{}` to", x))
++ }),
++ suggestion.into(),
++ Applicability::Unspecified,
++ );
++ }
++ },
++ );
++ }
++ } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++ if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
++ span_lint_and_then(
++ cx,
++ PTR_ARG,
++ arg.span,
++ "writing `&String` instead of `&str` involves a new object where a slice will do.",
++ |diag| {
++ diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
++ for (clonespan, suggestion) in spans {
++ diag.span_suggestion_short(
++ clonespan,
++ &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
++ Cow::Owned(format!("change `{}` to", x))
++ }),
++ suggestion.into(),
++ Applicability::Unspecified,
++ );
++ }
++ },
++ );
++ }
++ } else if match_type(cx, ty, &paths::COW) {
++ if_chain! {
++ if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
++ if let TyKind::Path(ref path) = ty.kind;
++ if let QPath::Resolved(None, ref pp) = *path;
++ if let [ref bx] = *pp.segments;
++ if let Some(ref params) = bx.args;
++ if !params.parenthesized;
++ if let Some(inner) = params.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ then {
++ let replacement = snippet_opt(cx, inner.span);
++ if let Some(r) = replacement {
++ span_lint_and_sugg(
++ cx,
++ PTR_ARG,
++ arg.span,
++ "using a reference to `Cow` is not recommended.",
++ "change this to",
++ "&".to_owned() + &r,
++ Applicability::Unspecified,
++ );
++ }
++ }
++ }
++ }
++ }
++ }
++
++ if let FnRetTy::Return(ref ty) = decl.output {
++ if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
++ let mut immutables = vec![];
++ for (_, ref mutbl, ref argspan) in decl
++ .inputs
++ .iter()
++ .filter_map(|ty| get_rptr_lm(ty))
++ .filter(|&(lt, _, _)| lt.name == out.name)
++ {
++ if *mutbl == Mutability::Mut {
++ return;
++ }
++ immutables.push(*argspan);
++ }
++ if immutables.is_empty() {
++ return;
++ }
++ span_lint_and_then(
++ cx,
++ MUT_FROM_REF,
++ ty.span,
++ "mutable borrow from immutable input(s)",
++ |diag| {
++ let ms = MultiSpan::from_spans(immutables);
++ diag.span_note(ms, "immutable borrow here");
++ },
++ );
++ }
++ }
++}
++
++fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
++ if let TyKind::Rptr(ref lt, ref m) = ty.kind {
++ Some((lt, m.mutbl, ty.span))
++ } else {
++ None
++ }
++}
++
++fn is_null_path(expr: &Expr<'_>) -> bool {
++ if let ExprKind::Call(ref pathexp, ref args) = expr.kind {
++ if args.is_empty() {
++ if let ExprKind::Path(ref path) = pathexp.kind {
++ return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
++ }
++ }
++ }
++ false
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
++ /// `isize`.
++ ///
++ /// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
++ /// cast by using the `add` method instead.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let vec = vec![b'a', b'b', b'c'];
++ /// let ptr = vec.as_ptr();
++ /// let offset = 1_usize;
++ ///
++ /// unsafe {
++ /// ptr.offset(offset as isize);
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// let vec = vec![b'a', b'b', b'c'];
++ /// let ptr = vec.as_ptr();
++ /// let offset = 1_usize;
++ ///
++ /// unsafe {
++ /// ptr.add(offset);
++ /// }
++ /// ```
++ pub PTR_OFFSET_WITH_CAST,
++ complexity,
++ "unneeded pointer offset cast"
++}
++
++declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrOffsetWithCast {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
++ let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
++ Some(call_arg) => call_arg,
++ None => return,
++ };
++
++ // Check if the argument to the method call is a cast from usize
++ let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
++ Some(cast_lhs_expr) => cast_lhs_expr,
++ None => return,
++ };
++
++ let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
++ if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
++ span_lint_and_sugg(
++ cx,
++ PTR_OFFSET_WITH_CAST,
++ expr.span,
++ &msg,
++ "try",
++ sugg,
++ Applicability::MachineApplicable,
++ );
++ } else {
++ span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
++ }
++ }
++}
++
++// If the given expression is a cast from a usize, return the lhs of the cast
++fn expr_as_cast_from_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind {
++ if is_expr_ty_usize(cx, &cast_lhs_expr) {
++ return Some(cast_lhs_expr);
++ }
++ }
++ None
++}
++
++// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the
++// receiver, the arg of the method call, and the method.
++fn expr_as_ptr_offset_call<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
++ if let ExprKind::MethodCall(ref path_segment, _, ref args) = expr.kind {
++ if is_expr_ty_raw_ptr(cx, &args[0]) {
++ if path_segment.ident.name == sym!(offset) {
++ return Some((&args[0], &args[1], Method::Offset));
++ }
++ if path_segment.ident.name == sym!(wrapping_offset) {
++ return Some((&args[0], &args[1], Method::WrappingOffset));
++ }
++ }
++ }
++ None
++}
++
++// Is the type of the expression a usize?
++fn is_expr_ty_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++ cx.tables.expr_ty(expr) == cx.tcx.types.usize
++}
++
++// Is the type of the expression a raw pointer?
++fn is_expr_ty_raw_ptr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++ cx.tables.expr_ty(expr).is_unsafe_ptr()
++}
++
++fn build_suggestion<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ method: Method,
++ receiver_expr: &Expr<'_>,
++ cast_lhs_expr: &Expr<'_>,
++) -> Option<String> {
++ let receiver = snippet_opt(cx, receiver_expr.span)?;
++ let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
++ Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
++}
++
++#[derive(Copy, Clone)]
++enum Method {
++ Offset,
++ WrappingOffset,
++}
++
++impl Method {
++ #[must_use]
++ fn suggestion(self) -> &'static str {
++ match self {
++ Self::Offset => "add",
++ Self::WrappingOffset => "wrapping_add",
++ }
++ }
++}
++
++impl fmt::Display for Method {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ match self {
++ Self::Offset => write!(f, "offset"),
++ Self::WrappingOffset => write!(f, "wrapping_offset"),
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{
++ higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
++ span_lint_and_sugg, SpanlessEq,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for expressions that could be replaced by the question mark operator.
++ ///
++ /// **Why is this bad?** Question mark usage is more idiomatic.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// if option.is_none() {
++ /// return None;
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```ignore
++ /// option?;
++ /// ```
++ pub QUESTION_MARK,
++ style,
++ "checks for expressions that could be replaced by the question mark operator"
++}
++
++declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
++
++impl QuestionMark {
++ /// Checks if the given expression on the given context matches the following structure:
++ ///
++ /// ```ignore
++ /// if option.is_none() {
++ /// return None;
++ /// }
++ /// ```
++ ///
++ /// If it matches, it will suggest to use the question mark operator instead
++ fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let Some((if_expr, body, else_)) = higher::if_block(&expr);
++ if let ExprKind::MethodCall(segment, _, args) = &if_expr.kind;
++ if segment.ident.name == sym!(is_none);
++ if Self::expression_returns_none(cx, body);
++ if let Some(subject) = args.get(0);
++ if Self::is_option(cx, subject);
++
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
++ let mut replacement: Option<String> = None;
++ if let Some(else_) = else_ {
++ if_chain! {
++ if let ExprKind::Block(block, None) = &else_.kind;
++ if block.stmts.is_empty();
++ if let Some(block_expr) = &block.expr;
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
++ then {
++ replacement = Some(format!("Some({}?)", receiver_str));
++ }
++ }
++ } else if Self::moves_by_default(cx, subject)
++ && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
++ {
++ replacement = Some(format!("{}.as_ref()?;", receiver_str));
++ } else {
++ replacement = Some(format!("{}?;", receiver_str));
++ }
++
++ if let Some(replacement_str) = replacement {
++ span_lint_and_sugg(
++ cx,
++ QUESTION_MARK,
++ expr.span,
++ "this block may be rewritten with the `?` operator",
++ "replace it with",
++ replacement_str,
++ applicability,
++ )
++ }
++ }
++ }
++ }
++
++ fn check_if_let_some_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Match(subject, arms, source) = &expr.kind;
++ if *source == MatchSource::IfLetDesugar { contains_else_clause: true };
++ if Self::is_option(cx, subject);
++
++ if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
++ if match_qpath(path1, &["Some"]);
++ if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
++ let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
++
++ if let ExprKind::Block(block, None) = &arms[0].body.kind;
++ if block.stmts.is_empty();
++ if let Some(trailing_expr) = &block.expr;
++ if let ExprKind::Path(path) = &trailing_expr.kind;
++ if match_qpath(path, &[&bind.as_str()]);
++
++ if let PatKind::Wild = arms[1].pat.kind;
++ if Self::expression_returns_none(cx, arms[1].body);
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability);
++ let replacement = format!(
++ "{}{}?",
++ receiver_str,
++ if by_ref { ".as_ref()" } else { "" },
++ );
++
++ span_lint_and_sugg(
++ cx,
++ QUESTION_MARK,
++ expr.span,
++ "this if-let-else may be rewritten with the `?` operator",
++ "replace it with",
++ replacement,
++ applicability,
++ )
++ }
++ }
++ }
++
++ fn moves_by_default(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++ let expr_ty = cx.tables.expr_ty(expression);
++
++ !expr_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, expression.span)
++ }
++
++ fn is_option(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++ let expr_ty = cx.tables.expr_ty(expression);
++
++ is_type_diagnostic_item(cx, expr_ty, sym!(option_type))
++ }
++
++ fn expression_returns_none(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++ match expression.kind {
++ ExprKind::Block(ref block, _) => {
++ if let Some(return_expression) = Self::return_expression(block) {
++ return Self::expression_returns_none(cx, &return_expression);
++ }
++
++ false
++ },
++ ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr),
++ ExprKind::Path(ref qp) => {
++ if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
++ cx.tables.qpath_res(qp, expression.hir_id)
++ {
++ return match_def_path(cx, def_id, &paths::OPTION_NONE);
++ }
++
++ false
++ },
++ _ => false,
++ }
++ }
++
++ fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ // Check if last expression is a return statement. Then, return the expression
++ if_chain! {
++ if block.stmts.len() == 1;
++ if let Some(expr) = block.stmts.iter().last();
++ if let StmtKind::Semi(ref expr) = expr.kind;
++ if let ExprKind::Ret(ret_expr) = expr.kind;
++ if let Some(ret_expr) = ret_expr;
++
++ then {
++ return Some(ret_expr);
++ }
++ }
++
++ // Check for `return` without a semicolon.
++ if_chain! {
++ if block.stmts.is_empty();
++ if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
++ then {
++ return Some(ret_expr);
++ }
++ }
++
++ None
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for QuestionMark {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ Self::check_is_none_and_early_return_none(cx, expr);
++ Self::check_if_let_some_and_early_return_none(cx, expr);
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_ast::ast::RangeLimits;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{higher, SpanlessEq};
++use crate::utils::{is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then};
++
++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.
++ ///
++ /// **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:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// for x..=(y-1) { .. }
++ /// ```
++ /// Could be written as
++ /// ```rust,ignore
++ /// for x..y { .. }
++ /// ```
++ pub RANGE_MINUS_ONE,
++ complexity,
++ "`x..=(y-1)` reads better as `x..y`"
++}
++
++declare_lint_pass!(Ranges => [
++ RANGE_ZIP_WITH_LEN,
++ RANGE_PLUS_ONE,
++ RANGE_MINUS_ONE
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind {
++ let name = path.ident.as_str();
++ if name == "zip" && args.len() == 2 {
++ let iter = &args[0].kind;
++ let zip_arg = &args[1];
++ if_chain! {
++ // `.iter()` call
++ if let ExprKind::MethodCall(ref iter_path, _, ref iter_args ) = *iter;
++ 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(cx, zip_arg);
++ if is_integer_const(cx, start, 0);
++ // `.len()` call
++ if let ExprKind::MethodCall(ref len_path, _, ref 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(_, ref iter_path)) = iter_args[0].kind;
++ if let ExprKind::Path(QPath::Resolved(_, ref 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,
++ expr.span,
++ &format!("It is more idiomatic to use `{}.iter().enumerate()`",
++ snippet(cx, iter_args[0].span, "_")));
++ }
++ }
++ }
++ }
++
++ check_exclusive_range_plus_one(cx, expr);
++ check_inclusive_range_minus_one(cx, expr);
++ }
++}
++
++// 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(cx, 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(cx, 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 y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
++ match expr.kind {
++ ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ ref lhs,
++ ref 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, ..
++ },
++ ref lhs,
++ ref rhs,
++ ) if is_integer_const(cx, rhs, 1) => Some(lhs),
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, match_type, paths,
++ snippet_opt, span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth,
++};
++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::BottomValue;
++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 std::convert::TryFrom;
++
++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<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
++ #[allow(clippy::too_many_lines)]
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, '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, def_id.to_def_id())
++ .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;
++ }
++
++ // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
++ 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 match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
++ if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
++ || match_type(cx, pred_arg_ty, &paths::OS_STRING);
++ 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 is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp;
++
++ // 1. `local` can be moved out if it is not used later.
++ // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone`
++ // call anyway.
++ let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold(
++ (false, !is_temp),
++ |(used, consumed), (tbb, tdata)| {
++ // Short-circuit
++ if (used && consumed) ||
++ // Give up on loops
++ tdata.terminator().successors().any(|s| *s == bb)
++ {
++ return (true, true);
++ }
++
++ let mut vis = LocalUseVisitor {
++ used: (local, false),
++ consumed_or_mutated: (ret_local, false),
++ };
++ vis.visit_basic_block_data(tbb, tdata);
++ (used || vis.used.1, consumed || vis.consumed_or_mutated.1)
++ },
++ );
++
++ if !used || !consumed_or_mutated {
++ 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 mut call_snip = &snip[dot + 1..];
++ // Machine applicable when `call_snip` looks like `foobar()`
++ if call_snip.ends_with("()") {
++ call_snip = call_snip[..call_snip.len()-2].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 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))) => {
++ base_local_and_movability(cx, mir, *place)
++ },
++ (false, mir::Rvalue::Ref(_, _, place)) => {
++ if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
++ 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>,
++) -> Option<(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);
++ }
++
++ Some((local, deref || field || slice))
++}
++
++struct LocalUseVisitor {
++ used: (mir::Local, bool),
++ consumed_or_mutated: (mir::Local, bool),
++}
++
++impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
++ 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, _: mir::Location) {
++ let local = place.local;
++
++ if local == self.used.0
++ && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_))
++ {
++ self.used.1 = true;
++ }
++
++ if local == self.consumed_or_mutated.0 {
++ match ctx {
++ PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
++ | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
++ self.consumed_or_mutated.1 = true;
++ },
++ _ => {},
++ }
++ }
++ }
++}
++
++/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
++#[derive(Copy, Clone)]
++struct MaybeStorageLive;
++
++impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
++ type Idx = mir::Local;
++ const NAME: &'static str = "maybe_storage_live";
++
++ fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
++ body.local_decls.len()
++ }
++
++ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
++ for arg in body.args_iter() {
++ state.insert(arg);
++ }
++ }
++}
++
++impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
++ 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
++ }
++}
++
++impl BottomValue for MaybeStorageLive {
++ /// bottom = dead
++ const BOTTOM_VALUE: bool = false;
++}
++
++/// 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<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
++ fn new(cx: &'a LateContext<'a, 'tcx>, body: &'a mir::Body<'tcx>) -> Self {
++ Self {
++ possible_borrower: TransitiveRelation::default(),
++ cx,
++ body,
++ }
++ }
++
++ fn into_map(
++ self,
++ cx: &LateContext<'a, '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) {
++ 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) {
++ return;
++ }
++
++ for op in args {
++ match op {
++ mir::Operand::Copy(p) | mir::Operand::Move(p) => {
++ self.possible_borrower.add(p.local, *dest);
++ },
++ _ => (),
++ }
++ }
++ }
++ }
++}
++
++struct ContainsRegion;
++
++impl TypeVisitor<'_> for ContainsRegion {
++ fn visit_region(&mut self, _: ty::Region<'_>) -> bool {
++ true
++ }
++}
++
++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),
++ _ => (),
++ };
++
++ match rvalue {
++ Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
++ Aggregate(_, ops) => ops.iter().for_each(visit_op),
++ BinaryOp(_, lhs, rhs) | CheckedBinaryOp(_, 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(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
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint_and_sugg;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for fields in struct literals where shorthands
++ /// could be used.
++ ///
++ /// **Why is this bad?** If the field and variable names are the same,
++ /// the field name is redundant.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let bar: u8 = 123;
++ ///
++ /// struct Foo {
++ /// bar: u8,
++ /// }
++ ///
++ /// let foo = Foo { bar: bar };
++ /// ```
++ /// the last line can be simplified to
++ /// ```ignore
++ /// let foo = Foo { bar };
++ /// ```
++ pub REDUNDANT_FIELD_NAMES,
++ style,
++ "checks for fields in struct literals where shorthands could be used"
++}
++
++declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
++
++impl EarlyLintPass for RedundantFieldNames {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++ if let ExprKind::Struct(_, ref fields, _) = expr.kind {
++ for field in fields {
++ if field.is_shorthand {
++ continue;
++ }
++ if let ExprKind::Path(None, path) = &field.expr.kind {
++ if path.segments.len() == 1
++ && path.segments[0].ident == field.ident
++ && path.segments[0].args.is_none()
++ {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_FIELD_NAMES,
++ field.span,
++ "redundant field names in struct initialization",
++ "replace it with",
++ field.ident.to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Lint for redundant pattern matching over `Result` or
++ /// `Option`
++ ///
++ /// **Why is this bad?** It's more concise and clear to just use the proper
++ /// utility function
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// if let Ok(_) = Ok::<i32, i32>(42) {}
++ /// if let Err(_) = Err::<i32, i32>(42) {}
++ /// if let None = None::<()> {}
++ /// if let Some(_) = Some(42) {}
++ /// match Ok::<i32, i32>(42) {
++ /// Ok(_) => true,
++ /// Err(_) => false,
++ /// };
++ /// ```
++ ///
++ /// The more idiomatic use would be:
++ ///
++ /// ```rust
++ /// if Ok::<i32, i32>(42).is_ok() {}
++ /// if Err::<i32, i32>(42).is_err() {}
++ /// if None::<()>.is_none() {}
++ /// if Some(42).is_some() {}
++ /// Ok::<i32, i32>(42).is_ok();
++ /// ```
++ pub REDUNDANT_PATTERN_MATCHING,
++ style,
++ "use the proper utility function avoiding an `if let`"
++}
++
++declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPatternMatching {
++ fn check_expr(&mut self, cx: &LateContext<'a, '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 { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
++ MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
++ _ => return,
++ }
++ }
++ }
++}
++
++fn find_sugg_for_if_let<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++ op: &Expr<'_>,
++ arms: &[Arm<'_>],
++ keyword: &'static str,
++) {
++ let good_method = match arms[0].pat.kind {
++ PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
++ if let PatKind::Wild = patterns[0].kind {
++ if match_qpath(path, &paths::RESULT_OK) {
++ "is_ok()"
++ } else if match_qpath(path, &paths::RESULT_ERR) {
++ "is_err()"
++ } else if match_qpath(path, &paths::OPTION_SOME) {
++ "is_some()"
++ } else {
++ return;
++ }
++ } else {
++ return;
++ }
++ },
++
++ PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()",
++
++ _ => return,
++ };
++
++ // 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 match_trait_method(cx, op, &paths::ITERATOR);
++ then {
++ return;
++ }
++ }
++
++ span_lint_and_then(
++ cx,
++ REDUNDANT_PATTERN_MATCHING,
++ arms[0].pat.span,
++ &format!("redundant pattern matching, consider using `{}`", good_method),
++ |diag| {
++ // while let ... = ... { ... }
++ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ let expr_span = expr.span;
++
++ // while let ... = ... { ... }
++ // ^^^
++ let op_span = op.span.source_callsite();
++
++ // while let ... = ... { ... }
++ // ^^^^^^^^^^^^^^^^^^^
++ let span = expr_span.until(op_span.shrink_to_hi());
++ diag.span_suggestion(
++ span,
++ "try this",
++ format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
++ Applicability::MachineApplicable, // snippet
++ );
++ },
++ );
++}
++
++fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, '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, ref patterns_left, _),
++ PatKind::TupleStruct(ref path_right, ref 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(
++ arms,
++ path_left,
++ path_right,
++ &paths::RESULT_OK,
++ &paths::RESULT_ERR,
++ "is_ok()",
++ "is_err()",
++ )
++ } else {
++ None
++ }
++ },
++ (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
++ | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
++ if patterns.len() == 1 =>
++ {
++ if let PatKind::Wild = patterns[0].kind {
++ find_good_method_for_match(
++ arms,
++ path_left,
++ path_right,
++ &paths::OPTION_SOME,
++ &paths::OPTION_NONE,
++ "is_some()",
++ "is_none()",
++ )
++ } else {
++ None
++ }
++ },
++ _ => None,
++ };
++
++ if let Some(good_method) = found_good_method {
++ span_lint_and_then(
++ cx,
++ REDUNDANT_PATTERN_MATCHING,
++ expr.span,
++ &format!("redundant pattern matching, consider using `{}`", good_method),
++ |diag| {
++ let span = expr.span.to(op.span);
++ diag.span_suggestion(
++ span,
++ "try this",
++ format!("{}.{}", snippet(cx, op.span, "_"), good_method),
++ Applicability::MaybeIncorrect, // snippet
++ );
++ },
++ );
++ }
++ }
++}
++
++fn find_good_method_for_match<'a>(
++ 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 match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
++ (&(*arms[0].body).kind, &(*arms[1].body).kind)
++ } else if match_qpath(path_right, expected_left) && match_qpath(path_left, 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,
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint_and_then;
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind, VisibilityKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they
++ /// are inside a private module.
++ ///
++ /// **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent
++ /// module's visibility.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// mod internal {
++ /// pub(crate) fn internal_fn() { }
++ /// }
++ /// ```
++ /// This function is not visible outside the module and it can be declared with `pub` or
++ /// private visibility
++ /// ```rust
++ /// mod internal {
++ /// pub fn internal_fn() { }
++ /// }
++ /// ```
++ pub REDUNDANT_PUB_CRATE,
++ nursery,
++ "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
++}
++
++#[derive(Default)]
++pub struct RedundantPubCrate {
++ is_exported: Vec<bool>,
++}
++
++impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPubCrate {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) {
++ if let VisibilityKind::Crate { .. } = item.vis.node {
++ if !cx.access_levels.is_exported(item.hir_id) {
++ if let Some(false) = self.is_exported.last() {
++ let span = item.span.with_hi(item.ident.span.hi());
++ let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ let descr = cx.tcx.def_kind(def_id).descr(def_id.to_def_id());
++ span_lint_and_then(
++ cx,
++ REDUNDANT_PUB_CRATE,
++ span,
++ &format!("pub(crate) {} inside private module", descr),
++ |diag| {
++ diag.span_suggestion(
++ item.vis.span,
++ "consider using",
++ "pub".to_string(),
++ Applicability::MachineApplicable,
++ );
++ },
++ )
++ }
++ }
++ }
++
++ if let ItemKind::Mod { .. } = item.kind {
++ self.is_exported.push(cx.access_levels.is_exported(item.hir_id));
++ }
++ }
++
++ fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) {
++ if let ItemKind::Mod { .. } = item.kind {
++ self.is_exported.pop().expect("unbalanced check_item/check_item_post");
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{snippet, span_lint_and_then};
++use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
++ ///
++ /// **Why is this bad?** Adding `'static` to every reference can create very
++ /// complicated types.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++ /// &[...]
++ /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++ /// &[...]
++ /// ```
++ /// This code can be rewritten as
++ /// ```ignore
++ /// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++ /// static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++ /// ```
++ pub REDUNDANT_STATIC_LIFETIMES,
++ style,
++ "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
++}
++
++declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
++
++impl RedundantStaticLifetimes {
++ // Recursively visit types
++ fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
++ match ty.kind {
++ // Be careful of nested structures (arrays and tuples)
++ TyKind::Array(ref ty, _) => {
++ self.visit_type(&*ty, cx, reason);
++ },
++ TyKind::Tup(ref tup) => {
++ for tup_ty in tup {
++ self.visit_type(&*tup_ty, cx, reason);
++ }
++ },
++ // This is what we are looking for !
++ TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
++ // Match the 'static lifetime
++ if let Some(lifetime) = *optional_lifetime {
++ match borrow_type.ty.kind {
++ TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
++ if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
++ let snip = snippet(cx, borrow_type.ty.span, "<type>");
++ let sugg = format!("&{}", snip);
++ span_lint_and_then(
++ cx,
++ REDUNDANT_STATIC_LIFETIMES,
++ lifetime.ident.span,
++ reason,
++ |diag| {
++ diag.span_suggestion(
++ ty.span,
++ "consider removing `'static`",
++ sugg,
++ Applicability::MachineApplicable, //snippet
++ );
++ },
++ );
++ }
++ },
++ _ => {},
++ }
++ }
++ self.visit_type(&*borrow_type.ty, cx, reason);
++ },
++ TyKind::Slice(ref ty) => {
++ self.visit_type(ty, cx, reason);
++ },
++ _ => {},
++ }
++ }
++}
++
++impl EarlyLintPass for RedundantStaticLifetimes {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ if !item.span.from_expansion() {
++ if let ItemKind::Const(_, ref var_type, _) = item.kind {
++ self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
++ // Don't check associated consts because `'static` cannot be elided on those (issue
++ // #2438)
++ }
++
++ if let ItemKind::Static(ref var_type, _, _) = item.kind {
++ self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime");
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{Expr, ExprKind, UnOp};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions.
++ ///
++ /// **Why is this bad?** Immediately dereferencing a reference is no-op and
++ /// makes the code less clear.
++ ///
++ /// **Known problems:** Multiple dereference/addrof pairs are not handled so
++ /// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// let a = f(*&mut b);
++ /// let c = *&d;
++ /// ```
++ pub DEREF_ADDROF,
++ complexity,
++ "use of `*&` or `*&mut` in an expression"
++}
++
++declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
++
++fn without_parens(mut e: &Expr) -> &Expr {
++ while let ExprKind::Paren(ref child_e) = e.kind {
++ e = child_e;
++ }
++ e
++}
++
++impl EarlyLintPass for DerefAddrOf {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++ if_chain! {
++ if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
++ if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind;
++ if !in_macro(addrof_target.span);
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ DEREF_ADDROF,
++ e.span,
++ "immediately dereferencing a reference",
++ "try this",
++ format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)),
++ applicability,
++ );
++ }
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for references in expressions that use
++ /// auto dereference.
++ ///
++ /// **Why is this bad?** The reference is a no-op and is automatically
++ /// dereferenced by the compiler and makes the code less clear.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// struct Point(u32, u32);
++ /// let point = Point(30, 20);
++ /// let x = (&point).0;
++ /// ```
++ pub REF_IN_DEREF,
++ complexity,
++ "Use of reference in auto dereference expression."
++}
++
++declare_lint_pass!(RefInDeref => [REF_IN_DEREF]);
++
++impl EarlyLintPass for RefInDeref {
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++ if_chain! {
++ if let ExprKind::Field(ref object, _) = e.kind;
++ if let ExprKind::Paren(ref parened) = object.kind;
++ if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ REF_IN_DEREF,
++ object.span,
++ "Creating a reference that is immediately dereferenced.",
++ "try this",
++ snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
++ applicability,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant, Constant};
++use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::{LitKind, StrStyle};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{BytePos, Span};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
++ /// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
++ /// regex syntax.
++ ///
++ /// **Why is this bad?** This will lead to a runtime panic.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// Regex::new("|")
++ /// ```
++ pub INVALID_REGEX,
++ correctness,
++ "invalid regular expressions"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex)
++ /// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`).
++ ///
++ /// **Why is this bad?** Matching the regex can likely be replaced by `==` or
++ /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
++ /// methods.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// Regex::new("^foobar")
++ /// ```
++ pub TRIVIAL_REGEX,
++ style,
++ "trivial regular expressions"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
++ /// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
++ /// idea anyway).
++ ///
++ /// **Why is this bad?** Performance, at least for now. The macro version is
++ /// likely to catch up long-term, but for now the dynamic version is faster.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// regex!("foo|bar")
++ /// ```
++ pub REGEX_MACRO,
++ style,
++ "use of `regex!(_)` instead of `Regex::new(_)`"
++}
++
++#[derive(Clone, Default)]
++pub struct Regex {
++ spans: FxHashSet<Span>,
++ last: Option<HirId>,
++}
++
++impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex {
++ fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) {
++ self.spans.clear();
++ }
++
++ fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++ if_chain! {
++ if self.last.is_none();
++ if let Some(ref expr) = block.expr;
++ if match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX);
++ if let Some(span) = is_expn_of(expr.span, "regex");
++ then {
++ if !self.spans.contains(&span) {
++ span_lint(cx,
++ REGEX_MACRO,
++ span,
++ "`regex!(_)` found. \
++ Please use `Regex::new(_)`, which is faster for now.");
++ self.spans.insert(span);
++ }
++ self.last = Some(block.hir_id);
++ }
++ }
++ }
++
++ fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++ if self.last.map_or(false, |id| block.hir_id == id) {
++ self.last = None;
++ }
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref fun, ref args) = expr.kind;
++ if let ExprKind::Path(ref qpath) = fun.kind;
++ if args.len() == 1;
++ if let Some(def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
++ then {
++ if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
++ match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
++ check_regex(cx, &args[0], true);
++ } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
++ match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
++ check_regex(cx, &args[0], false);
++ } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
++ check_set(cx, &args[0], true);
++ } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
++ check_set(cx, &args[0], false);
++ }
++ }
++ }
++ }
++}
++
++#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here
++#[must_use]
++fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span {
++ let offset = u32::from(offset);
++ let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
++ let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset);
++ assert!(start <= end);
++ Span::new(start, end, base.ctxt())
++}
++
++fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
++ constant(cx, cx.tables, e).and_then(|(c, _)| match c {
++ Constant::Str(s) => Some(s),
++ _ => None,
++ })
++}
++
++fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> {
++ use regex_syntax::hir::Anchor::{EndText, StartText};
++ use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
++
++ let is_literal = |e: &[regex_syntax::hir::Hir]| {
++ e.iter().all(|e| match *e.kind() {
++ Literal(_) => true,
++ _ => false,
++ })
++ };
++
++ match *s.kind() {
++ Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
++ Literal(_) => Some("consider using `str::contains`"),
++ Alternation(ref exprs) => {
++ if exprs.iter().all(|e| e.kind().is_empty()) {
++ Some("the regex is unlikely to be useful as it is")
++ } else {
++ None
++ }
++ },
++ Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) {
++ (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => {
++ Some("consider using `str::is_empty`")
++ },
++ (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
++ Some("consider using `==` on `str`s")
++ },
++ (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"),
++ (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
++ Some("consider using `str::ends_with`")
++ },
++ _ if is_literal(exprs) => Some("consider using `str::contains`"),
++ _ => None,
++ },
++ _ => None,
++ }
++}
++
++fn check_set<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
++ if_chain! {
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind;
++ if let ExprKind::Array(exprs) = expr.kind;
++ then {
++ for expr in exprs {
++ check_regex(cx, expr, utf8);
++ }
++ }
++ }
++}
++
++fn check_regex<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
++ let mut parser = regex_syntax::ParserBuilder::new()
++ .unicode(utf8)
++ .allow_invalid_utf8(!utf8)
++ .build();
++
++ if let ExprKind::Lit(ref lit) = expr.kind {
++ if let LitKind::Str(ref r, style) = lit.node {
++ let r = &r.as_str();
++ let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };
++ match parser.parse(r) {
++ Ok(r) => {
++ if let Some(repl) = is_trivial_regex(&r) {
++ span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
++ }
++ },
++ Err(regex_syntax::Error::Parse(e)) => {
++ span_lint(
++ cx,
++ INVALID_REGEX,
++ str_span(expr.span, *e.span(), offset),
++ &format!("regex syntax error: {}", e.kind()),
++ );
++ },
++ Err(regex_syntax::Error::Translate(e)) => {
++ span_lint(
++ cx,
++ INVALID_REGEX,
++ str_span(expr.span, *e.span(), offset),
++ &format!("regex syntax error: {}", e.kind()),
++ );
++ },
++ Err(e) => {
++ span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
++ },
++ }
++ }
++ } else if let Some(r) = const_str(cx, expr) {
++ match parser.parse(&r) {
++ Ok(r) => {
++ if let Some(repl) = is_trivial_regex(&r) {
++ span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
++ }
++ },
++ Err(regex_syntax::Error::Parse(e)) => {
++ span_lint(
++ cx,
++ INVALID_REGEX,
++ expr.span,
++ &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
++ );
++ },
++ Err(regex_syntax::Error::Translate(e)) => {
++ span_lint(
++ cx,
++ INVALID_REGEX,
++ expr.span,
++ &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
++ );
++ },
++ Err(e) => {
++ span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
++ },
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_ast::visit::FnKind;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::BytePos;
++
++use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for return statements at the end of a block.
++ ///
++ /// **Why is this bad?** Removing the `return` and semicolon will make the code
++ /// more rusty.
++ ///
++ /// **Known problems:** If the computation returning the value borrows a local
++ /// variable, removing the `return` may run afoul of the borrow checker.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(x: usize) -> usize {
++ /// return x;
++ /// }
++ /// ```
++ /// simplify to
++ /// ```rust
++ /// fn foo(x: usize) -> usize {
++ /// x
++ /// }
++ /// ```
++ pub NEEDLESS_RETURN,
++ style,
++ "using a return statement like `return expr;` where an expression would suffice"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `let`-bindings, which are subsequently
++ /// returned.
++ ///
++ /// **Why is this bad?** It is just extraneous code. Remove it to make your code
++ /// more rusty.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo() -> String {
++ /// let x = String::new();
++ /// x
++ /// }
++ /// ```
++ /// instead, use
++ /// ```
++ /// fn foo() -> String {
++ /// String::new()
++ /// }
++ /// ```
++ pub LET_AND_RETURN,
++ style,
++ "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for unit (`()`) expressions that can be removed.
++ ///
++ /// **Why is this bad?** Such expressions add no value, but can make the code
++ /// less readable. Depending on formatting they can make a `break` or `return`
++ /// statement look like a function call.
++ ///
++ /// **Known problems:** The lint currently misses unit return types in types,
++ /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn return_unit() -> () {
++ /// ()
++ /// }
++ /// ```
++ pub UNUSED_UNIT,
++ style,
++ "needless unit expression"
++}
++
++#[derive(PartialEq, Eq, Copy, Clone)]
++enum RetReplacement {
++ Empty,
++ Block,
++}
++
++declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);
++
++impl Return {
++ // Check the final stmt or expr in a block for unnecessary return.
++ fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
++ if let Some(stmt) = block.stmts.last() {
++ match stmt.kind {
++ ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
++ self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
++ },
++ _ => (),
++ }
++ }
++ }
++
++ // Check a the final expression in a block if it's a return.
++ fn check_final_expr(
++ &mut self,
++ cx: &EarlyContext<'_>,
++ expr: &ast::Expr,
++ span: Option<Span>,
++ replacement: RetReplacement,
++ ) {
++ match expr.kind {
++ // simple return is always "bad"
++ ast::ExprKind::Ret(ref inner) => {
++ // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
++ if !expr.attrs.iter().any(attr_is_cfg) {
++ Self::emit_return_lint(
++ cx,
++ span.expect("`else return` is not possible"),
++ inner.as_ref().map(|i| i.span),
++ replacement,
++ );
++ }
++ },
++ // a whole block? check it!
++ ast::ExprKind::Block(ref block, _) => {
++ self.check_block_return(cx, block);
++ },
++ // an if/if let expr, check both exprs
++ // note, if without else is going to be a type checking error anyways
++ // (except for unit type functions) so we don't match it
++ ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
++ self.check_block_return(cx, ifblock);
++ self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
++ },
++ // a match expr, check all arms
++ ast::ExprKind::Match(_, ref arms) => {
++ for arm in arms {
++ self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
++ }
++ },
++ _ => (),
++ }
++ }
++
++ fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
++ match inner_span {
++ Some(inner_span) => {
++ if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
++ return;
++ }
++
++ span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
++ if let Some(snippet) = snippet_opt(cx, inner_span) {
++ diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
++ }
++ })
++ },
++ None => match replacement {
++ RetReplacement::Empty => {
++ span_lint_and_sugg(
++ cx,
++ NEEDLESS_RETURN,
++ ret_span,
++ "unneeded `return` statement",
++ "remove `return`",
++ String::new(),
++ Applicability::MachineApplicable,
++ );
++ },
++ RetReplacement::Block => {
++ span_lint_and_sugg(
++ cx,
++ NEEDLESS_RETURN,
++ ret_span,
++ "unneeded `return` statement",
++ "replace `return` with an empty block",
++ "{}".to_string(),
++ Applicability::MachineApplicable,
++ );
++ },
++ },
++ }
++ }
++
++ // Check for "let x = EXPR; x"
++ fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) {
++ let mut it = block.stmts.iter();
++
++ // we need both a let-binding stmt and an expr
++ if_chain! {
++ if let Some(retexpr) = it.next_back();
++ if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind;
++ if let Some(stmt) = it.next_back();
++ if let ast::StmtKind::Local(ref local) = stmt.kind;
++ // don't lint in the presence of type inference
++ if local.ty.is_none();
++ if local.attrs.is_empty();
++ if let Some(ref initexpr) = local.init;
++ if let ast::PatKind::Ident(_, ident, _) = local.pat.kind;
++ if let ast::ExprKind::Path(_, ref path) = retexpr.kind;
++ if match_path_ast(path, &[&*ident.name.as_str()]);
++ if !in_external_macro(cx.sess(), initexpr.span);
++ if !in_external_macro(cx.sess(), retexpr.span);
++ if !in_external_macro(cx.sess(), local.span);
++ if !in_macro(local.span);
++ then {
++ span_lint_and_then(
++ cx,
++ LET_AND_RETURN,
++ retexpr.span,
++ "returning the result of a `let` binding from a block",
++ |err| {
++ err.span_label(local.span, "unnecessary `let` binding");
++
++ if let Some(snippet) = snippet_opt(cx, initexpr.span) {
++ err.multipart_suggestion(
++ "return the expression directly",
++ vec![
++ (local.span, String::new()),
++ (retexpr.span, snippet),
++ ],
++ Applicability::MachineApplicable,
++ );
++ } else {
++ err.span_help(initexpr.span, "this expression can be directly returned");
++ }
++ },
++ );
++ }
++ }
++ }
++}
++
++impl EarlyLintPass for Return {
++ fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
++ match kind {
++ FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
++ FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
++ FnKind::Fn(.., None) => {},
++ }
++ if_chain! {
++ if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
++ if let ast::TyKind::Tup(ref vals) = ty.kind;
++ if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
++ then {
++ let (rspan, appl) = if let Ok(fn_source) =
++ cx.sess().source_map()
++ .span_to_snippet(span.with_hi(ty.span.hi())) {
++ if let Some(rpos) = fn_source.rfind("->") {
++ #[allow(clippy::cast_possible_truncation)]
++ (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
++ Applicability::MachineApplicable)
++ } else {
++ (ty.span, Applicability::MaybeIncorrect)
++ }
++ } else {
++ (ty.span, Applicability::MaybeIncorrect)
++ };
++ span_lint_and_sugg(
++ cx,
++ UNUSED_UNIT,
++ rspan,
++ "unneeded unit return type",
++ "remove the `-> ()`",
++ String::new(),
++ appl,
++ );
++ }
++ }
++ }
++
++ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
++ Self::check_let_return(cx, block);
++ if_chain! {
++ if let Some(ref stmt) = block.stmts.last();
++ if let ast::StmtKind::Expr(ref expr) = stmt.kind;
++ if is_unit_expr(expr) && !stmt.span.from_expansion();
++ then {
++ let sp = expr.span;
++ span_lint_and_sugg(
++ cx,
++ UNUSED_UNIT,
++ sp,
++ "unneeded unit expression",
++ "remove the final `()`",
++ String::new(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++
++ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
++ match e.kind {
++ ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
++ if is_unit_expr(expr) && !expr.span.from_expansion() {
++ span_lint_and_sugg(
++ cx,
++ UNUSED_UNIT,
++ expr.span,
++ "unneeded `()`",
++ "remove the `()`",
++ String::new(),
++ Applicability::MachineApplicable,
++ );
++ }
++ },
++ _ => (),
++ }
++ }
++}
++
++fn attr_is_cfg(attr: &ast::Attribute) -> bool {
++ attr.meta_item_list().is_some() && attr.check_name(sym!(cfg))
++}
++
++// get the def site
++#[must_use]
++fn get_def(span: Span) -> Option<Span> {
++ if span.from_expansion() {
++ Some(span.ctxt().outer_expn_data().def_site)
++ } else {
++ None
++ }
++}
++
++// is this expr a `()` unit?
++fn is_unit_expr(expr: &ast::Expr) -> bool {
++ if let ast::ExprKind::Tup(ref vals) = expr.kind {
++ vals.is_empty()
++ } else {
++ false
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_trait_def_id, paths, span_lint};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for mis-uses of the serde API.
++ ///
++ /// **Why is this bad?** Serde is very finnicky about how its API should be
++ /// used, but the type system can't be used to enforce it (yet?).
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:** Implementing `Visitor::visit_string` but not
++ /// `Visitor::visit_str`.
++ pub SERDE_API_MISUSE,
++ correctness,
++ "various things that will negatively affect your serde experience"
++}
++
++declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SerdeAPI {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if let ItemKind::Impl {
++ of_trait: Some(ref trait_ref),
++ items,
++ ..
++ } = item.kind
++ {
++ let did = trait_ref.path.res.def_id();
++ if let Some(visit_did) = get_trait_def_id(cx, &paths::SERDE_DE_VISITOR) {
++ if did == visit_did {
++ let mut seen_str = None;
++ let mut seen_string = None;
++ for item in items {
++ match &*item.ident.as_str() {
++ "visit_str" => seen_str = Some(item.span),
++ "visit_string" => seen_string = Some(item.span),
++ _ => {},
++ }
++ }
++ if let Some(span) = seen_string {
++ if seen_str.is_none() {
++ span_lint(
++ cx,
++ SERDE_API_MISUSE,
++ span,
++ "you should not implement `visit_string` without also implementing `visit_str`",
++ );
++ }
++ }
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::reexport::Name;
++use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{
++ Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
++ UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for bindings that shadow other bindings already in
++ /// scope, while just changing reference level or mutability.
++ ///
++ /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust
++ /// code. Still, some may opt to avoid it in their code base, they can set this
++ /// lint to `Warn`.
++ ///
++ /// **Known problems:** This lint, as the other shadowing related lints,
++ /// currently only catches very simple patterns.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let x = 1;
++ /// let x = &x;
++ /// ```
++ pub SHADOW_SAME,
++ restriction,
++ "rebinding a name to itself, e.g., `let mut x = &mut x`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for bindings that shadow other bindings already in
++ /// scope, while reusing the original value.
++ ///
++ /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust
++ /// code. Still, some argue that name shadowing like this hurts readability,
++ /// because a value may be bound to different things depending on position in
++ /// the code.
++ ///
++ /// **Known problems:** This lint, as the other shadowing related lints,
++ /// currently only catches very simple patterns.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 2;
++ /// let x = x + 1;
++ /// ```
++ /// use different variable name:
++ /// ```rust
++ /// let x = 2;
++ /// let y = x + 1;
++ /// ```
++ pub SHADOW_REUSE,
++ restriction,
++ "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for bindings that shadow other bindings already in
++ /// scope, either without a initialization or with one that does not even use
++ /// the original value.
++ ///
++ /// **Why is this bad?** Name shadowing can hurt readability, especially in
++ /// large code bases, because it is easy to lose track of the active binding at
++ /// any place in the code. This can be alleviated by either giving more specific
++ /// names to bindings or introducing more scopes to contain the bindings.
++ ///
++ /// **Known problems:** This lint, as the other shadowing related lints,
++ /// currently only catches very simple patterns.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let y = 1;
++ /// # let z = 2;
++ /// let x = y;
++ /// let x = z; // shadows the earlier binding
++ /// ```
++ pub SHADOW_UNRELATED,
++ pedantic,
++ "rebinding a name without even using the original value"
++}
++
++declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Shadow {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ _: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ _: Span,
++ _: HirId,
++ ) {
++ if in_external_macro(cx.sess(), body.value.span) {
++ return;
++ }
++ check_fn(cx, decl, body);
++ }
++}
++
++fn check_fn<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) {
++ let mut bindings = Vec::with_capacity(decl.inputs.len());
++ for arg in iter_input_pats(decl, body) {
++ if let PatKind::Binding(.., ident, _) = arg.pat.kind {
++ bindings.push((ident.name, ident.span))
++ }
++ }
++ check_expr(cx, &body.value, &mut bindings);
++}
++
++fn check_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) {
++ let len = bindings.len();
++ for stmt in block.stmts {
++ match stmt.kind {
++ StmtKind::Local(ref local) => check_local(cx, local, bindings),
++ StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings),
++ StmtKind::Item(..) => {},
++ }
++ }
++ if let Some(ref o) = block.expr {
++ check_expr(cx, o, bindings);
++ }
++ bindings.truncate(len);
++}
++
++fn check_local<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) {
++ if in_external_macro(cx.sess(), local.span) {
++ return;
++ }
++ if higher::is_from_for_desugar(local) {
++ return;
++ }
++ let Local {
++ ref pat,
++ ref ty,
++ ref init,
++ span,
++ ..
++ } = *local;
++ if let Some(ref t) = *ty {
++ check_ty(cx, t, bindings)
++ }
++ if let Some(ref o) = *init {
++ check_expr(cx, o, bindings);
++ check_pat(cx, pat, Some(o), span, bindings);
++ } else {
++ check_pat(cx, pat, None, span, bindings);
++ }
++}
++
++fn is_binding(cx: &LateContext<'_, '_>, pat_id: HirId) -> bool {
++ let var_ty = cx.tables.node_type_opt(pat_id);
++ if let Some(var_ty) = var_ty {
++ match var_ty.kind {
++ ty::Adt(..) => false,
++ _ => true,
++ }
++ } else {
++ false
++ }
++}
++
++fn check_pat<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ pat: &'tcx Pat<'_>,
++ init: Option<&'tcx Expr<'_>>,
++ span: Span,
++ bindings: &mut Vec<(Name, Span)>,
++) {
++ // TODO: match more stuff / destructuring
++ match pat.kind {
++ PatKind::Binding(.., ident, ref inner) => {
++ let name = ident.name;
++ if is_binding(cx, pat.hir_id) {
++ let mut new_binding = true;
++ for tup in bindings.iter_mut() {
++ if tup.0 == name {
++ lint_shadow(cx, name, span, pat.span, init, tup.1);
++ tup.1 = ident.span;
++ new_binding = false;
++ break;
++ }
++ }
++ if new_binding {
++ bindings.push((name, ident.span));
++ }
++ }
++ if let Some(ref p) = *inner {
++ check_pat(cx, p, init, span, bindings);
++ }
++ },
++ PatKind::Struct(_, pfields, _) => {
++ if let Some(init_struct) = init {
++ if let ExprKind::Struct(_, ref efields, _) = init_struct.kind {
++ for field in pfields {
++ let name = field.ident.name;
++ let efield = efields
++ .iter()
++ .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
++ check_pat(cx, &field.pat, efield, span, bindings);
++ }
++ } else {
++ for field in pfields {
++ check_pat(cx, &field.pat, init, span, bindings);
++ }
++ }
++ } else {
++ for field in pfields {
++ check_pat(cx, &field.pat, None, span, bindings);
++ }
++ }
++ },
++ PatKind::Tuple(inner, _) => {
++ if let Some(init_tup) = init {
++ if let ExprKind::Tup(ref tup) = init_tup.kind {
++ for (i, p) in inner.iter().enumerate() {
++ check_pat(cx, p, Some(&tup[i]), p.span, bindings);
++ }
++ } else {
++ for p in inner {
++ check_pat(cx, p, init, span, bindings);
++ }
++ }
++ } else {
++ for p in inner {
++ check_pat(cx, p, None, span, bindings);
++ }
++ }
++ },
++ PatKind::Box(ref inner) => {
++ if let Some(initp) = init {
++ if let ExprKind::Box(ref inner_init) = initp.kind {
++ check_pat(cx, inner, Some(&**inner_init), span, bindings);
++ } else {
++ check_pat(cx, inner, init, span, bindings);
++ }
++ } else {
++ check_pat(cx, inner, init, span, bindings);
++ }
++ },
++ PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
++ // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
++ _ => (),
++ }
++}
++
++fn lint_shadow<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ name: Name,
++ span: Span,
++ pattern_span: Span,
++ init: Option<&'tcx Expr<'_>>,
++ prev_span: Span,
++) {
++ if let Some(expr) = init {
++ if is_self_shadow(name, expr) {
++ span_lint_and_then(
++ cx,
++ SHADOW_SAME,
++ span,
++ &format!(
++ "`{}` is shadowed by itself in `{}`",
++ snippet(cx, pattern_span, "_"),
++ snippet(cx, expr.span, "..")
++ ),
++ |diag| {
++ diag.span_note(prev_span, "previous binding is here");
++ },
++ );
++ } else if contains_name(name, expr) {
++ span_lint_and_then(
++ cx,
++ SHADOW_REUSE,
++ pattern_span,
++ &format!(
++ "`{}` is shadowed by `{}` which reuses the original value",
++ snippet(cx, pattern_span, "_"),
++ snippet(cx, expr.span, "..")
++ ),
++ |diag| {
++ diag.span_note(expr.span, "initialization happens here");
++ diag.span_note(prev_span, "previous binding is here");
++ },
++ );
++ } else {
++ span_lint_and_then(
++ cx,
++ SHADOW_UNRELATED,
++ pattern_span,
++ &format!(
++ "`{}` is shadowed by `{}`",
++ snippet(cx, pattern_span, "_"),
++ snippet(cx, expr.span, "..")
++ ),
++ |diag| {
++ diag.span_note(expr.span, "initialization happens here");
++ diag.span_note(prev_span, "previous binding is here");
++ },
++ );
++ }
++ } else {
++ span_lint_and_then(
++ cx,
++ SHADOW_UNRELATED,
++ span,
++ &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
++ |diag| {
++ diag.span_note(prev_span, "previous binding is here");
++ },
++ );
++ }
++}
++
++fn check_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++ match expr.kind {
++ ExprKind::Unary(_, ref e)
++ | ExprKind::Field(ref e, _)
++ | ExprKind::AddrOf(_, _, ref e)
++ | ExprKind::Box(ref e) => check_expr(cx, e, bindings),
++ ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, _, _) => check_block(cx, block, bindings),
++ // ExprKind::Call
++ // ExprKind::MethodCall
++ ExprKind::Array(v) | ExprKind::Tup(v) => {
++ for e in v {
++ check_expr(cx, e, bindings)
++ }
++ },
++ ExprKind::Match(ref init, arms, _) => {
++ check_expr(cx, init, bindings);
++ let len = bindings.len();
++ for arm in arms {
++ check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings);
++ // This is ugly, but needed to get the right type
++ if let Some(ref guard) = arm.guard {
++ match guard {
++ Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
++ }
++ }
++ check_expr(cx, &arm.body, bindings);
++ bindings.truncate(len);
++ }
++ },
++ _ => (),
++ }
++}
++
++fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) {
++ match ty.kind {
++ TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
++ TyKind::Array(ref fty, ref anon_const) => {
++ check_ty(cx, fty, bindings);
++ check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
++ },
++ TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => {
++ check_ty(cx, mty, bindings)
++ },
++ TyKind::Tup(tup) => {
++ for t in tup {
++ check_ty(cx, t, bindings)
++ }
++ },
++ TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
++ _ => (),
++ }
++}
++
++fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
++ match expr.kind {
++ ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
++ ExprKind::Block(ref block, _) => {
++ block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
++ },
++ ExprKind::Unary(op, ref inner) => (UnOp::UnDeref == op) && is_self_shadow(name, inner),
++ ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path),
++ _ => false,
++ }
++}
++
++fn path_eq_name(name: Name, path: &Path<'_>) -> bool {
++ !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
++}
--- /dev/null
--- /dev/null
++use crate::utils::{in_macro, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{Item, ItemKind, UseTreeKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::edition::Edition;
++
++declare_clippy_lint! {
++ /// **What it does:** Checking for imports with single component use path.
++ ///
++ /// **Why is this bad?** Import with single component use path such as `use cratename;`
++ /// is not necessary, and thus should be removed.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust, ignore
++ /// use regex;
++ ///
++ /// fn main() {
++ /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++ /// }
++ /// ```
++ /// Better as
++ /// ```rust, ignore
++ /// fn main() {
++ /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++ /// }
++ /// ```
++ pub SINGLE_COMPONENT_PATH_IMPORTS,
++ style,
++ "imports with single component path are redundant"
++}
++
++declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
++
++impl EarlyLintPass for SingleComponentPathImports {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ if_chain! {
++ if !in_macro(item.span);
++ if cx.sess.opts.edition == Edition::Edition2018;
++ if !item.vis.node.is_pub();
++ if let ItemKind::Use(use_tree) = &item.kind;
++ if let segments = &use_tree.prefix.segments;
++ if segments.len() == 1;
++ if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
++ then {
++ span_lint_and_sugg(
++ cx,
++ SINGLE_COMPONENT_PATH_IMPORTS,
++ item.span,
++ "this import is redundant",
++ "remove it entirely",
++ String::new(),
++ Applicability::MachineApplicable
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::sugg::Sugg;
++use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor};
++use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks slow zero-filled vector initialization
++ ///
++ /// **Why is this bad?** These structures are non-idiomatic and less efficient than simply using
++ /// `vec![0; len]`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use core::iter::repeat;
++ /// # let len = 4;
++ /// let mut vec1 = Vec::with_capacity(len);
++ /// vec1.resize(len, 0);
++ ///
++ /// let mut vec2 = Vec::with_capacity(len);
++ /// vec2.extend(repeat(0).take(len))
++ /// ```
++ pub SLOW_VECTOR_INITIALIZATION,
++ perf,
++ "slow vector initialization"
++}
++
++declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
++
++/// `VecAllocation` contains data regarding a vector allocated with `with_capacity` and then
++/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
++/// `vec = Vec::with_capacity(0)`
++struct VecAllocation<'tcx> {
++ /// Symbol of the local variable name
++ variable_name: Symbol,
++
++ /// Reference to the expression which allocates the vector
++ allocation_expr: &'tcx Expr<'tcx>,
++
++ /// Reference to the expression used as argument on `with_capacity` call. This is used
++ /// to only match slow zero-filling idioms of the same length than vector initialization.
++ len_expr: &'tcx Expr<'tcx>,
++}
++
++/// Type of slow initialization
++enum InitializationType<'tcx> {
++ /// Extend is a slow initialization with the form `vec.extend(repeat(0).take(..))`
++ Extend(&'tcx Expr<'tcx>),
++
++ /// Resize is a slow initialization with the form `vec.resize(.., 0)`
++ Resize(&'tcx Expr<'tcx>),
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SlowVectorInit {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
++ if_chain! {
++ if let ExprKind::Assign(ref left, ref right, _) = expr.kind;
++
++ // Extract variable name
++ if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind;
++ if let Some(variable_name) = path.segments.get(0);
++
++ // Extract len argument
++ if let Some(ref len_arg) = Self::is_vec_with_capacity(right);
++
++ then {
++ let vi = VecAllocation {
++ variable_name: variable_name.ident.name,
++ allocation_expr: right,
++ len_expr: len_arg,
++ };
++
++ Self::search_initialization(cx, vi, expr.hir_id);
++ }
++ }
++ }
++
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++ // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
++ if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
++ if let Some(ref init) = local.init;
++ if let Some(ref len_arg) = Self::is_vec_with_capacity(init);
++
++ then {
++ let vi = VecAllocation {
++ variable_name: variable_name.name,
++ allocation_expr: init,
++ len_expr: len_arg,
++ };
++
++ Self::search_initialization(cx, vi, stmt.hir_id);
++ }
++ }
++ }
++}
++
++impl SlowVectorInit {
++ /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
++ /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
++ fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ if_chain! {
++ if let ExprKind::Call(ref func, ref args) = expr.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &["Vec", "with_capacity"]);
++ if args.len() == 1;
++
++ then {
++ return Some(&args[0]);
++ }
++ }
++
++ None
++ }
++
++ /// Search initialization for the given vector
++ fn search_initialization<'tcx>(cx: &LateContext<'_, 'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
++ let enclosing_body = get_enclosing_block(cx, parent_node);
++
++ if enclosing_body.is_none() {
++ return;
++ }
++
++ let mut v = VectorInitializationVisitor {
++ cx,
++ vec_alloc,
++ slow_expression: None,
++ initialization_found: false,
++ };
++
++ v.visit_block(enclosing_body.unwrap());
++
++ if let Some(ref allocation_expr) = v.slow_expression {
++ Self::lint_initialization(cx, allocation_expr, &v.vec_alloc);
++ }
++ }
++
++ fn lint_initialization<'tcx>(
++ cx: &LateContext<'_, 'tcx>,
++ initialization: &InitializationType<'tcx>,
++ vec_alloc: &VecAllocation<'_>,
++ ) {
++ match initialization {
++ InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint(
++ cx,
++ e,
++ vec_alloc,
++ "slow zero-filling initialization",
++ SLOW_VECTOR_INITIALIZATION,
++ ),
++ };
++ }
++
++ fn emit_lint<'tcx>(
++ cx: &LateContext<'_, 'tcx>,
++ slow_fill: &Expr<'_>,
++ vec_alloc: &VecAllocation<'_>,
++ msg: &str,
++ lint: &'static Lint,
++ ) {
++ let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
++
++ span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| {
++ diag.span_suggestion(
++ vec_alloc.allocation_expr.span,
++ "consider replace allocation with",
++ format!("vec![0; {}]", len_expr),
++ Applicability::Unspecified,
++ );
++ });
++ }
++}
++
++/// `VectorInitializationVisitor` searches for unsafe or slow vector initializations for the given
++/// vector.
++struct VectorInitializationVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++
++ /// Contains the information.
++ vec_alloc: VecAllocation<'tcx>,
++
++ /// Contains the slow initialization expression, if one was found.
++ slow_expression: Option<InitializationType<'tcx>>,
++
++ /// `true` if the initialization of the vector has been found on the visited block.
++ initialization_found: bool,
++}
++
++impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
++ /// Checks if the given expression is extending a vector with `repeat(0).take(..)`
++ fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if self.initialization_found;
++ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++ if let ExprKind::Path(ref qpath_subj) = args[0].kind;
++ if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
++ if path.ident.name == sym!(extend);
++ if let Some(ref extend_arg) = args.get(1);
++ if self.is_repeat_take(extend_arg);
++
++ then {
++ self.slow_expression = Some(InitializationType::Extend(expr));
++ }
++ }
++ }
++
++ /// Checks if the given expression is resizing a vector with 0
++ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if self.initialization_found;
++ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++ if let ExprKind::Path(ref qpath_subj) = args[0].kind;
++ if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
++ if path.ident.name == sym!(resize);
++ if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
++
++ // Check that is filled with 0
++ if let ExprKind::Lit(ref lit) = fill_arg.kind;
++ if let LitKind::Int(0, _) = lit.node;
++
++ // Check that len expression is equals to `with_capacity` expression
++ if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
++
++ then {
++ self.slow_expression = Some(InitializationType::Resize(expr));
++ }
++ }
++ }
++
++ /// Returns `true` if give expression is `repeat(0).take(...)`
++ fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
++ if_chain! {
++ if let ExprKind::MethodCall(ref take_path, _, ref take_args) = expr.kind;
++ if take_path.ident.name == sym!(take);
++
++ // Check that take is applied to `repeat(0)`
++ if let Some(ref repeat_expr) = take_args.get(0);
++ if Self::is_repeat_zero(repeat_expr);
++
++ // Check that len expression is equals to `with_capacity` expression
++ if let Some(ref len_arg) = take_args.get(1);
++ if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
++
++ then {
++ return true;
++ }
++ }
++
++ false
++ }
++
++ /// Returns `true` if given expression is `repeat(0)`
++ fn is_repeat_zero(expr: &Expr<'_>) -> bool {
++ if_chain! {
++ if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind;
++ if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
++ if match_qpath(&qpath_repeat, &["repeat"]);
++ if let Some(ref repeat_arg) = repeat_args.get(0);
++ if let ExprKind::Lit(ref lit) = repeat_arg.kind;
++ if let LitKind::Int(0, _) = lit.node;
++
++ then {
++ return true
++ }
++ }
++
++ false
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++ if self.initialization_found {
++ match stmt.kind {
++ StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
++ self.search_slow_extend_filling(expr);
++ self.search_slow_resize_filling(expr);
++ },
++ _ => (),
++ }
++
++ self.initialization_found = false;
++ } else {
++ walk_stmt(self, stmt);
++ }
++ }
++
++ fn visit_block(&mut self, block: &'tcx Block<'_>) {
++ if self.initialization_found {
++ if let Some(ref s) = block.stmts.get(0) {
++ self.visit_stmt(s)
++ }
++
++ self.initialization_found = false;
++ } else {
++ walk_block(self, block);
++ }
++ }
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ // Skip all the expressions previous to the vector initialization
++ if self.vec_alloc.allocation_expr.hir_id == expr.hir_id {
++ self.initialization_found = true;
++ }
++
++ walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use if_chain::if_chain;
++
++use crate::utils::SpanlessEq;
++use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for string appends of the form `x = x + y` (without
++ /// `let`!).
++ ///
++ /// **Why is this bad?** It's not really bad, but some people think that the
++ /// `.push_str(_)` method is more readable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let mut x = "Hello".to_owned();
++ /// x = x + ", World";
++ /// ```
++ pub STRING_ADD_ASSIGN,
++ pedantic,
++ "using `x = x + ..` where x is a `String` instead of `push_str()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for all instances of `x + _` where `x` is of type
++ /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
++ /// match.
++ ///
++ /// **Why is this bad?** It's not bad in and of itself. However, this particular
++ /// `Add` implementation is asymmetric (the other operand need not be `String`,
++ /// but `x` does), while addition as mathematically defined is symmetric, also
++ /// the `String::push_str(_)` function is a perfectly good replacement.
++ /// Therefore, some dislike it and wish not to have it in their code.
++ ///
++ /// That said, other people think that string addition, having a long tradition
++ /// in other languages is actually fine, which is why we decided to make this
++ /// particular lint `allow` by default.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let x = "Hello".to_owned();
++ /// x + ", World";
++ /// ```
++ pub STRING_ADD,
++ restriction,
++ "using `x + ..` where x is a `String` instead of `push_str()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the `as_bytes` method called on string literals
++ /// that contain only ASCII characters.
++ ///
++ /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used
++ /// instead. They are shorter but less discoverable than `as_bytes()`.
++ ///
++ /// **Known Problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let bs = "a byte string".as_bytes();
++ /// ```
++ pub STRING_LIT_AS_BYTES,
++ style,
++ "calling `as_bytes` on a string literal instead of using a byte string literal"
++}
++
++declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if in_external_macro(cx.sess(), e.span) {
++ return;
++ }
++
++ if let ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ ref left,
++ _,
++ ) = e.kind
++ {
++ if is_string(cx, left) {
++ if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
++ let parent = get_parent_expr(cx, e);
++ if let Some(p) = parent {
++ if let ExprKind::Assign(ref target, _, _) = p.kind {
++ // avoid duplicate matches
++ if SpanlessEq::new(cx).eq_expr(target, left) {
++ return;
++ }
++ }
++ }
++ }
++ span_lint(
++ cx,
++ STRING_ADD,
++ e.span,
++ "you added something to a string. Consider using `String::push_str()` instead",
++ );
++ }
++ } else if let ExprKind::Assign(ref target, ref src, _) = e.kind {
++ if is_string(cx, target) && is_add(cx, src, target) {
++ span_lint(
++ cx,
++ STRING_ADD_ASSIGN,
++ e.span,
++ "you assigned the result of adding something to this string. Consider using \
++ `String::push_str()` instead",
++ );
++ }
++ }
++ }
++}
++
++fn is_string(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
++ is_type_diagnostic_item(cx, walk_ptrs_ty(cx.tables.expr_ty(e)), sym!(string_type))
++}
++
++fn is_add(cx: &LateContext<'_, '_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
++ match src.kind {
++ ExprKind::Binary(
++ Spanned {
++ node: BinOpKind::Add, ..
++ },
++ ref left,
++ _,
++ ) => SpanlessEq::new(cx).eq_expr(target, left),
++ ExprKind::Block(ref block, _) => {
++ block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
++ },
++ _ => false,
++ }
++}
++
++// Max length a b"foo" string can take
++const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
++
++declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ use crate::utils::{snippet, snippet_with_applicability};
++ use rustc_ast::ast::LitKind;
++
++ if_chain! {
++ if let ExprKind::MethodCall(path, _, args) = &e.kind;
++ if path.ident.name == sym!(as_bytes);
++ if let ExprKind::Lit(lit) = &args[0].kind;
++ if let LitKind::Str(lit_content, _) = &lit.node;
++ then {
++ let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
++ let mut applicability = Applicability::MachineApplicable;
++ if callsite.starts_with("include_str!") {
++ span_lint_and_sugg(
++ cx,
++ STRING_LIT_AS_BYTES,
++ e.span,
++ "calling `as_bytes()` on `include_str!(..)`",
++ "consider using `include_bytes!(..)` instead",
++ snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
++ "include_str",
++ "include_bytes",
++ 1,
++ ),
++ applicability,
++ );
++ } else if lit_content.as_str().is_ascii()
++ && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
++ && !args[0].span.from_expansion()
++ {
++ span_lint_and_sugg(
++ cx,
++ STRING_LIT_AS_BYTES,
++ e.span,
++ "calling `as_bytes()` on a string literal",
++ "consider using a byte string literal instead",
++ format!(
++ "b{}",
++ snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
++ ),
++ applicability,
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{get_trait_def_id, span_lint, 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 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,
++ correctness,
++ "suspicious use of operators in impl of OpAssign trait"
++}
++
++declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SuspiciousImpl {
++ fn check_expr(&mut self, cx: &LateContext<'a, '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 if the binary expression is part of another bi/unary expression
++ // or operator assignment as a child node
++ let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
++ while parent_expr != hir::CRATE_HIR_ID {
++ if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
++ match e.kind {
++ hir::ExprKind::Binary(..)
++ | hir::ExprKind::Unary(hir::UnOp::UnNot, _)
++ | hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
++ | hir::ExprKind::AssignOp(..) => return,
++ _ => {},
++ }
++ }
++ parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
++ }
++ // as a parent node
++ let mut visitor = BinaryExprVisitor { in_binary_expr: false };
++ walk_expr(&mut visitor, expr);
++
++ if visitor.in_binary_expr {
++ return;
++ }
++
++ if let Some(impl_trait) = check_binop(
++ cx,
++ expr,
++ binop.node,
++ &["Add", "Sub", "Mul", "Div"],
++ &[
++ hir::BinOpKind::Add,
++ hir::BinOpKind::Sub,
++ hir::BinOpKind::Mul,
++ hir::BinOpKind::Div,
++ ],
++ ) {
++ span_lint(
++ cx,
++ SUSPICIOUS_ARITHMETIC_IMPL,
++ binop.span,
++ &format!(r#"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!(r#"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] = crate::utils::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 {
++ in_binary_expr: bool,
++}
++
++impl<'a, '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::UnNot, _)
++ | hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
++ | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
++ _ => {},
++ }
++
++ walk_expr(self, expr);
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::sugg::Sugg;
++use crate::utils::{
++ differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty,
++ SpanlessEq,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for manual swapping.
++ ///
++ /// **Why is this bad?** The `std::mem::swap` function exposes the intent better
++ /// without deinitializing or copying either variable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let mut a = 42;
++ /// let mut b = 1337;
++ ///
++ /// let t = b;
++ /// b = a;
++ /// a = t;
++ /// ```
++ /// Use std::mem::swap():
++ /// ```rust
++ /// let mut a = 1;
++ /// let mut b = 2;
++ /// std::mem::swap(&mut a, &mut b);
++ /// ```
++ pub MANUAL_SWAP,
++ complexity,
++ "manual swap of two variables"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `foo = bar; bar = foo` sequences.
++ ///
++ /// **Why is this bad?** This looks like a failed attempt to swap.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let mut a = 1;
++ /// # let mut b = 2;
++ /// a = b;
++ /// b = a;
++ /// ```
++ /// If swapping is intended, use `swap()` instead:
++ /// ```rust
++ /// # let mut a = 1;
++ /// # let mut b = 2;
++ /// std::mem::swap(&mut a, &mut b);
++ /// ```
++ pub ALMOST_SWAPPED,
++ correctness,
++ "`foo = bar; bar = foo` sequence"
++}
++
++declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
++ fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++ check_manual_swap(cx, block);
++ check_suspicious_swap(cx, block);
++ }
++}
++
++/// Implementation of the `MANUAL_SWAP` lint.
++fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) {
++ for w in block.stmts.windows(3) {
++ if_chain! {
++ // let t = foo();
++ if let StmtKind::Local(ref tmp) = w[0].kind;
++ if let Some(ref tmp_init) = tmp.init;
++ if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
++
++ // foo() = bar();
++ if let StmtKind::Semi(ref first) = w[1].kind;
++ if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind;
++
++ // bar() = t;
++ if let StmtKind::Semi(ref second) = w[2].kind;
++ if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind;
++ if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind;
++ if rhs2.segments.len() == 1;
++
++ if ident.as_str() == rhs2.segments[0].ident.as_str();
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
++ then {
++ if let ExprKind::Field(ref lhs1, _) = lhs1.kind {
++ if let ExprKind::Field(ref lhs2, _) = lhs2.kind {
++ if lhs1.hir_id.owner == lhs2.hir_id.owner {
++ return;
++ }
++ }
++ }
++
++ let mut applicability = Applicability::MachineApplicable;
++
++ let slice = check_for_slice(cx, lhs1, lhs2);
++ let (replace, what, sugg) = if let Slice::NotSwappable = slice {
++ return;
++ } else if let Slice::Swappable(slice, idx1, idx2) = slice {
++ if let Some(slice) = Sugg::hir_opt(cx, slice) {
++ (
++ false,
++ format!(" elements of `{}`", slice),
++ format!(
++ "{}.swap({}, {})",
++ slice.maybe_par(),
++ snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
++ snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
++ ),
++ )
++ } else {
++ (false, String::new(), String::new())
++ }
++ } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
++ (
++ true,
++ format!(" `{}` and `{}`", first, second),
++ format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
++ )
++ } else {
++ (true, String::new(), String::new())
++ };
++
++ let span = w[0].span.to(second.span);
++
++ span_lint_and_then(
++ cx,
++ MANUAL_SWAP,
++ span,
++ &format!("this looks like you are swapping{} manually", what),
++ |diag| {
++ if !sugg.is_empty() {
++ diag.span_suggestion(
++ span,
++ "try",
++ sugg,
++ applicability,
++ );
++
++ if replace {
++ diag.note("or maybe you should use `std::mem::replace`?");
++ }
++ }
++ }
++ );
++ }
++ }
++ }
++}
++
++enum Slice<'a> {
++ /// `slice.swap(idx1, idx2)` can be used
++ ///
++ /// ## Example
++ ///
++ /// ```rust
++ /// # let mut a = vec![0, 1];
++ /// let t = a[1];
++ /// a[1] = a[0];
++ /// a[0] = t;
++ /// // can be written as
++ /// a.swap(0, 1);
++ /// ```
++ Swappable(&'a Expr<'a>, &'a Expr<'a>, &'a Expr<'a>),
++ /// The `swap` function cannot be used.
++ ///
++ /// ## Example
++ ///
++ /// ```rust
++ /// # let mut a = [vec![1, 2], vec![3, 4]];
++ /// let t = a[0][1];
++ /// a[0][1] = a[1][0];
++ /// a[1][0] = t;
++ /// ```
++ NotSwappable,
++ /// Not a slice
++ None,
++}
++
++/// Checks if both expressions are index operations into "slice-like" types.
++fn check_for_slice<'a>(cx: &LateContext<'_, '_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> {
++ if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
++ if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
++ let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));
++
++ if matches!(ty.kind, ty::Slice(_))
++ || matches!(ty.kind, ty::Array(_, _))
++ || is_type_diagnostic_item(cx, ty, sym!(vec_type))
++ || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
++ {
++ return Slice::Swappable(lhs1, idx1, idx2);
++ }
++ } else {
++ return Slice::NotSwappable;
++ }
++ }
++ }
++
++ Slice::None
++}
++
++/// Implementation of the `ALMOST_SWAPPED` lint.
++fn check_suspicious_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) {
++ for w in block.stmts.windows(2) {
++ if_chain! {
++ if let StmtKind::Semi(ref first) = w[0].kind;
++ if let StmtKind::Semi(ref second) = w[1].kind;
++ if !differing_macro_contexts(first.span, second.span);
++ if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind;
++ if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind;
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
++ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
++ then {
++ let lhs0 = Sugg::hir_opt(cx, lhs0);
++ let rhs0 = Sugg::hir_opt(cx, rhs0);
++ let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
++ (
++ format!(" `{}` and `{}`", first, second),
++ first.mut_addr().to_string(),
++ second.mut_addr().to_string(),
++ )
++ } else {
++ (String::new(), String::new(), String::new())
++ };
++
++ let span = first.span.to(second.span);
++
++ span_lint_and_then(cx,
++ ALMOST_SWAPPED,
++ span,
++ &format!("this looks like you are trying to swap{}", what),
++ |diag| {
++ if !what.is_empty() {
++ diag.span_suggestion(
++ span,
++ "try",
++ format!(
++ "std::mem::swap({}, {})",
++ lhs,
++ rhs,
++ ),
++ Applicability::MaybeIncorrect,
++ );
++ diag.note("or maybe you should use `std::mem::replace`?");
++ }
++ });
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint_and_sugg;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{BytePos, Span};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks doc comments for usage of tab characters.
++ ///
++ /// **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.
++ /// To keep a consistent view on the source, also doc comments should not have tabs.
++ /// Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the
++ /// display settings of the author and reader differ.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// ///
++ /// /// Struct to hold two strings:
++ /// /// - first one
++ /// /// - second one
++ /// pub struct DoubleString {
++ /// ///
++ /// /// - First String:
++ /// /// - needs to be inside here
++ /// first_string: String,
++ /// ///
++ /// /// - Second String:
++ /// /// - needs to be inside here
++ /// second_string: String,
++ ///}
++ /// ```
++ ///
++ /// Will be converted to:
++ /// ```rust
++ /// ///
++ /// /// Struct to hold two strings:
++ /// /// - first one
++ /// /// - second one
++ /// pub struct DoubleString {
++ /// ///
++ /// /// - First String:
++ /// /// - needs to be inside here
++ /// first_string: String,
++ /// ///
++ /// /// - Second String:
++ /// /// - needs to be inside here
++ /// second_string: String,
++ ///}
++ /// ```
++ pub TABS_IN_DOC_COMMENTS,
++ style,
++ "using tabs in doc comments is not recommended"
++}
++
++declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
++
++impl TabsInDocComments {
++ fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
++ if let ast::AttrKind::DocComment(comment) = attr.kind {
++ let comment = comment.as_str();
++
++ for (lo, hi) in get_chunks_of_tabs(&comment) {
++ let new_span = Span::new(
++ attr.span.lo() + BytePos(lo),
++ attr.span.lo() + BytePos(hi),
++ attr.span.ctxt(),
++ );
++ span_lint_and_sugg(
++ cx,
++ TABS_IN_DOC_COMMENTS,
++ new_span,
++ "using tabs in doc comments is not recommended",
++ "consider using four spaces per tab",
++ " ".repeat((hi - lo) as usize),
++ Applicability::MaybeIncorrect,
++ );
++ }
++ }
++ }
++}
++
++impl EarlyLintPass for TabsInDocComments {
++ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) {
++ Self::warn_if_tabs_in_doc(cx, &attribute);
++ }
++}
++
++///
++/// scans the string for groups of tabs and returns the start(inclusive) and end positions
++/// (exclusive) of all groups
++/// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as
++/// 012 3456 7 89
++/// ^-^ ^---^
++fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
++ let line_length_way_to_long = "doc comment longer than 2^32 chars";
++ let mut spans: Vec<(u32, u32)> = vec![];
++ let mut current_start: u32 = 0;
++
++ // tracker to decide if the last group of tabs is not closed by a non-tab character
++ let mut is_active = false;
++
++ let chars_array: Vec<_> = the_str.chars().collect();
++
++ if chars_array == vec!['\t'] {
++ return vec![(0, 1)];
++ }
++
++ for (index, arr) in chars_array.windows(2).enumerate() {
++ let index = u32::try_from(index).expect(line_length_way_to_long);
++ match arr {
++ ['\t', '\t'] => {
++ // either string starts with double tab, then we have to set it active,
++ // otherwise is_active is true anyway
++ is_active = true;
++ },
++ [_, '\t'] => {
++ // as ['\t', '\t'] is excluded, this has to be a start of a tab group,
++ // set indices accordingly
++ is_active = true;
++ current_start = index + 1;
++ },
++ ['\t', _] => {
++ // this now has to be an end of the group, hence we have to push a new tuple
++ is_active = false;
++ spans.push((current_start, index + 1));
++ },
++ _ => {},
++ }
++ }
++
++ // only possible when tabs are at the end, insert last group
++ if is_active {
++ spans.push((
++ current_start,
++ u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
++ ));
++ }
++
++ spans
++}
++
++#[cfg(test)]
++mod tests_for_get_chunks_of_tabs {
++ use super::get_chunks_of_tabs;
++
++ #[test]
++ fn test_empty_string() {
++ let res = get_chunks_of_tabs("");
++
++ assert_eq!(res, vec![]);
++ }
++
++ #[test]
++ fn test_simple() {
++ let res = get_chunks_of_tabs("sd\t\t\taa");
++
++ assert_eq!(res, vec![(2, 5)]);
++ }
++
++ #[test]
++ fn test_only_t() {
++ let res = get_chunks_of_tabs("\t\t");
++
++ assert_eq!(res, vec![(0, 2)]);
++ }
++
++ #[test]
++ fn test_only_one_t() {
++ let res = get_chunks_of_tabs("\t");
++
++ assert_eq!(res, vec![(0, 1)]);
++ }
++
++ #[test]
++ fn test_double() {
++ let res = get_chunks_of_tabs("sd\tasd\t\taa");
++
++ assert_eq!(res, vec![(2, 3), (6, 8)]);
++ }
++
++ #[test]
++ fn test_start() {
++ let res = get_chunks_of_tabs("\t\taa");
++
++ assert_eq!(res, vec![(0, 2)]);
++ }
++
++ #[test]
++ fn test_end() {
++ let res = get_chunks_of_tabs("aa\t\t");
++
++ assert_eq!(res, vec![(2, 4)]);
++ }
++
++ #[test]
++ fn test_start_single() {
++ let res = get_chunks_of_tabs("\taa");
++
++ assert_eq!(res, vec![(0, 1)]);
++ }
++
++ #[test]
++ fn test_end_single() {
++ let res = get_chunks_of_tabs("aa\t");
++
++ assert_eq!(res, vec![(2, 3)]);
++ }
++
++ #[test]
++ fn test_no_tabs() {
++ let res = get_chunks_of_tabs("dsfs");
++
++ assert_eq!(res, vec![]);
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_adjusted, span_lint};
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for construction of a structure or tuple just to
++ /// assign a value in it.
++ ///
++ /// **Why is this bad?** Readability. If the structure is only created to be
++ /// updated, why not write the structure you want in the first place?
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// (0, 0).0 = 1
++ /// ```
++ pub TEMPORARY_ASSIGNMENT,
++ complexity,
++ "assignments to temporaries"
++}
++
++fn is_temporary(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ match &expr.kind {
++ ExprKind::Struct(..) | ExprKind::Tup(..) => true,
++ ExprKind::Path(qpath) => {
++ if let Res::Def(DefKind::Const, ..) = cx.tables.qpath_res(qpath, expr.hir_id) {
++ true
++ } else {
++ false
++ }
++ },
++ _ => false,
++ }
++}
++
++declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TemporaryAssignment {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Assign(target, ..) = &expr.kind {
++ let mut base = target;
++ while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind {
++ base = f;
++ }
++ if is_temporary(cx, base) && !is_adjusted(cx, base) {
++ span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.
++ ///
++ /// **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's
++ /// more straight forward to use the dedicated `is_digit` method.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let c = 'c';
++ /// # let radix = 10;
++ /// let is_digit = c.to_digit(radix).is_some();
++ /// ```
++ /// can be written as:
++ /// ```
++ /// # let c = 'c';
++ /// # let radix = 10;
++ /// let is_digit = c.is_digit(radix);
++ /// ```
++ pub TO_DIGIT_IS_SOME,
++ style,
++ "`char.is_digit()` is clearer"
++}
++
++declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if_chain! {
++ if let hir::ExprKind::MethodCall(is_some_path, _, is_some_args) = &expr.kind;
++ if is_some_path.ident.name.as_str() == "is_some";
++ if let [to_digit_expr] = &**is_some_args;
++ then {
++ let match_result = match &to_digit_expr.kind {
++ hir::ExprKind::MethodCall(to_digits_path, _, to_digit_args) => {
++ if_chain! {
++ if let [char_arg, radix_arg] = &**to_digit_args;
++ if to_digits_path.ident.name.as_str() == "to_digit";
++ let char_arg_ty = cx.tables.expr_ty_adjusted(char_arg);
++ if char_arg_ty.kind == ty::Char;
++ then {
++ Some((true, char_arg, radix_arg))
++ } else {
++ None
++ }
++ }
++ }
++ hir::ExprKind::Call(to_digits_call, to_digit_args) => {
++ if_chain! {
++ if let [char_arg, radix_arg] = &**to_digit_args;
++ if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
++ if let to_digits_call_res = cx.tables.qpath_res(to_digits_path, to_digits_call.hir_id);
++ if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
++ if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]);
++ then {
++ Some((false, char_arg, radix_arg))
++ } else {
++ None
++ }
++ }
++ }
++ _ => None
++ };
++
++ if let Some((is_method_call, char_arg, radix_arg)) = match_result {
++ let mut applicability = Applicability::MachineApplicable;
++ let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability);
++ let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability);
++
++ span_lint_and_sugg(
++ cx,
++ TO_DIGIT_IS_SOME,
++ expr.span,
++ "use of `.to_digit(..).is_some()`",
++ "try this",
++ if is_method_call {
++ format!("{}.is_digit({})", char_arg_snip, radix_snip)
++ } else {
++ format!("char::is_digit({}, {})", char_arg_snip, radix_snip)
++ },
++ applicability,
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_hir::{GenericBound, Generics, WherePredicate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++#[derive(Copy, Clone)]
++pub struct TraitBounds;
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
++ ///
++ /// **Why is this bad?** Repeating the type for every bound makes the code
++ /// less readable than combining the bounds
++ ///
++ /// **Example:**
++ /// ```rust
++ /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
++ /// ```
++ ///
++ /// Could be written as:
++ ///
++ /// ```rust
++ /// pub fn foo<T>(t: T) where T: Copy + Clone {}
++ /// ```
++ pub TYPE_REPETITION_IN_BOUNDS,
++ pedantic,
++ "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
++}
++
++impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
++ fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics<'_>) {
++ if in_macro(gen.span) {
++ return;
++ }
++ let hash = |ty| -> u64 {
++ let mut hasher = SpanlessHash::new(cx, cx.tables);
++ hasher.hash_ty(ty);
++ hasher.finish()
++ };
++ let mut map = FxHashMap::default();
++ let mut applicability = Applicability::MaybeIncorrect;
++ for bound in gen.where_clause.predicates {
++ if let WherePredicate::BoundPredicate(ref p) = bound {
++ let h = hash(&p.bounded_ty);
++ if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
++ let mut hint_string = format!(
++ "consider combining the bounds: `{}:",
++ snippet(cx, p.bounded_ty.span, "_")
++ );
++ for b in v.iter() {
++ if let GenericBound::Trait(ref poly_trait_ref, _) = b {
++ let path = &poly_trait_ref.trait_ref.path;
++ hint_string.push_str(&format!(
++ " {} +",
++ snippet_with_applicability(cx, path.span, "..", &mut applicability)
++ ));
++ }
++ }
++ for b in p.bounds.iter() {
++ if let GenericBound::Trait(ref poly_trait_ref, _) = b {
++ let path = &poly_trait_ref.trait_ref.path;
++ hint_string.push_str(&format!(
++ " {} +",
++ snippet_with_applicability(cx, path.span, "..", &mut applicability)
++ ));
++ }
++ }
++ hint_string.truncate(hint_string.len() - 2);
++ hint_string.push('`');
++ span_lint_and_help(
++ cx,
++ TYPE_REPETITION_IN_BOUNDS,
++ p.span,
++ "this type has already been used as a bound predicate",
++ None,
++ &hint_string,
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{
++ is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
++ span_lint_and_then, sugg,
++};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes that can't ever be correct on any
++ /// architecture.
++ ///
++ /// **Why is this bad?** It's basically guaranteed to be undefined behaviour.
++ ///
++ /// **Known problems:** When accessing C, users might want to store pointer
++ /// sized objects in `extradata` arguments to save an allocation.
++ ///
++ /// **Example:**
++ /// ```ignore
++ /// let ptr: *const T = core::intrinsics::transmute('x')
++ /// ```
++ pub WRONG_TRANSMUTE,
++ correctness,
++ "transmutes that are confusing at best, undefined behaviour at worst and always useless"
++}
++
++// FIXME: Move this to `complexity` again, after #5343 is fixed
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes to the original type of the object
++ /// and transmutes that could be a cast.
++ ///
++ /// **Why is this bad?** Readability. The code tricks people into thinking that
++ /// something complex is going on.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
++ /// ```
++ pub USELESS_TRANSMUTE,
++ nursery,
++ "transmutes that have the same to and from types or could be a cast/coercion"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes between a type `T` and `*T`.
++ ///
++ /// **Why is this bad?** It's easy to mistakenly transmute between a type and a
++ /// pointer to that type.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// core::intrinsics::transmute(t) // where the result type is the same as
++ /// // `*t` or `&t`'s
++ /// ```
++ pub CROSSPOINTER_TRANSMUTE,
++ complexity,
++ "transmutes that have to or from types that are a pointer to the other"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from a pointer to a reference.
++ ///
++ /// **Why is this bad?** This can always be rewritten with `&` and `*`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// unsafe {
++ /// let _: &T = std::mem::transmute(p); // where p: *const T
++ /// }
++ ///
++ /// // can be written:
++ /// let _: &T = &*p;
++ /// ```
++ pub TRANSMUTE_PTR_TO_REF,
++ complexity,
++ "transmutes from a pointer to a reference type"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from an integer to a `char`.
++ ///
++ /// **Why is this bad?** Not every integer is a Unicode scalar value.
++ ///
++ /// **Known problems:**
++ /// - [`from_u32`] which this lint suggests using is slower than `transmute`
++ /// as it needs to validate the input.
++ /// If you are certain that the input is always a valid Unicode scalar value,
++ /// use [`from_u32_unchecked`] which is as fast as `transmute`
++ /// but has a semantically meaningful name.
++ /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
++ ///
++ /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
++ /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 1_u32;
++ /// unsafe {
++ /// let _: char = std::mem::transmute(x); // where x: u32
++ /// }
++ ///
++ /// // should be:
++ /// let _ = std::char::from_u32(x).unwrap();
++ /// ```
++ pub TRANSMUTE_INT_TO_CHAR,
++ complexity,
++ "transmutes from an integer to a `char`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.
++ ///
++ /// **Why is this bad?** Not every byte slice is a valid UTF-8 string.
++ ///
++ /// **Known problems:**
++ /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
++ /// as it needs to validate the input.
++ /// If you are certain that the input is always a valid UTF-8,
++ /// use [`from_utf8_unchecked`] which is as fast as `transmute`
++ /// but has a semantically meaningful name.
++ /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
++ ///
++ /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
++ /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let b: &[u8] = &[1_u8, 2_u8];
++ /// unsafe {
++ /// let _: &str = std::mem::transmute(b); // where b: &[u8]
++ /// }
++ ///
++ /// // should be:
++ /// let _ = std::str::from_utf8(b).unwrap();
++ /// ```
++ pub TRANSMUTE_BYTES_TO_STR,
++ complexity,
++ "transmutes from a `&[u8]` to a `&str`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from an integer to a `bool`.
++ ///
++ /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = 1_u8;
++ /// unsafe {
++ /// let _: bool = std::mem::transmute(x); // where x: u8
++ /// }
++ ///
++ /// // should be:
++ /// let _: bool = x != 0;
++ /// ```
++ pub TRANSMUTE_INT_TO_BOOL,
++ complexity,
++ "transmutes from an integer to a `bool`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from an integer to a float.
++ ///
++ /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
++ /// and safe.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// unsafe {
++ /// let _: f32 = std::mem::transmute(1_u32); // where x: u32
++ /// }
++ ///
++ /// // should be:
++ /// let _: f32 = f32::from_bits(1_u32);
++ /// ```
++ pub TRANSMUTE_INT_TO_FLOAT,
++ complexity,
++ "transmutes from an integer to a float"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from a float to an integer.
++ ///
++ /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
++ /// and safe.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// unsafe {
++ /// let _: u32 = std::mem::transmute(1f32);
++ /// }
++ ///
++ /// // should be:
++ /// let _: u32 = 1f32.to_bits();
++ /// ```
++ pub TRANSMUTE_FLOAT_TO_INT,
++ complexity,
++ "transmutes from a float to an integer"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes from a pointer to a pointer, or
++ /// from a reference to a reference.
++ ///
++ /// **Why is this bad?** Transmutes are dangerous, and these can instead be
++ /// written as casts.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let ptr = &1u32 as *const u32;
++ /// unsafe {
++ /// // pointer-to-pointer transmute
++ /// let _: *const f32 = std::mem::transmute(ptr);
++ /// // ref-ref transmute
++ /// let _: &f32 = std::mem::transmute(&1u32);
++ /// }
++ /// // These can be respectively written:
++ /// let _ = ptr as *const f32;
++ /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
++ /// ```
++ pub TRANSMUTE_PTR_TO_PTR,
++ complexity,
++ "transmutes from a pointer to a pointer / a reference to a reference"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmutes between collections whose
++ /// types have different ABI, size or alignment.
++ ///
++ /// **Why is this bad?** This is undefined behavior.
++ ///
++ /// **Known problems:** Currently, we cannot know whether a type is a
++ /// collection, so we just lint the ones that come with `std`.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// // different size, therefore likely out-of-bounds memory access
++ /// // You absolutely do not want this in your code!
++ /// unsafe {
++ /// std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
++ /// };
++ /// ```
++ ///
++ /// You must always iterate, map and collect the values:
++ ///
++ /// ```rust
++ /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
++ /// ```
++ pub UNSOUND_COLLECTION_TRANSMUTE,
++ correctness,
++ "transmute between collections of layout-incompatible types"
++}
++declare_lint_pass!(Transmute => [
++ CROSSPOINTER_TRANSMUTE,
++ TRANSMUTE_PTR_TO_REF,
++ TRANSMUTE_PTR_TO_PTR,
++ USELESS_TRANSMUTE,
++ WRONG_TRANSMUTE,
++ TRANSMUTE_INT_TO_CHAR,
++ TRANSMUTE_BYTES_TO_STR,
++ TRANSMUTE_INT_TO_BOOL,
++ TRANSMUTE_INT_TO_FLOAT,
++ TRANSMUTE_FLOAT_TO_INT,
++ UNSOUND_COLLECTION_TRANSMUTE,
++]);
++
++// used to check for UNSOUND_COLLECTION_TRANSMUTE
++static COLLECTIONS: &[&[&str]] = &[
++ &paths::VEC,
++ &paths::VEC_DEQUE,
++ &paths::BINARY_HEAP,
++ &paths::BTREESET,
++ &paths::BTREEMAP,
++ &paths::HASHSET,
++ &paths::HASHMAP,
++];
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
++ #[allow(clippy::similar_names, clippy::too_many_lines)]
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref path_expr, ref args) = e.kind;
++ if let ExprKind::Path(ref qpath) = path_expr.kind;
++ if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::TRANSMUTE);
++ then {
++ let from_ty = cx.tables.expr_ty(&args[0]);
++ let to_ty = cx.tables.expr_ty(e);
++
++ match (&from_ty.kind, &to_ty.kind) {
++ _ if from_ty == to_ty => span_lint(
++ cx,
++ USELESS_TRANSMUTE,
++ e.span,
++ &format!("transmute from a type (`{}`) to itself", from_ty),
++ ),
++ (&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then(
++ cx,
++ USELESS_TRANSMUTE,
++ e.span,
++ "transmute from a reference to a pointer",
++ |diag| {
++ if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++ let rty_and_mut = ty::TypeAndMut {
++ ty: rty,
++ mutbl: rty_mutbl,
++ };
++
++ let sugg = if ptr_ty == rty_and_mut {
++ arg.as_ty(to_ty)
++ } else {
++ arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
++ };
++
++ diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
++ }
++ },
++ ),
++ (&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then(
++ cx,
++ USELESS_TRANSMUTE,
++ e.span,
++ "transmute from an integer to a pointer",
++ |diag| {
++ if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++ diag.span_suggestion(
++ e.span,
++ "try",
++ arg.as_ty(&to_ty.to_string()).to_string(),
++ Applicability::Unspecified,
++ );
++ }
++ },
++ ),
++ (&ty::Float(_), &ty::Ref(..))
++ | (&ty::Float(_), &ty::RawPtr(_))
++ | (&ty::Char, &ty::Ref(..))
++ | (&ty::Char, &ty::RawPtr(_)) => span_lint(
++ cx,
++ WRONG_TRANSMUTE,
++ e.span,
++ &format!("transmute from a `{}` to a pointer", from_ty),
++ ),
++ (&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
++ cx,
++ CROSSPOINTER_TRANSMUTE,
++ e.span,
++ &format!(
++ "transmute from a type (`{}`) to the type that it points to (`{}`)",
++ from_ty, to_ty
++ ),
++ ),
++ (_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
++ cx,
++ CROSSPOINTER_TRANSMUTE,
++ e.span,
++ &format!(
++ "transmute from a type (`{}`) to a pointer to that type (`{}`)",
++ from_ty, to_ty
++ ),
++ ),
++ (&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then(
++ cx,
++ TRANSMUTE_PTR_TO_REF,
++ e.span,
++ &format!(
++ "transmute from a pointer type (`{}`) to a reference type \
++ (`{}`)",
++ from_ty, to_ty
++ ),
++ |diag| {
++ let arg = sugg::Sugg::hir(cx, &args[0], "..");
++ let (deref, cast) = if mutbl == Mutability::Mut {
++ ("&mut *", "*mut")
++ } else {
++ ("&*", "*const")
++ };
++
++ let arg = if from_pty.ty == to_ref_ty {
++ arg
++ } else {
++ arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty)))
++ };
++
++ diag.span_suggestion(
++ e.span,
++ "try",
++ sugg::make_unop(deref, arg).to_string(),
++ Applicability::Unspecified,
++ );
++ },
++ ),
++ (&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => {
++ span_lint_and_then(
++ cx,
++ TRANSMUTE_INT_TO_CHAR,
++ e.span,
++ &format!("transmute from a `{}` to a `char`", from_ty),
++ |diag| {
++ let arg = sugg::Sugg::hir(cx, &args[0], "..");
++ let arg = if let ty::Int(_) = from_ty.kind {
++ arg.as_ty(ast::UintTy::U32.name_str())
++ } else {
++ arg
++ };
++ diag.span_suggestion(
++ e.span,
++ "consider using",
++ format!("std::char::from_u32({}).unwrap()", arg.to_string()),
++ Applicability::Unspecified,
++ );
++ },
++ )
++ },
++ (&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => {
++ if_chain! {
++ if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind);
++ if let ty::Uint(ast::UintTy::U8) = slice_ty.kind;
++ if from_mutbl == to_mutbl;
++ then {
++ let postfix = if from_mutbl == Mutability::Mut {
++ "_mut"
++ } else {
++ ""
++ };
++
++ span_lint_and_sugg(
++ cx,
++ TRANSMUTE_BYTES_TO_STR,
++ e.span,
++ &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++ "consider using",
++ format!(
++ "std::str::from_utf8{}({}).unwrap()",
++ postfix,
++ snippet(cx, args[0].span, ".."),
++ ),
++ Applicability::Unspecified,
++ );
++ } else {
++ if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) {
++ span_lint_and_then(
++ cx,
++ TRANSMUTE_PTR_TO_PTR,
++ e.span,
++ "transmute from a reference to a reference",
++ |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++ let ty_from_and_mut = ty::TypeAndMut {
++ ty: ty_from,
++ mutbl: from_mutbl
++ };
++ let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl };
++ let sugg_paren = arg
++ .as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
++ .as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
++ let sugg = if to_mutbl == Mutability::Mut {
++ sugg_paren.mut_addr_deref()
++ } else {
++ sugg_paren.addr_deref()
++ };
++ diag.span_suggestion(
++ e.span,
++ "try",
++ sugg.to_string(),
++ Applicability::Unspecified,
++ );
++ },
++ )
++ }
++ }
++ }
++ },
++ (&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then(
++ cx,
++ TRANSMUTE_PTR_TO_PTR,
++ e.span,
++ "transmute from a pointer to a pointer",
++ |diag| {
++ if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++ let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty));
++ diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
++ }
++ },
++ ),
++ (&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => {
++ span_lint_and_then(
++ cx,
++ TRANSMUTE_INT_TO_BOOL,
++ e.span,
++ &format!("transmute from a `{}` to a `bool`", from_ty),
++ |diag| {
++ let arg = sugg::Sugg::hir(cx, &args[0], "..");
++ let zero = sugg::Sugg::NonParen(Cow::from("0"));
++ diag.span_suggestion(
++ e.span,
++ "consider using",
++ sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(),
++ Applicability::Unspecified,
++ );
++ },
++ )
++ },
++ (&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then(
++ cx,
++ TRANSMUTE_INT_TO_FLOAT,
++ e.span,
++ &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++ |diag| {
++ let arg = sugg::Sugg::hir(cx, &args[0], "..");
++ let arg = if let ty::Int(int_ty) = from_ty.kind {
++ arg.as_ty(format!(
++ "u{}",
++ int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
++ ))
++ } else {
++ arg
++ };
++ diag.span_suggestion(
++ e.span,
++ "consider using",
++ format!("{}::from_bits({})", to_ty, arg.to_string()),
++ Applicability::Unspecified,
++ );
++ },
++ ),
++ (&ty::Float(float_ty), &ty::Int(_)) | (&ty::Float(float_ty), &ty::Uint(_)) => span_lint_and_then(
++ cx,
++ TRANSMUTE_FLOAT_TO_INT,
++ e.span,
++ &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++ |diag| {
++ let mut expr = &args[0];
++ let mut arg = sugg::Sugg::hir(cx, expr, "..");
++
++ if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
++ expr = &inner_expr;
++ }
++
++ if_chain! {
++ // if the expression is a float literal and it is unsuffixed then
++ // add a suffix so the suggestion is valid and unambiguous
++ let op = format!("{}{}", arg, float_ty.name_str()).into();
++ if let ExprKind::Lit(lit) = &expr.kind;
++ if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
++ then {
++ match arg {
++ sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
++ _ => arg = sugg::Sugg::NonParen(op)
++ }
++ }
++ }
++
++ arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
++
++ // cast the result of `to_bits` if `to_ty` is signed
++ arg = if let ty::Int(int_ty) = to_ty.kind {
++ arg.as_ty(int_ty.name_str().to_string())
++ } else {
++ arg
++ };
++
++ diag.span_suggestion(
++ e.span,
++ "consider using",
++ arg.to_string(),
++ Applicability::Unspecified,
++ );
++ },
++ ),
++ (&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => {
++ if from_adt.did != to_adt.did ||
++ !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) {
++ return;
++ }
++ if from_substs.types().zip(to_substs.types())
++ .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) {
++ span_lint(
++ cx,
++ UNSOUND_COLLECTION_TRANSMUTE,
++ e.span,
++ &format!(
++ "transmute from `{}` to `{}` with mismatched layout is unsound",
++ from_ty,
++ to_ty
++ )
++ );
++ }
++ },
++ _ => return,
++ }
++ }
++ }
++ }
++}
++
++/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
++/// not available , use
++/// the type's `ToString` implementation. In weird cases it could lead to types
++/// with invalid `'_`
++/// lifetime, but it should be rare.
++fn get_type_snippet(cx: &LateContext<'_, '_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
++ let seg = last_path_segment(path);
++ if_chain! {
++ if let Some(ref params) = seg.args;
++ if !params.parenthesized;
++ if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ }).nth(1);
++ if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
++ then {
++ return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
++ }
++ }
++
++ to_ref_ty.to_string()
++}
++
++// check if the component types of the transmuted collection and the result have different ABI,
++// size or alignment
++fn is_layout_incompatible<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
++ let empty_param_env = ty::ParamEnv::empty();
++ // check if `from` and `to` are normalizable to avoid ICE (#4968)
++ if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
++ return false;
++ }
++ let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
++ let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
++ if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
++ from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
++ } else {
++ // no idea about layout, so don't lint
++ false
++ }
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant_context, Constant};
++use crate::utils::{match_qpath, paths, span_lint};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for transmute calls which would receive a null pointer.
++ ///
++ /// **Why is this bad?** Transmuting a null pointer is undefined behavior.
++ ///
++ /// **Known problems:** Not all cases can be detected at the moment of this writing.
++ /// For example, variables which hold a null pointer and are then fed to a `transmute`
++ /// call, aren't detectable yet.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
++ /// ```
++ pub TRANSMUTING_NULL,
++ correctness,
++ "transmutes from a null pointer to a reference, which is undefined behavior"
++}
++
++declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
++
++const LINT_MSG: &str = "transmuting a known null pointer into a reference.";
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TransmutingNull {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if in_external_macro(cx.sess(), expr.span) {
++ return;
++ }
++
++ if_chain! {
++ if let ExprKind::Call(ref func, ref args) = expr.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
++ if args.len() == 1;
++
++ then {
++
++ // Catching transmute over constants that resolve to `null`.
++ let mut const_eval_context = constant_context(cx, cx.tables);
++ if_chain! {
++ if let ExprKind::Path(ref _qpath) = args[0].kind;
++ let x = const_eval_context.expr(&args[0]);
++ if let Some(constant) = x;
++ if let Constant::RawPtr(0) = constant;
++ then {
++ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++ }
++ }
++
++ // Catching:
++ // `std::mem::transmute(0 as *const i32)`
++ if_chain! {
++ if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind;
++ if let ExprKind::Lit(ref lit) = inner_expr.kind;
++ if let LitKind::Int(0, _) = lit.node;
++ then {
++ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++ }
++ }
++
++ // Catching:
++ // `std::mem::transmute(std::ptr::null::<i32>())`
++ if_chain! {
++ if let ExprKind::Call(ref func1, ref args1) = args[0].kind;
++ if let ExprKind::Path(ref path1) = func1.kind;
++ if match_qpath(path1, &paths::STD_PTR_NULL);
++ if args1.is_empty();
++ then {
++ span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++ }
++ }
++
++ // FIXME:
++ // Also catch transmutations of variables which are known nulls.
++ // To do this, MIR const propagation seems to be the better tool.
++ // Whenever MIR const prop routines are more developed, this will
++ // become available. As of this writing (25/03/19) it is not yet.
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use std::cmp;
++
++use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::config::Config as SessionConfig;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++use rustc_target::abi::LayoutOf;
++use rustc_target::spec::abi::Abi;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for functions taking arguments by reference, where
++ /// the argument type is `Copy` and small enough to be more efficient to always
++ /// pass by value.
++ ///
++ /// **Why is this bad?** In many calling conventions instances of structs will
++ /// be passed through registers if they fit into two or less general purpose
++ /// registers.
++ ///
++ /// **Known problems:** This lint is target register size dependent, it is
++ /// limited to 32-bit to try and reduce portability problems between 32 and
++ /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
++ /// will be different.
++ ///
++ /// The configuration option `trivial_copy_size_limit` can be set to override
++ /// this limit for a project.
++ ///
++ /// This lint attempts to allow passing arguments by reference if a reference
++ /// to that argument is returned. This is implemented by comparing the lifetime
++ /// of the argument and return value for equality. However, this can cause
++ /// false positives in cases involving multiple lifetimes that are bounded by
++ /// each other.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // Bad
++ /// fn foo(v: &u32) {}
++ /// ```
++ ///
++ /// ```rust
++ /// // Better
++ /// fn foo(v: u32) {}
++ /// ```
++ pub TRIVIALLY_COPY_PASS_BY_REF,
++ pedantic,
++ "functions taking small copyable arguments by reference"
++}
++
++#[derive(Copy, Clone)]
++pub struct TriviallyCopyPassByRef {
++ limit: u64,
++}
++
++impl<'a, 'tcx> TriviallyCopyPassByRef {
++ pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
++ let limit = limit.unwrap_or_else(|| {
++ let bit_width = u64::from(target.ptr_width);
++ // Cap the calculated bit width at 32-bits to reduce
++ // portability problems between 32 and 64-bit targets
++ let bit_width = cmp::min(bit_width, 32);
++ #[allow(clippy::integer_division)]
++ let byte_width = bit_width / 8;
++ // Use a limit of 2 times the register byte width
++ byte_width * 2
++ });
++ Self { limit }
++ }
++
++ fn check_poly_fn(&mut self, cx: &LateContext<'_, 'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
++ let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++
++ let fn_sig = cx.tcx.fn_sig(fn_def_id);
++ let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
++
++ // Use lifetimes to determine if we're returning a reference to the
++ // argument. In that case we can't switch to pass-by-value as the
++ // argument will not live long enough.
++ let output_lts = match fn_sig.output().kind {
++ ty::Ref(output_lt, _, _) => vec![output_lt],
++ ty::Adt(_, substs) => substs.regions().collect(),
++ _ => vec![],
++ };
++
++ for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) {
++ // All spans generated from a proc-macro invocation are the same...
++ match span {
++ Some(s) if s == input.span => return,
++ _ => (),
++ }
++
++ if_chain! {
++ if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind;
++ if !output_lts.contains(&input_lt);
++ if is_copy(cx, ty);
++ if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
++ if size <= self.limit;
++ if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
++ then {
++ let value_type = if is_self_ty(decl_ty) {
++ "self".into()
++ } else {
++ snippet(cx, decl_ty.span, "_").into()
++ };
++ span_lint_and_sugg(
++ cx,
++ TRIVIALLY_COPY_PASS_BY_REF,
++ input.span,
++ &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit),
++ "consider passing by value instead",
++ value_type,
++ Applicability::Unspecified,
++ );
++ }
++ }
++ }
++ }
++}
++
++impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++ if item.span.from_expansion() {
++ return;
++ }
++
++ if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
++ self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
++ }
++ }
++
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ _body: &'tcx Body<'_>,
++ span: Span,
++ hir_id: HirId,
++ ) {
++ if span.from_expansion() {
++ return;
++ }
++
++ match kind {
++ FnKind::ItemFn(.., header, _, attrs) => {
++ if header.abi != Abi::Rust {
++ return;
++ }
++ for a in attrs {
++ if a.meta_item_list().is_some() && a.check_name(sym!(proc_macro_derive)) {
++ return;
++ }
++ }
++ },
++ FnKind::Method(..) => (),
++ _ => return,
++ }
++
++ // Exclude non-inherent impls
++ if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
++ if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
++ ItemKind::Trait(..))
++ {
++ return;
++ }
++ }
++
++ self.check_poly_fn(cx, hir_id, decl, Some(span));
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::Ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usages of `Err(x)?`.
++ ///
++ /// **Why is this bad?** The `?` operator is designed to allow calls that
++ /// can fail to be easily chained. For example, `foo()?.bar()` or
++ /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
++ /// always return), it is more clear to write `return Err(x)`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn foo(fail: bool) -> Result<i32, String> {
++ /// if fail {
++ /// Err("failed")?;
++ /// }
++ /// Ok(0)
++ /// }
++ /// ```
++ /// Could be written:
++ ///
++ /// ```rust
++ /// fn foo(fail: bool) -> Result<i32, String> {
++ /// if fail {
++ /// return Err("failed".into());
++ /// }
++ /// Ok(0)
++ /// }
++ /// ```
++ pub TRY_ERR,
++ style,
++ "return errors explicitly rather than hiding them behind a `?`"
++}
++
++declare_lint_pass!(TryErr => [TRY_ERR]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TryErr {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // Looks for a structure like this:
++ // match ::std::ops::Try::into_result(Err(5)) {
++ // ::std::result::Result::Err(err) =>
++ // #[allow(unreachable_code)]
++ // return ::std::ops::Try::from_error(::std::convert::From::from(err)),
++ // ::std::result::Result::Ok(val) =>
++ // #[allow(unreachable_code)]
++ // val,
++ // };
++ if_chain! {
++ if !in_external_macro(cx.tcx.sess, expr.span);
++ if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind;
++ if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind;
++ if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
++ if match_qpath(match_fun_path, &paths::TRY_INTO_RESULT);
++ if let Some(ref try_arg) = try_args.get(0);
++ if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind;
++ if let Some(ref err_arg) = err_args.get(0);
++ if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
++ if match_qpath(err_fun_path, &paths::RESULT_ERR);
++ if let Some(return_type) = find_err_return_type(cx, &expr.kind);
++
++ then {
++ let err_type = cx.tables.expr_ty(err_arg);
++ let origin_snippet = if err_arg.span.from_expansion() {
++ snippet_with_macro_callsite(cx, err_arg.span, "_")
++ } else {
++ snippet(cx, err_arg.span, "_")
++ };
++ let suggestion = if err_type == return_type {
++ format!("return Err({})", origin_snippet)
++ } else {
++ format!("return Err({}.into())", origin_snippet)
++ };
++
++ span_lint_and_sugg(
++ cx,
++ TRY_ERR,
++ expr.span,
++ "returning an `Err(_)` with the `?` operator",
++ "try this",
++ suggestion,
++ Applicability::MachineApplicable
++ );
++ }
++ }
++ }
++}
++
++// In order to determine whether to suggest `.into()` or not, we need to find the error type the
++// function returns. To do that, we look for the From::from call (see tree above), and capture
++// its output type.
++fn find_err_return_type<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
++ if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr {
++ arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty))
++ } else {
++ None
++ }
++}
++
++// Check for From::from in one of the match arms.
++fn find_err_return_type_arm<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arm: &'tcx Arm<'_>) -> Option<Ty<'tcx>> {
++ if_chain! {
++ if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind;
++ if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind;
++ if let ExprKind::Path(ref from_error_fn) = from_error_path.kind;
++ if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR);
++ if let Some(from_error_arg) = from_error_args.get(0);
++ then {
++ Some(cx.tables.expr_ty(from_error_arg))
++ } else {
++ None
++ }
++ }
++}
--- /dev/null
--- /dev/null
++#![allow(rustc::default_hash_types)]
++
++use std::borrow::Cow;
++use std::cmp::Ordering;
++use std::collections::BTreeMap;
++
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy};
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_hir as hir;
++use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{
++ BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
++ ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn,
++ TraitItem, TraitItemKind, TyKind, UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables};
++use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
++use rustc_span::hygiene::{ExpnKind, MacroKind};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::sym;
++use rustc_target::abi::LayoutOf;
++use rustc_target::spec::abi::Abi;
++use rustc_typeck::hir_ty_to_ty;
++
++use crate::consts::{constant, Constant};
++use crate::utils::paths;
++use crate::utils::{
++ clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item,
++ last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
++ qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
++ span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
++};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
++ ///
++ /// **Why is this bad?** `Vec` already keeps its contents in a separate area on
++ /// the heap. So if you `Box` it, you just add another level of indirection
++ /// without any benefit whatsoever.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// struct X {
++ /// values: Box<Vec<Foo>>,
++ /// }
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust,ignore
++ /// struct X {
++ /// values: Vec<Foo>,
++ /// }
++ /// ```
++ pub BOX_VEC,
++ perf,
++ "usage of `Box<Vec<T>>`, vector elements are already on the heap"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
++ ///
++ /// **Why is this bad?** `Vec` already keeps its contents in a separate area on
++ /// the heap. So if you `Box` its contents, you just add another level of indirection.
++ ///
++ /// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see #3530,
++ /// 1st comment).
++ ///
++ /// **Example:**
++ /// ```rust
++ /// struct X {
++ /// values: Vec<Box<i32>>,
++ /// }
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// struct X {
++ /// values: Vec<i32>,
++ /// }
++ /// ```
++ pub VEC_BOX,
++ complexity,
++ "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
++ /// definitions
++ ///
++ /// **Why is this bad?** `Option<_>` represents an optional value. `Option<Option<_>>`
++ /// represents an optional optional value which is logically the same thing as an optional
++ /// value but has an unneeded extra level of wrapping.
++ ///
++ /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
++ /// consider a custom `enum` instead, with clear names for each case.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example**
++ /// ```rust
++ /// fn get_data() -> Option<Option<u32>> {
++ /// None
++ /// }
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// pub enum Contents {
++ /// Data(Vec<u8>), // Was Some(Some(Vec<u8>))
++ /// NotYetFetched, // Was Some(None)
++ /// None, // Was None
++ /// }
++ ///
++ /// fn get_data() -> Contents {
++ /// Contents::None
++ /// }
++ /// ```
++ pub OPTION_OPTION,
++ pedantic,
++ "usage of `Option<Option<T>>`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a
++ /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
++ ///
++ /// **Why is this bad?** Gankro says:
++ ///
++ /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
++ /// pointers and indirection.
++ /// > It wastes memory, it has terrible cache locality, and is all-around slow.
++ /// `RingBuf`, while
++ /// > "only" amortized for push/pop, should be faster in the general case for
++ /// almost every possible
++ /// > workload, and isn't even amortized at all if you can predict the capacity
++ /// you need.
++ /// >
++ /// > `LinkedList`s are only really good if you're doing a lot of merging or
++ /// splitting of lists.
++ /// > This is because they can just mangle some pointers instead of actually
++ /// copying the data. Even
++ /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
++ /// can still be better
++ /// > because of how expensive it is to seek to the middle of a `LinkedList`.
++ ///
++ /// **Known problems:** False positives – the instances where using a
++ /// `LinkedList` makes sense are few and far between, but they can still happen.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::collections::LinkedList;
++ /// let x: LinkedList<usize> = LinkedList::new();
++ /// ```
++ pub LINKEDLIST,
++ pedantic,
++ "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
++ ///
++ /// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more
++ /// general.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// fn foo(bar: &Box<T>) { ... }
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust,ignore
++ /// fn foo(bar: &T) { ... }
++ /// ```
++ pub BORROWED_BOX,
++ complexity,
++ "a borrow of a boxed type"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of redundant allocations anywhere in the code.
++ ///
++ /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
++ /// add an unnecessary level of indirection.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::rc::Rc;
++ /// fn foo(bar: Rc<&usize>) {}
++ /// ```
++ ///
++ /// Better:
++ ///
++ /// ```rust
++ /// fn foo(bar: &usize) {}
++ /// ```
++ pub REDUNDANT_ALLOCATION,
++ perf,
++ "redundant allocation"
++}
++
++pub struct Types {
++ vec_box_size_threshold: u64,
++}
++
++impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'_, '_>,
++ _: FnKind<'_>,
++ decl: &FnDecl<'_>,
++ _: &Body<'_>,
++ _: Span,
++ id: HirId,
++ ) {
++ // Skip trait implementations; see issue #605.
++ if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) {
++ if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
++ return;
++ }
++ }
++
++ self.check_fn_decl(cx, decl);
++ }
++
++ fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, field: &hir::StructField<'_>) {
++ self.check_ty(cx, &field.ty, false);
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, item: &TraitItem<'_>) {
++ match item.kind {
++ TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false),
++ TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl),
++ _ => (),
++ }
++ }
++
++ fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
++ if let Some(ref ty) = local.ty {
++ self.check_ty(cx, ty, true);
++ }
++ }
++}
++
++/// Checks if `qpath` has last segment with type parameter matching `path`
++fn match_type_parameter(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, path: &[&str]) -> Option<Span> {
++ let last = last_path_segment(qpath);
++ if_chain! {
++ if let Some(ref params) = last.args;
++ if !params.parenthesized;
++ if let Some(ty) = params.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ if let TyKind::Path(ref qpath) = ty.kind;
++ if let Some(did) = qpath_res(cx, qpath, ty.hir_id).opt_def_id();
++ if match_def_path(cx, did, path);
++ then {
++ return Some(ty.span);
++ }
++ }
++ None
++}
++
++fn match_borrows_parameter(_cx: &LateContext<'_, '_>, qpath: &QPath<'_>) -> Option<Span> {
++ let last = last_path_segment(qpath);
++ if_chain! {
++ if let Some(ref params) = last.args;
++ if !params.parenthesized;
++ if let Some(ty) = params.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ if let TyKind::Rptr(..) = ty.kind;
++ then {
++ return Some(ty.span);
++ }
++ }
++ None
++}
++
++impl Types {
++ pub fn new(vec_box_size_threshold: u64) -> Self {
++ Self { vec_box_size_threshold }
++ }
++
++ fn check_fn_decl(&mut self, cx: &LateContext<'_, '_>, decl: &FnDecl<'_>) {
++ for input in decl.inputs {
++ self.check_ty(cx, input, false);
++ }
++
++ if let FnRetTy::Return(ref ty) = decl.output {
++ self.check_ty(cx, ty, false);
++ }
++ }
++
++ /// Recursively check for `TypePass` lints in the given type. Stop at the first
++ /// lint found.
++ ///
++ /// The parameter `is_local` distinguishes the context of the type; types from
++ /// local bindings should only be checked for the `BORROWED_BOX` lint.
++ #[allow(clippy::too_many_lines)]
++ fn check_ty(&mut self, cx: &LateContext<'_, '_>, hir_ty: &hir::Ty<'_>, is_local: bool) {
++ if hir_ty.span.from_expansion() {
++ return;
++ }
++ match hir_ty.kind {
++ TyKind::Path(ref qpath) if !is_local => {
++ let hir_id = hir_ty.hir_id;
++ let res = qpath_res(cx, qpath, hir_id);
++ if let Some(def_id) = res.opt_def_id() {
++ if Some(def_id) == cx.tcx.lang_items().owned_box() {
++ if let Some(span) = match_borrows_parameter(cx, qpath) {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_ALLOCATION,
++ hir_ty.span,
++ "usage of `Box<&T>`",
++ "try",
++ snippet(cx, span, "..").to_string(),
++ Applicability::MachineApplicable,
++ );
++ return; // don't recurse into the type
++ }
++ if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
++ span_lint_and_help(
++ cx,
++ BOX_VEC,
++ hir_ty.span,
++ "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
++ None,
++ "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.",
++ );
++ return; // don't recurse into the type
++ }
++ } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
++ if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_ALLOCATION,
++ hir_ty.span,
++ "usage of `Rc<Rc<T>>`",
++ "try",
++ snippet(cx, span, "..").to_string(),
++ Applicability::MachineApplicable,
++ );
++ return; // don't recurse into the type
++ }
++ if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_ALLOCATION,
++ hir_ty.span,
++ "usage of `Rc<Box<T>>`",
++ "try",
++ snippet(cx, span, "..").to_string(),
++ Applicability::MachineApplicable,
++ );
++ return; // don't recurse into the type
++ }
++ if let Some(span) = match_borrows_parameter(cx, qpath) {
++ span_lint_and_sugg(
++ cx,
++ REDUNDANT_ALLOCATION,
++ hir_ty.span,
++ "usage of `Rc<&T>`",
++ "try",
++ snippet(cx, span, "..").to_string(),
++ Applicability::MachineApplicable,
++ );
++ return; // don't recurse into the type
++ }
++ } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) {
++ if_chain! {
++ // Get the _ part of Vec<_>
++ if let Some(ref last) = last_path_segment(qpath).args;
++ if let Some(ty) = last.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ // ty is now _ at this point
++ if let TyKind::Path(ref ty_qpath) = ty.kind;
++ let res = qpath_res(cx, ty_qpath, ty.hir_id);
++ if let Some(def_id) = res.opt_def_id();
++ if Some(def_id) == cx.tcx.lang_items().owned_box();
++ // At this point, we know ty is Box<T>, now get T
++ if let Some(ref last) = last_path_segment(ty_qpath).args;
++ if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty);
++ if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env);
++ if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes());
++ if ty_ty_size <= self.vec_box_size_threshold;
++ then {
++ span_lint_and_sugg(
++ cx,
++ VEC_BOX,
++ hir_ty.span,
++ "`Vec<T>` is already on the heap, the boxing is unnecessary.",
++ "try",
++ format!("Vec<{}>", ty_ty),
++ Applicability::MachineApplicable,
++ );
++ return; // don't recurse into the type
++ }
++ }
++ } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) {
++ if match_type_parameter(cx, qpath, &paths::OPTION).is_some() {
++ span_lint(
++ cx,
++ OPTION_OPTION,
++ hir_ty.span,
++ "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
++ enum if you need to distinguish all 3 cases",
++ );
++ return; // don't recurse into the type
++ }
++ } else if match_def_path(cx, def_id, &paths::LINKED_LIST) {
++ span_lint_and_help(
++ cx,
++ LINKEDLIST,
++ hir_ty.span,
++ "I see you're using a LinkedList! Perhaps you meant some other data structure?",
++ None,
++ "a `VecDeque` might work",
++ );
++ return; // don't recurse into the type
++ }
++ }
++ match *qpath {
++ QPath::Resolved(Some(ref ty), ref p) => {
++ self.check_ty(cx, ty, is_local);
++ for ty in p.segments.iter().flat_map(|seg| {
++ seg.args
++ .as_ref()
++ .map_or_else(|| [].iter(), |params| params.args.iter())
++ .filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ })
++ }) {
++ self.check_ty(cx, ty, is_local);
++ }
++ },
++ QPath::Resolved(None, ref p) => {
++ for ty in p.segments.iter().flat_map(|seg| {
++ seg.args
++ .as_ref()
++ .map_or_else(|| [].iter(), |params| params.args.iter())
++ .filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ })
++ }) {
++ self.check_ty(cx, ty, is_local);
++ }
++ },
++ QPath::TypeRelative(ref ty, ref seg) => {
++ self.check_ty(cx, ty, is_local);
++ if let Some(ref params) = seg.args {
++ for ty in params.args.iter().filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ }) {
++ self.check_ty(cx, ty, is_local);
++ }
++ }
++ },
++ }
++ },
++ TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty),
++ // recurse
++ TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => {
++ self.check_ty(cx, ty, is_local)
++ },
++ TyKind::Tup(tys) => {
++ for ty in tys {
++ self.check_ty(cx, ty, is_local);
++ }
++ },
++ _ => {},
++ }
++ }
++
++ fn check_ty_rptr(
++ &mut self,
++ cx: &LateContext<'_, '_>,
++ hir_ty: &hir::Ty<'_>,
++ is_local: bool,
++ lt: &Lifetime,
++ mut_ty: &MutTy<'_>,
++ ) {
++ match mut_ty.ty.kind {
++ TyKind::Path(ref qpath) => {
++ let hir_id = mut_ty.ty.hir_id;
++ let def = qpath_res(cx, qpath, hir_id);
++ if_chain! {
++ if let Some(def_id) = def.opt_def_id();
++ if Some(def_id) == cx.tcx.lang_items().owned_box();
++ if let QPath::Resolved(None, ref path) = *qpath;
++ if let [ref bx] = *path.segments;
++ if let Some(ref params) = bx.args;
++ if !params.parenthesized;
++ if let Some(inner) = params.args.iter().find_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ });
++ then {
++ if is_any_trait(inner) {
++ // Ignore `Box<Any>` types; see issue #1884 for details.
++ return;
++ }
++
++ let ltopt = if lt.is_elided() {
++ String::new()
++ } else {
++ format!("{} ", lt.name.ident().as_str())
++ };
++
++ if mut_ty.mutbl == Mutability::Mut {
++ // Ignore `&mut Box<T>` types; see issue #2907 for
++ // details.
++ return;
++ }
++ let mut applicability = Applicability::MachineApplicable;
++ span_lint_and_sugg(
++ cx,
++ BORROWED_BOX,
++ hir_ty.span,
++ "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
++ "try",
++ format!(
++ "&{}{}",
++ ltopt,
++ &snippet_with_applicability(cx, inner.span, "..", &mut applicability)
++ ),
++ Applicability::Unspecified,
++ );
++ return; // don't recurse into the type
++ }
++ };
++ self.check_ty(cx, &mut_ty.ty, is_local);
++ },
++ _ => self.check_ty(cx, &mut_ty.ty, is_local),
++ }
++ }
++}
++
++// Returns true if given type is `Any` trait.
++fn is_any_trait(t: &hir::Ty<'_>) -> bool {
++ if_chain! {
++ if let TyKind::TraitObject(ref traits, _) = t.kind;
++ if !traits.is_empty();
++ // Only Send/Sync can be used as additional traits, so it is enough to
++ // check only the first trait.
++ if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT);
++ then {
++ return true;
++ }
++ }
++
++ false
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for binding a unit value.
++ ///
++ /// **Why is this bad?** A unit value cannot usefully be used anywhere. So
++ /// binding one is kind of pointless.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = {
++ /// 1;
++ /// };
++ /// ```
++ pub LET_UNIT_VALUE,
++ pedantic,
++ "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
++}
++
++declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnitValue {
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++ if let StmtKind::Local(ref local) = stmt.kind {
++ if is_unit(cx.tables.pat_ty(&local.pat)) {
++ if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
++ return;
++ }
++ if higher::is_from_for_desugar(local) {
++ return;
++ }
++ span_lint_and_then(
++ cx,
++ LET_UNIT_VALUE,
++ stmt.span,
++ "this let-binding has unit value",
++ |diag| {
++ if let Some(expr) = &local.init {
++ let snip = snippet_with_macro_callsite(cx, expr.span, "()");
++ diag.span_suggestion(
++ stmt.span,
++ "omit the `let` binding",
++ format!("{};", snip),
++ Applicability::MachineApplicable, // snippet
++ );
++ }
++ },
++ );
++ }
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons to unit. This includes all binary
++ /// comparisons (like `==` and `<`) and asserts.
++ ///
++ /// **Why is this bad?** Unit is always equal to itself, and thus is just a
++ /// clumsily written constant. Mostly this happens when someone accidentally
++ /// adds semicolons at the end of the operands.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # fn foo() {};
++ /// # fn bar() {};
++ /// # fn baz() {};
++ /// if {
++ /// foo();
++ /// } == {
++ /// bar();
++ /// } {
++ /// baz();
++ /// }
++ /// ```
++ /// is equal to
++ /// ```rust
++ /// # fn foo() {};
++ /// # fn bar() {};
++ /// # fn baz() {};
++ /// {
++ /// foo();
++ /// bar();
++ /// baz();
++ /// }
++ /// ```
++ ///
++ /// For asserts:
++ /// ```rust
++ /// # fn foo() {};
++ /// # fn bar() {};
++ /// assert_eq!({ foo(); }, { bar(); });
++ /// ```
++ /// will always succeed
++ pub UNIT_CMP,
++ correctness,
++ "comparing unit values"
++}
++
++declare_lint_pass!(UnitCmp => [UNIT_CMP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitCmp {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++ if expr.span.from_expansion() {
++ if let Some(callee) = expr.span.source_callee() {
++ if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
++ if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
++ let op = cmp.node;
++ if op.is_comparison() && is_unit(cx.tables.expr_ty(left)) {
++ let result = match &*symbol.as_str() {
++ "assert_eq" | "debug_assert_eq" => "succeed",
++ "assert_ne" | "debug_assert_ne" => "fail",
++ _ => return,
++ };
++ span_lint(
++ cx,
++ UNIT_CMP,
++ expr.span,
++ &format!(
++ "`{}` of unit values detected. This will always {}",
++ symbol.as_str(),
++ result
++ ),
++ );
++ }
++ }
++ }
++ }
++ return;
++ }
++ if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
++ let op = cmp.node;
++ if op.is_comparison() && is_unit(cx.tables.expr_ty(left)) {
++ let result = match op {
++ BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true",
++ _ => "false",
++ };
++ span_lint(
++ cx,
++ UNIT_CMP,
++ expr.span,
++ &format!(
++ "{}-comparison of unit values detected. This will always be {}",
++ op.as_str(),
++ result
++ ),
++ );
++ }
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for passing a unit value as an argument to a function without using a
++ /// unit literal (`()`).
++ ///
++ /// **Why is this bad?** This is likely the result of an accidental semicolon.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// foo({
++ /// let a = bar();
++ /// baz(a);
++ /// })
++ /// ```
++ pub UNIT_ARG,
++ complexity,
++ "passing unit to a function"
++}
++
++declare_lint_pass!(UnitArg => [UNIT_ARG]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if expr.span.from_expansion() {
++ return;
++ }
++
++ // apparently stuff in the desugaring of `?` can trigger this
++ // so check for that here
++ // only the calls to `Try::from_error` is marked as desugared,
++ // so we need to check both the current Expr and its parent.
++ if is_questionmark_desugar_marked_call(expr) {
++ return;
++ }
++ if_chain! {
++ let map = &cx.tcx.hir();
++ let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
++ if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
++ if is_questionmark_desugar_marked_call(parent_expr);
++ then {
++ return;
++ }
++ }
++
++ match expr.kind {
++ ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => {
++ for arg in args {
++ if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) {
++ if let ExprKind::Match(.., match_source) = &arg.kind {
++ if *match_source == MatchSource::TryDesugar {
++ continue;
++ }
++ }
++
++ span_lint_and_sugg(
++ cx,
++ UNIT_ARG,
++ arg.span,
++ "passing a unit value to a function",
++ "if you intended to pass a unit value, use a unit literal instead",
++ "()".to_string(),
++ Applicability::MaybeIncorrect,
++ );
++ }
++ }
++ },
++ _ => (),
++ }
++ }
++}
++
++fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
++ use rustc_span::hygiene::DesugaringKind;
++ if let ExprKind::Call(ref callee, _) = expr.kind {
++ callee.span.is_desugaring(DesugaringKind::QuestionMark)
++ } else {
++ false
++ }
++}
++
++fn is_unit(ty: Ty<'_>) -> bool {
++ match ty.kind {
++ ty::Tuple(slice) if slice.is_empty() => true,
++ _ => false,
++ }
++}
++
++fn is_unit_literal(expr: &Expr<'_>) -> bool {
++ match expr.kind {
++ ExprKind::Tup(ref slice) if slice.is_empty() => true,
++ _ => false,
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts from any numerical to a float type where
++ /// the receiving type cannot store all values from the original type without
++ /// rounding errors. This possible rounding is to be expected, so this lint is
++ /// `Allow` by default.
++ ///
++ /// Basically, this warns on casting any integer with 32 or more bits to `f32`
++ /// or any 64-bit integer to `f64`.
++ ///
++ /// **Why is this bad?** It's not bad at all. But in some applications it can be
++ /// helpful to know where precision loss can take place. This lint can help find
++ /// those places in the code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = u64::MAX;
++ /// x as f64;
++ /// ```
++ pub CAST_PRECISION_LOSS,
++ pedantic,
++ "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts from a signed to an unsigned numerical
++ /// type. In this case, negative values wrap around to large positive values,
++ /// which can be quite surprising in practice. However, as the cast works as
++ /// defined, this lint is `Allow` by default.
++ ///
++ /// **Why is this bad?** Possibly surprising results. You can activate this lint
++ /// as a one-time check to see where numerical wrapping can arise.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let y: i8 = -1;
++ /// y as u128; // will return 18446744073709551615
++ /// ```
++ pub CAST_SIGN_LOSS,
++ pedantic,
++ "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts between numerical types that may
++ /// truncate large values. This is expected behavior, so the cast is `Allow` by
++ /// default.
++ ///
++ /// **Why is this bad?** In some problem domains, it is good practice to avoid
++ /// truncation. This lint can be activated to help assess where additional
++ /// checks could be beneficial.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn as_u8(x: u64) -> u8 {
++ /// x as u8
++ /// }
++ /// ```
++ pub CAST_POSSIBLE_TRUNCATION,
++ pedantic,
++ "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts from an unsigned type to a signed type of
++ /// the same size. Performing such a cast is a 'no-op' for the compiler,
++ /// i.e., nothing is changed at the bit level, and the binary representation of
++ /// the value is reinterpreted. This can cause wrapping if the value is too big
++ /// for the target signed type. However, the cast works as defined, so this lint
++ /// is `Allow` by default.
++ ///
++ /// **Why is this bad?** While such a cast is not bad in itself, the results can
++ /// be surprising when this is not the intended behavior, as demonstrated by the
++ /// example below.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// u32::MAX as i32; // will yield a value of `-1`
++ /// ```
++ pub CAST_POSSIBLE_WRAP,
++ pedantic,
++ "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts between numerical types that may
++ /// be replaced by safe conversion functions.
++ ///
++ /// **Why is this bad?** Rust's `as` keyword will perform many kinds of
++ /// conversions, including silently lossy conversions. Conversion functions such
++ /// as `i32::from` will only perform lossless conversions. Using the conversion
++ /// functions prevents conversions from turning into silent lossy conversions if
++ /// the types of the input expressions ever change, and make it easier for
++ /// people reading the code to know that the conversion is lossless.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// fn as_u64(x: u8) -> u64 {
++ /// x as u64
++ /// }
++ /// ```
++ ///
++ /// Using `::from` would look like this:
++ ///
++ /// ```rust
++ /// fn as_u64(x: u8) -> u64 {
++ /// u64::from(x)
++ /// }
++ /// ```
++ pub CAST_LOSSLESS,
++ pedantic,
++ "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts to the same type.
++ ///
++ /// **Why is this bad?** It's just unnecessary.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let _ = 2i32 as i32;
++ /// ```
++ pub UNNECESSARY_CAST,
++ complexity,
++ "cast to the same type, e.g., `x as i32` where `x: i32`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a
++ /// more-strictly-aligned pointer
++ ///
++ /// **Why is this bad?** Dereferencing the resulting pointer may be undefined
++ /// behavior.
++ ///
++ /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
++ /// on the resulting pointer is fine.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let _ = (&1u8 as *const u8) as *const u16;
++ /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
++ /// ```
++ pub CAST_PTR_ALIGNMENT,
++ correctness,
++ "cast from a pointer to a more-strictly-aligned pointer"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts of function pointers to something other than usize
++ ///
++ /// **Why is this bad?**
++ /// Casting a function pointer to anything other than usize/isize is not portable across
++ /// architectures, because you end up losing bits if the target type is too small or end up with a
++ /// bunch of extra bits that waste space and add more instructions to the final binary than
++ /// strictly necessary for the problem
++ ///
++ /// Casting to isize also doesn't make sense since there are no signed addresses.
++ ///
++ /// **Example**
++ ///
++ /// ```rust
++ /// // Bad
++ /// fn fun() -> i32 { 1 }
++ /// let a = fun as i64;
++ ///
++ /// // Good
++ /// fn fun2() -> i32 { 1 }
++ /// let a = fun2 as usize;
++ /// ```
++ pub FN_TO_NUMERIC_CAST,
++ style,
++ "casting a function pointer to a numeric type other than usize"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to
++ /// store address.
++ ///
++ /// **Why is this bad?**
++ /// Such a cast discards some bits of the function's address. If this is intended, it would be more
++ /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
++ /// a comment) to perform the truncation.
++ ///
++ /// **Example**
++ ///
++ /// ```rust
++ /// // Bad
++ /// fn fn1() -> i16 {
++ /// 1
++ /// };
++ /// let _ = fn1 as i32;
++ ///
++ /// // Better: Cast to usize first, then comment with the reason for the truncation
++ /// fn fn2() -> i16 {
++ /// 1
++ /// };
++ /// let fn_ptr = fn2 as usize;
++ /// let fn_ptr_truncated = fn_ptr as i32;
++ /// ```
++ pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++ style,
++ "casting a function pointer to a numeric type not wide enough to store the address"
++}
++
++/// Returns the size in bits of an integral type.
++/// Will return 0 if the type is not an int or uint variant
++fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
++ match typ.kind {
++ ty::Int(i) => match i {
++ IntTy::Isize => tcx.data_layout.pointer_size.bits(),
++ IntTy::I8 => 8,
++ IntTy::I16 => 16,
++ IntTy::I32 => 32,
++ IntTy::I64 => 64,
++ IntTy::I128 => 128,
++ },
++ ty::Uint(i) => match i {
++ UintTy::Usize => tcx.data_layout.pointer_size.bits(),
++ UintTy::U8 => 8,
++ UintTy::U16 => 16,
++ UintTy::U32 => 32,
++ UintTy::U64 => 64,
++ UintTy::U128 => 128,
++ },
++ _ => 0,
++ }
++}
++
++fn is_isize_or_usize(typ: Ty<'_>) -> bool {
++ match typ.kind {
++ ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true,
++ _ => false,
++ }
++}
++
++fn span_precision_loss_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
++ let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
++ let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
++ let arch_dependent_str = "on targets with 64-bit wide pointers ";
++ let from_nbits_str = if arch_dependent {
++ "64".to_owned()
++ } else if is_isize_or_usize(cast_from) {
++ "32 or 64".to_owned()
++ } else {
++ int_ty_to_nbits(cast_from, cx.tcx).to_string()
++ };
++ span_lint(
++ cx,
++ CAST_PRECISION_LOSS,
++ expr.span,
++ &format!(
++ "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \
++ but `{1}`'s mantissa is only {4} bits wide)",
++ cast_from,
++ if cast_to_f64 { "f64" } else { "f32" },
++ if arch_dependent { arch_dependent_str } else { "" },
++ from_nbits_str,
++ mantissa_nbits
++ ),
++ );
++}
++
++fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool {
++ if let ExprKind::Binary(_, _, _) = op.kind {
++ if snip.starts_with('(') && snip.ends_with(')') {
++ return true;
++ }
++ }
++ false
++}
++
++fn span_lossless_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++ // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
++ if in_constant(cx, expr.hir_id) {
++ return;
++ }
++ // The suggestion is to use a function call, so if the original expression
++ // has parens on the outside, they are no longer needed.
++ let mut applicability = Applicability::MachineApplicable;
++ let opt = snippet_opt(cx, op.span);
++ let sugg = if let Some(ref snip) = opt {
++ if should_strip_parens(op, snip) {
++ &snip[1..snip.len() - 1]
++ } else {
++ snip.as_str()
++ }
++ } else {
++ applicability = Applicability::HasPlaceholders;
++ ".."
++ };
++
++ span_lint_and_sugg(
++ cx,
++ CAST_LOSSLESS,
++ expr.span,
++ &format!(
++ "casting `{}` to `{}` may become silently lossy if you later change the type",
++ cast_from, cast_to
++ ),
++ "try",
++ format!("{}::from({})", cast_to, sugg),
++ applicability,
++ );
++}
++
++enum ArchSuffix {
++ _32,
++ _64,
++ None,
++}
++
++fn check_loss_of_sign(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++ if !cast_from.is_signed() || cast_to.is_signed() {
++ return;
++ }
++
++ // don't lint for positive constants
++ let const_val = constant(cx, &cx.tables, op);
++ if_chain! {
++ if let Some((const_val, _)) = const_val;
++ if let Constant::Int(n) = const_val;
++ if let ty::Int(ity) = cast_from.kind;
++ if sext(cx.tcx, n, ity) >= 0;
++ then {
++ return
++ }
++ }
++
++ // don't lint for the result of methods that always return non-negative values
++ if let ExprKind::MethodCall(ref path, _, _) = op.kind {
++ let mut method_name = path.ident.name.as_str();
++ let whitelisted_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
++
++ if_chain! {
++ if method_name == "unwrap";
++ if let Some(arglist) = method_chain_args(op, &["unwrap"]);
++ if let ExprKind::MethodCall(ref inner_path, _, _) = &arglist[0][0].kind;
++ then {
++ method_name = inner_path.ident.name.as_str();
++ }
++ }
++
++ if whitelisted_methods.iter().any(|&name| method_name == name) {
++ return;
++ }
++ }
++
++ span_lint(
++ cx,
++ CAST_SIGN_LOSS,
++ expr.span,
++ &format!(
++ "casting `{}` to `{}` may lose the sign of the value",
++ cast_from, cast_to
++ ),
++ );
++}
++
++fn check_truncation_and_wrapping(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++ let arch_64_suffix = " on targets with 64-bit wide pointers";
++ let arch_32_suffix = " on targets with 32-bit wide pointers";
++ let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
++ let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++ let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++ let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) =
++ match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
++ (true, true) | (false, false) => (
++ to_nbits < from_nbits,
++ ArchSuffix::None,
++ to_nbits == from_nbits && cast_unsigned_to_signed,
++ ArchSuffix::None,
++ ),
++ (true, false) => (
++ to_nbits <= 32,
++ if to_nbits == 32 {
++ ArchSuffix::_64
++ } else {
++ ArchSuffix::None
++ },
++ to_nbits <= 32 && cast_unsigned_to_signed,
++ ArchSuffix::_32,
++ ),
++ (false, true) => (
++ from_nbits == 64,
++ ArchSuffix::_32,
++ cast_unsigned_to_signed,
++ if from_nbits == 64 {
++ ArchSuffix::_64
++ } else {
++ ArchSuffix::_32
++ },
++ ),
++ };
++ if span_truncation {
++ span_lint(
++ cx,
++ CAST_POSSIBLE_TRUNCATION,
++ expr.span,
++ &format!(
++ "casting `{}` to `{}` may truncate the value{}",
++ cast_from,
++ cast_to,
++ match suffix_truncation {
++ ArchSuffix::_32 => arch_32_suffix,
++ ArchSuffix::_64 => arch_64_suffix,
++ ArchSuffix::None => "",
++ }
++ ),
++ );
++ }
++ if span_wrap {
++ span_lint(
++ cx,
++ CAST_POSSIBLE_WRAP,
++ expr.span,
++ &format!(
++ "casting `{}` to `{}` may wrap around the value{}",
++ cast_from,
++ cast_to,
++ match suffix_wrap {
++ ArchSuffix::_32 => arch_32_suffix,
++ ArchSuffix::_64 => arch_64_suffix,
++ ArchSuffix::None => "",
++ }
++ ),
++ );
++ }
++}
++
++fn check_lossless(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++ let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
++ let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++ let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++ if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned
++ {
++ span_lossless_lint(cx, expr, op, cast_from, cast_to);
++ }
++}
++
++declare_lint_pass!(Casts => [
++ CAST_PRECISION_LOSS,
++ CAST_SIGN_LOSS,
++ CAST_POSSIBLE_TRUNCATION,
++ CAST_POSSIBLE_WRAP,
++ CAST_LOSSLESS,
++ UNNECESSARY_CAST,
++ CAST_PTR_ALIGNMENT,
++ FN_TO_NUMERIC_CAST,
++ FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++]);
++
++// Check if the given type is either `core::ffi::c_void` or
++// one of the platform specific `libc::<platform>::c_void` of libc.
++fn is_c_void(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++ if let ty::Adt(adt, _) = ty.kind {
++ let names = cx.get_def_path(adt.did);
++
++ if names.is_empty() {
++ return false;
++ }
++ if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
++ return true;
++ }
++ }
++ false
++}
++
++/// Returns the mantissa bits wide of a fp type.
++/// Will return 0 if the type is not a fp
++fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
++ match typ.kind {
++ ty::Float(FloatTy::F32) => 23,
++ ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
++ _ => 0,
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if expr.span.from_expansion() {
++ return;
++ }
++ if let ExprKind::Cast(ref ex, _) = expr.kind {
++ let (cast_from, cast_to) = (cx.tables.expr_ty(ex), cx.tables.expr_ty(expr));
++ lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
++ if let ExprKind::Lit(ref lit) = ex.kind {
++ if_chain! {
++ if let LitKind::Int(n, _) = lit.node;
++ if let Some(src) = snippet_opt(cx, lit.span);
++ if cast_to.is_floating_point();
++ if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
++ let from_nbits = 128 - n.leading_zeros();
++ let to_nbits = fp_ty_mantissa_nbits(cast_to);
++ if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
++ then {
++ span_lint_and_sugg(
++ cx,
++ UNNECESSARY_CAST,
++ expr.span,
++ &format!("casting integer literal to `{}` is unnecessary", cast_to),
++ "try",
++ format!("{}_{}", n, cast_to),
++ Applicability::MachineApplicable,
++ );
++ return;
++ }
++ }
++ match lit.node {
++ LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
++ _ => {
++ if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) {
++ span_lint(
++ cx,
++ UNNECESSARY_CAST,
++ expr.span,
++ &format!(
++ "casting to the same type is unnecessary (`{}` -> `{}`)",
++ cast_from, cast_to
++ ),
++ );
++ }
++ },
++ }
++ }
++ if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
++ lint_numeric_casts(cx, expr, ex, cast_from, cast_to);
++ }
++
++ lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
++ }
++ }
++}
++
++fn lint_numeric_casts<'tcx>(
++ cx: &LateContext<'_, 'tcx>,
++ expr: &Expr<'tcx>,
++ cast_expr: &Expr<'_>,
++ cast_from: Ty<'tcx>,
++ cast_to: Ty<'tcx>,
++) {
++ match (cast_from.is_integral(), cast_to.is_integral()) {
++ (true, false) => {
++ let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++ let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind {
++ 32
++ } else {
++ 64
++ };
++ if is_isize_or_usize(cast_from) || from_nbits >= to_nbits {
++ span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64);
++ }
++ if from_nbits < to_nbits {
++ span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
++ }
++ },
++ (false, true) => {
++ span_lint(
++ cx,
++ CAST_POSSIBLE_TRUNCATION,
++ expr.span,
++ &format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to),
++ );
++ if !cast_to.is_signed() {
++ span_lint(
++ cx,
++ CAST_SIGN_LOSS,
++ expr.span,
++ &format!(
++ "casting `{}` to `{}` may lose the sign of the value",
++ cast_from, cast_to
++ ),
++ );
++ }
++ },
++ (true, true) => {
++ check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to);
++ check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
++ check_lossless(cx, expr, cast_expr, cast_from, cast_to);
++ },
++ (false, false) => {
++ if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) {
++ span_lint(
++ cx,
++ CAST_POSSIBLE_TRUNCATION,
++ expr.span,
++ "casting `f64` to `f32` may truncate the value",
++ );
++ }
++ if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) {
++ span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
++ }
++ },
++ }
++}
++
++fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
++ if_chain! {
++ if let ty::RawPtr(from_ptr_ty) = &cast_from.kind;
++ if let ty::RawPtr(to_ptr_ty) = &cast_to.kind;
++ if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
++ if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
++ if from_layout.align.abi < to_layout.align.abi;
++ // with c_void, we inherently need to trust the user
++ if !is_c_void(cx, from_ptr_ty.ty);
++ // when casting from a ZST, we don't know enough to properly lint
++ if !from_layout.is_zst();
++ then {
++ span_lint(
++ cx,
++ CAST_PTR_ALIGNMENT,
++ expr.span,
++ &format!(
++ "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
++ cast_from,
++ cast_to,
++ from_layout.align.abi.bytes(),
++ to_layout.align.abi.bytes(),
++ ),
++ );
++ }
++ }
++}
++
++fn lint_fn_to_numeric_cast(
++ cx: &LateContext<'_, '_>,
++ expr: &Expr<'_>,
++ cast_expr: &Expr<'_>,
++ cast_from: Ty<'_>,
++ cast_to: Ty<'_>,
++) {
++ // We only want to check casts to `ty::Uint` or `ty::Int`
++ match cast_to.kind {
++ ty::Uint(_) | ty::Int(..) => { /* continue on */ },
++ _ => return,
++ }
++ match cast_from.kind {
++ ty::FnDef(..) | ty::FnPtr(_) => {
++ let mut applicability = Applicability::MaybeIncorrect;
++ let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
++
++ let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++ if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
++ span_lint_and_sugg(
++ cx,
++ FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++ expr.span,
++ &format!(
++ "casting function pointer `{}` to `{}`, which truncates the value",
++ from_snippet, cast_to
++ ),
++ "try",
++ format!("{} as usize", from_snippet),
++ applicability,
++ );
++ } else if cast_to.kind != ty::Uint(UintTy::Usize) {
++ span_lint_and_sugg(
++ cx,
++ FN_TO_NUMERIC_CAST,
++ expr.span,
++ &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to),
++ "try",
++ format!("{} as usize", from_snippet),
++ applicability,
++ );
++ }
++ },
++ _ => {},
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for types used in structs, parameters and `let`
++ /// declarations above a certain complexity threshold.
++ ///
++ /// **Why is this bad?** Too complex types make the code less readable. Consider
++ /// using a `type` definition to simplify them.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::rc::Rc;
++ /// struct Foo {
++ /// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
++ /// }
++ /// ```
++ pub TYPE_COMPLEXITY,
++ complexity,
++ "usage of very complex types that might be better factored into `type` definitions"
++}
++
++pub struct TypeComplexity {
++ threshold: u64,
++}
++
++impl TypeComplexity {
++ #[must_use]
++ pub fn new(threshold: u64) -> Self {
++ Self { threshold }
++ }
++}
++
++impl_lint_pass!(TypeComplexity => [TYPE_COMPLEXITY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexity {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ _: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ _: &'tcx Body<'_>,
++ _: Span,
++ _: HirId,
++ ) {
++ self.check_fndecl(cx, decl);
++ }
++
++ fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) {
++ // enum variants are also struct fields now
++ self.check_type(cx, &field.ty);
++ }
++
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ match item.kind {
++ ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_type(cx, ty),
++ // functions, enums, structs, impls and traits are covered
++ _ => (),
++ }
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++ match item.kind {
++ TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
++ TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl),
++ // methods with default impl are covered by check_fn
++ _ => (),
++ }
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++ match item.kind {
++ ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty),
++ // methods are covered by check_fn
++ _ => (),
++ }
++ }
++
++ fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) {
++ if let Some(ref ty) = local.ty {
++ self.check_type(cx, ty);
++ }
++ }
++}
++
++impl<'a, 'tcx> TypeComplexity {
++ fn check_fndecl(&self, cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>) {
++ for arg in decl.inputs {
++ self.check_type(cx, arg);
++ }
++ if let FnRetTy::Return(ref ty) = decl.output {
++ self.check_type(cx, ty);
++ }
++ }
++
++ fn check_type(&self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
++ if ty.span.from_expansion() {
++ return;
++ }
++ let score = {
++ let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
++ visitor.visit_ty(ty);
++ visitor.score
++ };
++
++ if score > self.threshold {
++ span_lint(
++ cx,
++ TYPE_COMPLEXITY,
++ ty.span,
++ "very complex type used. Consider factoring parts into `type` definitions",
++ );
++ }
++ }
++}
++
++/// Walks a type and assigns a complexity score to it.
++struct TypeComplexityVisitor {
++ /// total complexity score of the type
++ score: u64,
++ /// current nesting level
++ nest: u64,
++}
++
++impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
++ type Map = Map<'tcx>;
++
++ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
++ let (add_score, sub_nest) = match ty.kind {
++ // _, &x and *x have only small overhead; don't mess with nesting level
++ TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0),
++
++ // the "normal" components of a type: named types, arrays/tuples
++ TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
++
++ // function types bring a lot of overhead
++ TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
++
++ TyKind::TraitObject(ref param_bounds, _) => {
++ let has_lifetime_parameters = param_bounds.iter().any(|bound| {
++ bound.bound_generic_params.iter().any(|gen| match gen.kind {
++ GenericParamKind::Lifetime { .. } => true,
++ _ => false,
++ })
++ });
++ if has_lifetime_parameters {
++ // complex trait bounds like A<'a, 'b>
++ (50 * self.nest, 1)
++ } else {
++ // simple trait bounds like A + B
++ (20 * self.nest, 0)
++ }
++ },
++
++ _ => (0, 0),
++ };
++ self.score += add_score;
++ self.nest += sub_nest;
++ walk_ty(self, ty);
++ self.nest -= sub_nest;
++ }
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for expressions where a character literal is cast
++ /// to `u8` and suggests using a byte literal instead.
++ ///
++ /// **Why is this bad?** In general, casting values to smaller types is
++ /// error-prone and should be avoided where possible. In the particular case of
++ /// converting a character literal to u8, it is easy to avoid by just using a
++ /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
++ /// than `'a' as u8`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// 'x' as u8
++ /// ```
++ ///
++ /// A better version, using the byte literal:
++ ///
++ /// ```rust,ignore
++ /// b'x'
++ /// ```
++ pub CHAR_LIT_AS_U8,
++ complexity,
++ "casting a character literal to `u8` truncates"
++}
++
++declare_lint_pass!(CharLitAsU8 => [CHAR_LIT_AS_U8]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CharLitAsU8 {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if !expr.span.from_expansion();
++ if let ExprKind::Cast(e, _) = &expr.kind;
++ if let ExprKind::Lit(l) = &e.kind;
++ if let LitKind::Char(c) = l.node;
++ if ty::Uint(UintTy::U8) == cx.tables.expr_ty(expr).kind;
++ then {
++ let mut applicability = Applicability::MachineApplicable;
++ let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
++
++ span_lint_and_then(
++ cx,
++ CHAR_LIT_AS_U8,
++ expr.span,
++ "casting a character literal to `u8` truncates",
++ |diag| {
++ diag.note("`char` is four bytes wide, but `u8` is a single byte");
++
++ if c.is_ascii() {
++ diag.span_suggestion(
++ expr.span,
++ "use a byte literal instead",
++ format!("b{}", snippet),
++ applicability,
++ );
++ }
++ });
++ }
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons where one side of the relation is
++ /// either the minimum or maximum value for its type and warns if it involves a
++ /// case that is always true or always false. Only integer and boolean types are
++ /// checked.
++ ///
++ /// **Why is this bad?** An expression like `min <= x` may misleadingly imply
++ /// that it is possible for `x` to be less than the minimum. Expressions like
++ /// `max < x` are probably mistakes.
++ ///
++ /// **Known problems:** For `usize` the size of the current compile target will
++ /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
++ /// a comparison to detect target pointer width will trigger this lint. One can
++ /// use `mem::sizeof` and compare its value or conditional compilation
++ /// attributes
++ /// like `#[cfg(target_pointer_width = "64")] ..` instead.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// let vec: Vec<isize> = Vec::new();
++ /// if vec.len() <= 0 {}
++ /// if 100 > i32::MAX {}
++ /// ```
++ pub ABSURD_EXTREME_COMPARISONS,
++ correctness,
++ "a comparison with a maximum or minimum value that is always true or false"
++}
++
++declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
++
++enum ExtremeType {
++ Minimum,
++ Maximum,
++}
++
++struct ExtremeExpr<'a> {
++ which: ExtremeType,
++ expr: &'a Expr<'a>,
++}
++
++enum AbsurdComparisonResult {
++ AlwaysFalse,
++ AlwaysTrue,
++ InequalityImpossible,
++}
++
++fn is_cast_between_fixed_and_target<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++ if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
++ let precast_ty = cx.tables.expr_ty(cast_exp);
++ let cast_ty = cx.tables.expr_ty(expr);
++
++ return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
++ }
++
++ false
++}
++
++fn detect_absurd_comparison<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ op: BinOpKind,
++ lhs: &'tcx Expr<'_>,
++ rhs: &'tcx Expr<'_>,
++) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
++ use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
++ use crate::types::ExtremeType::{Maximum, Minimum};
++ use crate::utils::comparisons::{normalize_comparison, Rel};
++
++ // absurd comparison only makes sense on primitive types
++ // primitive types don't implement comparison operators with each other
++ if cx.tables.expr_ty(lhs) != cx.tables.expr_ty(rhs) {
++ return None;
++ }
++
++ // comparisons between fix sized types and target sized types are considered unanalyzable
++ if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
++ return None;
++ }
++
++ let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
++
++ let lx = detect_extreme_expr(cx, normalized_lhs);
++ let rx = detect_extreme_expr(cx, normalized_rhs);
++
++ Some(match rel {
++ Rel::Lt => {
++ match (lx, rx) {
++ (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
++ (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
++ _ => return None,
++ }
++ },
++ Rel::Le => {
++ match (lx, rx) {
++ (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
++ (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
++ (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
++ (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
++ _ => return None,
++ }
++ },
++ Rel::Ne | Rel::Eq => return None,
++ })
++}
++
++fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
++ use crate::types::ExtremeType::{Maximum, Minimum};
++
++ let ty = cx.tables.expr_ty(expr);
++
++ let cv = constant(cx, cx.tables, expr)?.0;
++
++ let which = match (&ty.kind, cv) {
++ (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum,
++ (&ty::Int(ity), Constant::Int(i))
++ if i == unsext(cx.tcx, i128::min_value() >> (128 - int_bits(cx.tcx, ity)), ity) =>
++ {
++ Minimum
++ },
++
++ (&ty::Bool, Constant::Bool(true)) => Maximum,
++ (&ty::Int(ity), Constant::Int(i))
++ if i == unsext(cx.tcx, i128::max_value() >> (128 - int_bits(cx.tcx, ity)), ity) =>
++ {
++ Maximum
++ },
++ (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::max_value(), uty) == i => Maximum,
++
++ _ => return None,
++ };
++ Some(ExtremeExpr { which, expr })
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
++ use crate::types::ExtremeType::{Maximum, Minimum};
++
++ if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
++ if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
++ if !expr.span.from_expansion() {
++ let msg = "this comparison involving the minimum or maximum element for this \
++ type contains a case that is always true or always false";
++
++ let conclusion = match result {
++ AlwaysFalse => "this comparison is always false".to_owned(),
++ AlwaysTrue => "this comparison is always true".to_owned(),
++ InequalityImpossible => format!(
++ "the case where the two sides are not equal never occurs, consider using `{} == {}` \
++ instead",
++ snippet(cx, lhs.span, "lhs"),
++ snippet(cx, rhs.span, "rhs")
++ ),
++ };
++
++ let help = format!(
++ "because `{}` is the {} value for this type, {}",
++ snippet(cx, culprit.expr.span, "x"),
++ match culprit.which {
++ Minimum => "minimum",
++ Maximum => "maximum",
++ },
++ conclusion
++ );
++
++ span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
++ }
++ }
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons where the relation is always either
++ /// true or false, but where one side has been upcast so that the comparison is
++ /// necessary. Only integer types are checked.
++ ///
++ /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`
++ /// will mistakenly imply that it is possible for `x` to be outside the range of
++ /// `u8`.
++ ///
++ /// **Known problems:**
++ /// https://github.com/rust-lang/rust-clippy/issues/886
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x: u8 = 1;
++ /// (x as u32) > 300;
++ /// ```
++ pub INVALID_UPCAST_COMPARISONS,
++ pedantic,
++ "a comparison involving an upcast which is always true or false"
++}
++
++declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
++
++#[derive(Copy, Clone, Debug, Eq)]
++enum FullInt {
++ S(i128),
++ U(u128),
++}
++
++impl FullInt {
++ #[allow(clippy::cast_sign_loss)]
++ #[must_use]
++ fn cmp_s_u(s: i128, u: u128) -> Ordering {
++ if s < 0 {
++ Ordering::Less
++ } else if u > (i128::max_value() as u128) {
++ Ordering::Greater
++ } else {
++ (s as u128).cmp(&u)
++ }
++ }
++}
++
++impl PartialEq for FullInt {
++ #[must_use]
++ fn eq(&self, other: &Self) -> bool {
++ self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal
++ }
++}
++
++impl PartialOrd for FullInt {
++ #[must_use]
++ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++ Some(match (self, other) {
++ (&Self::S(s), &Self::S(o)) => s.cmp(&o),
++ (&Self::U(s), &Self::U(o)) => s.cmp(&o),
++ (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
++ (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
++ })
++ }
++}
++impl Ord for FullInt {
++ #[must_use]
++ fn cmp(&self, other: &Self) -> Ordering {
++ self.partial_cmp(other)
++ .expect("`partial_cmp` for FullInt can never return `None`")
++ }
++}
++
++fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
++ if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
++ let pre_cast_ty = cx.tables.expr_ty(cast_exp);
++ let cast_ty = cx.tables.expr_ty(expr);
++ // if it's a cast from i32 to u32 wrapping will invalidate all these checks
++ if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
++ return None;
++ }
++ match pre_cast_ty.kind {
++ ty::Int(int_ty) => Some(match int_ty {
++ IntTy::I8 => (
++ FullInt::S(i128::from(i8::min_value())),
++ FullInt::S(i128::from(i8::max_value())),
++ ),
++ IntTy::I16 => (
++ FullInt::S(i128::from(i16::min_value())),
++ FullInt::S(i128::from(i16::max_value())),
++ ),
++ IntTy::I32 => (
++ FullInt::S(i128::from(i32::min_value())),
++ FullInt::S(i128::from(i32::max_value())),
++ ),
++ IntTy::I64 => (
++ FullInt::S(i128::from(i64::min_value())),
++ FullInt::S(i128::from(i64::max_value())),
++ ),
++ IntTy::I128 => (FullInt::S(i128::min_value()), FullInt::S(i128::max_value())),
++ IntTy::Isize => (
++ FullInt::S(isize::min_value() as i128),
++ FullInt::S(isize::max_value() as i128),
++ ),
++ }),
++ ty::Uint(uint_ty) => Some(match uint_ty {
++ UintTy::U8 => (
++ FullInt::U(u128::from(u8::min_value())),
++ FullInt::U(u128::from(u8::max_value())),
++ ),
++ UintTy::U16 => (
++ FullInt::U(u128::from(u16::min_value())),
++ FullInt::U(u128::from(u16::max_value())),
++ ),
++ UintTy::U32 => (
++ FullInt::U(u128::from(u32::min_value())),
++ FullInt::U(u128::from(u32::max_value())),
++ ),
++ UintTy::U64 => (
++ FullInt::U(u128::from(u64::min_value())),
++ FullInt::U(u128::from(u64::max_value())),
++ ),
++ UintTy::U128 => (FullInt::U(u128::min_value()), FullInt::U(u128::max_value())),
++ UintTy::Usize => (
++ FullInt::U(usize::min_value() as u128),
++ FullInt::U(usize::max_value() as u128),
++ ),
++ }),
++ _ => None,
++ }
++ } else {
++ None
++ }
++}
++
++fn node_as_const_fullint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> {
++ let val = constant(cx, cx.tables, expr)?.0;
++ if let Constant::Int(const_int) = val {
++ match cx.tables.expr_ty(expr).kind {
++ ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
++ ty::Uint(_) => Some(FullInt::U(const_int)),
++ _ => None,
++ }
++ } else {
++ None
++ }
++}
++
++fn err_upcast_comparison(cx: &LateContext<'_, '_>, span: Span, expr: &Expr<'_>, always: bool) {
++ if let ExprKind::Cast(ref cast_val, _) = expr.kind {
++ span_lint(
++ cx,
++ INVALID_UPCAST_COMPARISONS,
++ span,
++ &format!(
++ "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
++ snippet(cx, cast_val.span, "the expression"),
++ if always { "true" } else { "false" },
++ ),
++ );
++ }
++}
++
++fn upcast_comparison_bounds_err<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ span: Span,
++ rel: comparisons::Rel,
++ lhs_bounds: Option<(FullInt, FullInt)>,
++ lhs: &'tcx Expr<'_>,
++ rhs: &'tcx Expr<'_>,
++ invert: bool,
++) {
++ use crate::utils::comparisons::Rel;
++
++ if let Some((lb, ub)) = lhs_bounds {
++ if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
++ if rel == Rel::Eq || rel == Rel::Ne {
++ if norm_rhs_val < lb || norm_rhs_val > ub {
++ err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
++ }
++ } else if match rel {
++ Rel::Lt => {
++ if invert {
++ norm_rhs_val < lb
++ } else {
++ ub < norm_rhs_val
++ }
++ },
++ Rel::Le => {
++ if invert {
++ norm_rhs_val <= lb
++ } else {
++ ub <= norm_rhs_val
++ }
++ },
++ Rel::Eq | Rel::Ne => unreachable!(),
++ } {
++ err_upcast_comparison(cx, span, lhs, true)
++ } else if match rel {
++ Rel::Lt => {
++ if invert {
++ norm_rhs_val >= ub
++ } else {
++ lb >= norm_rhs_val
++ }
++ },
++ Rel::Le => {
++ if invert {
++ norm_rhs_val > ub
++ } else {
++ lb > norm_rhs_val
++ }
++ },
++ Rel::Eq | Rel::Ne => unreachable!(),
++ } {
++ err_upcast_comparison(cx, span, lhs, false)
++ }
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
++ let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
++ let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
++ val
++ } else {
++ return;
++ };
++
++ let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
++ let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
++
++ upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
++ upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
++ }
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for public `impl` or `fn` missing generalization
++ /// over different hashers and implicitly defaulting to the default hashing
++ /// algorithm (`SipHash`).
++ ///
++ /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be
++ /// used with them.
++ ///
++ /// **Known problems:** Suggestions for replacing constructors can contain
++ /// false-positives. Also applying suggestions can require modification of other
++ /// pieces of code, possibly including external crates.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::collections::HashMap;
++ /// # use std::hash::{Hash, BuildHasher};
++ /// # trait Serialize {};
++ /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
++ ///
++ /// pub fn foo(map: &mut HashMap<i32, i32>) { }
++ /// ```
++ /// could be rewritten as
++ /// ```rust
++ /// # use std::collections::HashMap;
++ /// # use std::hash::{Hash, BuildHasher};
++ /// # trait Serialize {};
++ /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
++ ///
++ /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
++ /// ```
++ pub IMPLICIT_HASHER,
++ pedantic,
++ "missing generalization over different hashers"
++}
++
++declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher {
++ #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ use rustc_span::BytePos;
++
++ fn suggestion<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ diag: &mut DiagnosticBuilder<'_>,
++ generics_span: Span,
++ generics_suggestion_span: Span,
++ target: &ImplicitHasherType<'_>,
++ vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
++ ) {
++ let generics_snip = snippet(cx, generics_span, "");
++ // trim `<` `>`
++ let generics_snip = if generics_snip.is_empty() {
++ ""
++ } else {
++ &generics_snip[1..generics_snip.len() - 1]
++ };
++
++ multispan_sugg(
++ diag,
++ "consider adding a type parameter".to_string(),
++ vec![
++ (
++ generics_suggestion_span,
++ format!(
++ "<{}{}S: ::std::hash::BuildHasher{}>",
++ generics_snip,
++ if generics_snip.is_empty() { "" } else { ", " },
++ if vis.suggestions.is_empty() {
++ ""
++ } else {
++ // request users to add `Default` bound so that generic constructors can be used
++ " + Default"
++ },
++ ),
++ ),
++ (
++ target.span(),
++ format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
++ ),
++ ],
++ );
++
++ if !vis.suggestions.is_empty() {
++ multispan_sugg(diag, "...and use generic constructor".into(), vis.suggestions);
++ }
++ }
++
++ if !cx.access_levels.is_exported(item.hir_id) {
++ return;
++ }
++
++ match item.kind {
++ ItemKind::Impl {
++ ref generics,
++ self_ty: ref ty,
++ ref items,
++ ..
++ } => {
++ let mut vis = ImplicitHasherTypeVisitor::new(cx);
++ vis.visit_ty(ty);
++
++ for target in &vis.found {
++ if differing_macro_contexts(item.span, target.span()) {
++ return;
++ }
++
++ let generics_suggestion_span = generics.span.substitute_dummy({
++ let pos = snippet_opt(cx, item.span.until(target.span()))
++ .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
++ if let Some(pos) = pos {
++ Span::new(pos, pos, item.span.data().ctxt)
++ } else {
++ return;
++ }
++ });
++
++ let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
++ for item in items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
++ ctr_vis.visit_impl_item(item);
++ }
++
++ span_lint_and_then(
++ cx,
++ IMPLICIT_HASHER,
++ target.span(),
++ &format!(
++ "impl for `{}` should be generalized over different hashers",
++ target.type_name()
++ ),
++ move |diag| {
++ suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
++ },
++ );
++ }
++ },
++ ItemKind::Fn(ref sig, ref generics, body_id) => {
++ let body = cx.tcx.hir().body(body_id);
++
++ for ty in sig.decl.inputs {
++ let mut vis = ImplicitHasherTypeVisitor::new(cx);
++ vis.visit_ty(ty);
++
++ for target in &vis.found {
++ if in_external_macro(cx.sess(), generics.span) {
++ continue;
++ }
++ let generics_suggestion_span = generics.span.substitute_dummy({
++ let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span))
++ .and_then(|snip| {
++ let i = snip.find("fn")?;
++ Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32))
++ })
++ .expect("failed to create span for type parameters");
++ Span::new(pos, pos, item.span.data().ctxt)
++ });
++
++ let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
++ ctr_vis.visit_body(body);
++
++ span_lint_and_then(
++ cx,
++ IMPLICIT_HASHER,
++ target.span(),
++ &format!(
++ "parameter of type `{}` should be generalized over different hashers",
++ target.type_name()
++ ),
++ move |diag| {
++ suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
++ },
++ );
++ }
++ }
++ },
++ _ => {},
++ }
++ }
++}
++
++enum ImplicitHasherType<'tcx> {
++ HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
++ HashSet(Span, Ty<'tcx>, Cow<'static, str>),
++}
++
++impl<'tcx> ImplicitHasherType<'tcx> {
++ /// Checks that `ty` is a target type without a `BuildHasher`.
++ fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
++ if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind {
++ let params: Vec<_> = path
++ .segments
++ .last()
++ .as_ref()?
++ .args
++ .as_ref()?
++ .args
++ .iter()
++ .filter_map(|arg| match arg {
++ GenericArg::Type(ty) => Some(ty),
++ _ => None,
++ })
++ .collect();
++ let params_len = params.len();
++
++ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++
++ if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) && params_len == 2 {
++ Some(ImplicitHasherType::HashMap(
++ hir_ty.span,
++ ty,
++ snippet(cx, params[0].span, "K"),
++ snippet(cx, params[1].span, "V"),
++ ))
++ } else if is_type_diagnostic_item(cx, ty, sym!(hashset_type)) && params_len == 1 {
++ Some(ImplicitHasherType::HashSet(
++ hir_ty.span,
++ ty,
++ snippet(cx, params[0].span, "T"),
++ ))
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++ }
++
++ fn type_name(&self) -> &'static str {
++ match *self {
++ ImplicitHasherType::HashMap(..) => "HashMap",
++ ImplicitHasherType::HashSet(..) => "HashSet",
++ }
++ }
++
++ fn type_arguments(&self) -> String {
++ match *self {
++ ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
++ ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
++ }
++ }
++
++ fn ty(&self) -> Ty<'tcx> {
++ match *self {
++ ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
++ }
++ }
++
++ fn span(&self) -> Span {
++ match *self {
++ ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
++ }
++ }
++}
++
++struct ImplicitHasherTypeVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ found: Vec<ImplicitHasherType<'tcx>>,
++}
++
++impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
++ fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++ Self { cx, found: vec![] }
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
++ if let Some(target) = ImplicitHasherType::new(self.cx, t) {
++ self.found.push(target);
++ }
++
++ walk_ty(self, t);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++/// Looks for default-hasher-dependent constructors like `HashMap::new`.
++struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ body: &'a TypeckTables<'tcx>,
++ target: &'b ImplicitHasherType<'tcx>,
++ suggestions: BTreeMap<Span, String>,
++}
++
++impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++ fn new(cx: &'a LateContext<'a, 'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
++ Self {
++ cx,
++ body: cx.tables,
++ target,
++ suggestions: BTreeMap::new(),
++ }
++ }
++}
++
++impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_body(&mut self, body: &'tcx Body<'_>) {
++ let prev_body = self.body;
++ self.body = self.cx.tcx.body_tables(body.id());
++ walk_body(self, body);
++ self.body = prev_body;
++ }
++
++ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Call(ref fun, ref args) = e.kind;
++ if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind;
++ if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
++ then {
++ if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) {
++ return;
++ }
++
++ if match_path(ty_path, &paths::HASHMAP) {
++ if method.ident.name == sym!(new) {
++ self.suggestions
++ .insert(e.span, "HashMap::default()".to_string());
++ } else if method.ident.name == sym!(with_capacity) {
++ self.suggestions.insert(
++ e.span,
++ format!(
++ "HashMap::with_capacity_and_hasher({}, Default::default())",
++ snippet(self.cx, args[0].span, "capacity"),
++ ),
++ );
++ }
++ } else if match_path(ty_path, &paths::HASHSET) {
++ if method.ident.name == sym!(new) {
++ self.suggestions
++ .insert(e.span, "HashSet::default()".to_string());
++ } else if method.ident.name == sym!(with_capacity) {
++ self.suggestions.insert(
++ e.span,
++ format!(
++ "HashSet::with_capacity_and_hasher({}, Default::default())",
++ snippet(self.cx, args[0].span, "capacity"),
++ ),
++ );
++ }
++ }
++ }
++ }
++
++ walk_expr(self, e);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++ }
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.
++ ///
++ /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour.
++ /// `UnsafeCell` is the only way to obtain aliasable data that is considered
++ /// mutable.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// fn x(r: &i32) {
++ /// unsafe {
++ /// *(r as *const _ as *mut _) += 1;
++ /// }
++ /// }
++ /// ```
++ ///
++ /// Instead consider using interior mutability types.
++ ///
++ /// ```rust
++ /// use std::cell::UnsafeCell;
++ ///
++ /// fn x(r: &UnsafeCell<i32>) {
++ /// unsafe {
++ /// *r.get() += 1;
++ /// }
++ /// }
++ /// ```
++ pub CAST_REF_TO_MUT,
++ correctness,
++ "a cast of reference to a mutable pointer"
++}
++
++declare_lint_pass!(RefToMut => [CAST_REF_TO_MUT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RefToMut {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if_chain! {
++ if let ExprKind::Unary(UnOp::UnDeref, e) = &expr.kind;
++ if let ExprKind::Cast(e, t) = &e.kind;
++ if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind;
++ if let ExprKind::Cast(e, t) = &e.kind;
++ if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind;
++ if let ty::Ref(..) = cx.tables.node_type(e.hir_id).kind;
++ then {
++ span_lint(
++ cx,
++ CAST_REF_TO_MUT,
++ expr.span,
++ "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`",
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_allowed, snippet, span_lint_and_sugg};
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use unicode_normalization::UnicodeNormalization;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for the Unicode zero-width space in the code.
++ ///
++ /// **Why is this bad?** Having an invisible character in the code makes for all
++ /// sorts of April fools, but otherwise is very much frowned upon.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:** You don't see it, but there may be a zero-width space
++ /// somewhere in this text.
++ pub ZERO_WIDTH_SPACE,
++ correctness,
++ "using a zero-width space in a string literal, which is confusing"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for non-ASCII characters in string literals.
++ ///
++ /// **Why is this bad?** Yeah, we know, the 90's called and wanted their charset
++ /// back. Even so, there still are editors and other programs out there that
++ /// don't work well with Unicode. So if the code is meant to be used
++ /// internationally, on multiple operating systems, or has other portability
++ /// requirements, activating this lint could be useful.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// let x = String::from("€");
++ /// ```
++ /// Could be written as:
++ /// ```rust
++ /// let x = String::from("\u{20ac}");
++ /// ```
++ pub NON_ASCII_LITERAL,
++ pedantic,
++ "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for string literals that contain Unicode in a form
++ /// that is not equal to its
++ /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
++ ///
++ /// **Why is this bad?** If such a string is compared to another, the results
++ /// may be surprising.
++ ///
++ /// **Known problems** None.
++ ///
++ /// **Example:** You may not see it, but "à"" and "à"" aren't the same string. The
++ /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
++ pub UNICODE_NOT_NFC,
++ pedantic,
++ "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
++}
++
++declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
++
++impl LateLintPass<'_, '_> for Unicode {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) {
++ if let ExprKind::Lit(ref lit) = expr.kind {
++ if let LitKind::Str(_, _) = lit.node {
++ check_str(cx, lit.span, expr.hir_id)
++ }
++ }
++ }
++}
++
++fn escape<T: Iterator<Item = char>>(s: T) -> String {
++ let mut result = String::new();
++ for c in s {
++ if c as u32 > 0x7F {
++ for d in c.escape_unicode() {
++ result.push(d)
++ }
++ } else {
++ result.push(c);
++ }
++ }
++ result
++}
++
++fn check_str(cx: &LateContext<'_, '_>, span: Span, id: HirId) {
++ let string = snippet(cx, span, "");
++ if string.contains('\u{200B}') {
++ span_lint_and_sugg(
++ cx,
++ ZERO_WIDTH_SPACE,
++ span,
++ "zero-width space detected",
++ "consider replacing the string with",
++ string.replace("\u{200B}", "\\u{200B}"),
++ Applicability::MachineApplicable,
++ );
++ }
++ if string.chars().any(|c| c as u32 > 0x7F) {
++ span_lint_and_sugg(
++ cx,
++ NON_ASCII_LITERAL,
++ span,
++ "literal non-ASCII character detected",
++ "consider replacing the string with",
++ if is_allowed(cx, UNICODE_NOT_NFC, id) {
++ escape(string.chars())
++ } else {
++ escape(string.nfc())
++ },
++ Applicability::MachineApplicable,
++ );
++ }
++ if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
++ span_lint_and_sugg(
++ cx,
++ UNICODE_NOT_NFC,
++ span,
++ "non-NFC Unicode sequence detected",
++ "consider replacing the string with",
++ string.nfc().collect::<String>(),
++ Applicability::MachineApplicable,
++ );
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons with an address of a function item.
++ ///
++ /// **Why is this bad?** Function item address is not guaranteed to be unique and could vary
++ /// between different code generation units. Furthermore different function items could have
++ /// the same address after being merged together.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// type F = fn();
++ /// fn a() {}
++ /// let f: F = a;
++ /// if f == a {
++ /// // ...
++ /// }
++ /// ```
++ pub FN_ADDRESS_COMPARISONS,
++ correctness,
++ "comparison with an address of a function item"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for comparisons with an address of a trait vtable.
++ ///
++ /// **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which
++ /// are not guaranteed to be unique and could vary between different code generation units.
++ /// Furthermore vtables for different types could have the same address after being merged
++ /// together.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust,ignore
++ /// let a: Rc<dyn Trait> = ...
++ /// let b: Rc<dyn Trait> = ...
++ /// if Rc::ptr_eq(&a, &b) {
++ /// ...
++ /// }
++ /// ```
++ pub VTABLE_ADDRESS_COMPARISONS,
++ correctness,
++ "comparison with an address of a trait vtable"
++}
++
++declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COMPARISONS]);
++
++impl LateLintPass<'_, '_> for UnnamedAddress {
++ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++ fn is_comparison(binop: BinOpKind) -> bool {
++ match binop {
++ BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true,
++ _ => false,
++ }
++ }
++
++ fn is_trait_ptr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ match cx.tables.expr_ty_adjusted(expr).kind {
++ ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(),
++ _ => false,
++ }
++ }
++
++ fn is_fn_def(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ if let ty::FnDef(..) = cx.tables.expr_ty(expr).kind {
++ true
++ } else {
++ false
++ }
++ }
++
++ if_chain! {
++ if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
++ if is_comparison(binop.node);
++ if is_trait_ptr(cx, left) && is_trait_ptr(cx, right);
++ then {
++ span_lint_and_help(
++ cx,
++ VTABLE_ADDRESS_COMPARISONS,
++ expr.span,
++ "comparing trait object pointers compares a non-unique vtable address",
++ None,
++ "consider extracting and comparing data pointers only",
++ );
++ }
++ }
++
++ if_chain! {
++ if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind;
++ if let ExprKind::Path(ref func_qpath) = func.kind;
++ if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++ if match_def_path(cx, def_id, &paths::PTR_EQ) ||
++ match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
++ match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
++ let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
++ if ty_param.is_trait();
++ then {
++ span_lint_and_help(
++ cx,
++ VTABLE_ADDRESS_COMPARISONS,
++ expr.span,
++ "comparing trait object pointers compares a non-unique vtable address",
++ None,
++ "consider extracting and comparing data pointers only",
++ );
++ }
++ }
++
++ if_chain! {
++ if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
++ if is_comparison(binop.node);
++ if cx.tables.expr_ty_adjusted(left).is_fn_ptr() &&
++ cx.tables.expr_ty_adjusted(right).is_fn_ptr();
++ if is_fn_def(cx, left) || is_fn_def(cx, right);
++ then {
++ span_lint(
++ cx,
++ FN_ADDRESS_COMPARISONS,
++ expr.span,
++ "comparing with a non-unique address of a function item",
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::span_lint;
++use rustc_ast::ast::{Ident, Item, ItemKind, UseTree, UseTreeKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::SymbolStr;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for imports that remove "unsafe" from an item's
++ /// name.
++ ///
++ /// **Why is this bad?** Renaming makes it less clear which traits and
++ /// structures are unsafe.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// use std::cell::{UnsafeCell as TotallySafeCell};
++ ///
++ /// extern crate crossbeam;
++ /// use crossbeam::{spawn_unsafe as spawn};
++ /// ```
++ pub UNSAFE_REMOVED_FROM_NAME,
++ style,
++ "`unsafe` removed from API names on import"
++}
++
++declare_lint_pass!(UnsafeNameRemoval => [UNSAFE_REMOVED_FROM_NAME]);
++
++impl EarlyLintPass for UnsafeNameRemoval {
++ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++ if let ItemKind::Use(ref use_tree) = item.kind {
++ check_use_tree(use_tree, cx, item.span);
++ }
++ }
++}
++
++fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
++ match use_tree.kind {
++ UseTreeKind::Simple(Some(new_name), ..) => {
++ let old_name = use_tree
++ .prefix
++ .segments
++ .last()
++ .expect("use paths cannot be empty")
++ .ident;
++ unsafe_to_safe_check(old_name, new_name, cx, span);
++ },
++ UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {},
++ UseTreeKind::Nested(ref nested_use_tree) => {
++ for &(ref use_tree, _) in nested_use_tree {
++ check_use_tree(use_tree, cx, span);
++ }
++ },
++ }
++}
++
++fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) {
++ let old_str = old_name.name.as_str();
++ let new_str = new_name.name.as_str();
++ if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
++ span_lint(
++ cx,
++ UNSAFE_REMOVED_FROM_NAME,
++ span,
++ &format!(
++ "removed `unsafe` from the name of `{}` in use as `{}`",
++ old_str, new_str
++ ),
++ );
++ }
++}
++
++#[must_use]
++fn contains_unsafe(name: &SymbolStr) -> bool {
++ name.contains("Unsafe") || name.contains("unsafe")
++}
--- /dev/null
--- /dev/null
++use crate::utils::{is_try, match_qpath, match_trait_method, paths, span_lint};
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for unused written/read amount.
++ ///
++ /// **Why is this bad?** `io::Write::write(_vectored)` and
++ /// `io::Read::read(_vectored)` are not guaranteed to
++ /// process the entire buffer. They return how many bytes were processed, which
++ /// might be smaller
++ /// than a given buffer's length. If you don't need to deal with
++ /// partial-write/read, use
++ /// `write_all`/`read_exact` instead.
++ ///
++ /// **Known problems:** Detects only common patterns.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// use std::io;
++ /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
++ /// // must be `w.write_all(b"foo")?;`
++ /// w.write(b"foo")?;
++ /// Ok(())
++ /// }
++ /// ```
++ pub UNUSED_IO_AMOUNT,
++ correctness,
++ "unused written/read amount"
++}
++
++declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount {
++ fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
++ let expr = match s.kind {
++ hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr,
++ _ => return,
++ };
++
++ match expr.kind {
++ hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => {
++ if let hir::ExprKind::Call(ref func, ref args) = res.kind {
++ if let hir::ExprKind::Path(ref path) = func.kind {
++ if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 {
++ check_method_call(cx, &args[0], expr);
++ }
++ }
++ } else {
++ check_method_call(cx, res, expr);
++ }
++ },
++
++ hir::ExprKind::MethodCall(ref path, _, ref args) => match &*path.ident.as_str() {
++ "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
++ check_method_call(cx, &args[0], expr);
++ },
++ _ => (),
++ },
++
++ _ => (),
++ }
++ }
++}
++
++fn check_method_call(cx: &LateContext<'_, '_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
++ if let hir::ExprKind::MethodCall(ref path, _, _) = call.kind {
++ let symbol = &*path.ident.as_str();
++ let read_trait = match_trait_method(cx, call, &paths::IO_READ);
++ let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
++
++ match (read_trait, write_trait, symbol) {
++ (true, _, "read") => span_lint(
++ cx,
++ UNUSED_IO_AMOUNT,
++ expr.span,
++ "read amount is not handled. Use `Read::read_exact` instead",
++ ),
++ (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
++ (_, true, "write") => span_lint(
++ cx,
++ UNUSED_IO_AMOUNT,
++ expr.span,
++ "written amount is not handled. Use `Write::write_all` instead",
++ ),
++ (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
++ _ => (),
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
++use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Path};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::span_lint_and_help;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks methods that contain a `self` argument but don't use it
++ ///
++ /// **Why is this bad?** It may be clearer to define the method as an associated function instead
++ /// of an instance method if it doesn't require `self`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// struct A;
++ /// impl A {
++ /// fn method(&self) {}
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust,ignore
++ /// struct A;
++ /// impl A {
++ /// fn method() {}
++ /// }
++ /// ```
++ pub UNUSED_SELF,
++ pedantic,
++ "methods that contain a `self` argument but don't use it"
++}
++
++declare_lint_pass!(UnusedSelf => [UNUSED_SELF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedSelf {
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &ImplItem<'_>) {
++ if impl_item.span.from_expansion() {
++ return;
++ }
++ let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id);
++ let parent_item = cx.tcx.hir().expect_item(parent);
++ let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++ let assoc_item = cx.tcx.associated_item(def_id);
++ if_chain! {
++ if let ItemKind::Impl { of_trait: None, .. } = parent_item.kind;
++ if assoc_item.fn_has_self_parameter;
++ if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
++ let body = cx.tcx.hir().body(*body_id);
++ if !body.params.is_empty();
++ then {
++ let self_param = &body.params[0];
++ let self_hir_id = self_param.pat.hir_id;
++ let mut visitor = UnusedSelfVisitor {
++ cx,
++ uses_self: false,
++ self_hir_id: &self_hir_id,
++ };
++ visitor.visit_body(body);
++ if !visitor.uses_self {
++ span_lint_and_help(
++ cx,
++ UNUSED_SELF,
++ self_param.span,
++ "unused `self` argument",
++ None,
++ "consider refactoring to a associated function",
++ );
++ return;
++ }
++ }
++ }
++ }
++}
++
++struct UnusedSelfVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ uses_self: bool,
++ self_hir_id: &'a HirId,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++ if self.uses_self {
++ // This function already uses `self`
++ return;
++ }
++ if let Res::Local(hir_id) = &path.res {
++ self.uses_self = self.self_hir_id == hir_id
++ }
++ walk_path(self, path);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
++ ///
++ /// **Why is this bad?** Using `if let` or `match` is more idiomatic.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let option = Some(0);
++ /// # fn do_something_with(_x: usize) {}
++ /// if option.is_some() {
++ /// do_something_with(option.unwrap())
++ /// }
++ /// ```
++ ///
++ /// Could be written:
++ ///
++ /// ```rust
++ /// # let option = Some(0);
++ /// # fn do_something_with(_x: usize) {}
++ /// if let Some(value) = option {
++ /// do_something_with(value)
++ /// }
++ /// ```
++ pub UNNECESSARY_UNWRAP,
++ complexity,
++ "checks for calls of `unwrap[_err]()` that cannot fail"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
++ ///
++ /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
++ ///
++ /// **Known problems:** This lint only checks `if` conditions not assignments.
++ /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let option = Some(0);
++ /// # fn do_something_with(_x: usize) {}
++ /// if option.is_none() {
++ /// do_something_with(option.unwrap())
++ /// }
++ /// ```
++ ///
++ /// This code will always panic. The if condition should probably be inverted.
++ pub PANICKING_UNWRAP,
++ correctness,
++ "checks for calls of `unwrap[_err]()` that will always fail"
++}
++
++/// Visitor that keeps track of which variables are unwrappable.
++struct UnwrappableVariablesVisitor<'a, 'tcx> {
++ unwrappables: Vec<UnwrapInfo<'tcx>>,
++ cx: &'a LateContext<'a, 'tcx>,
++}
++/// Contains information about whether a variable can be unwrapped.
++#[derive(Copy, Clone, Debug)]
++struct UnwrapInfo<'tcx> {
++ /// The variable that is checked
++ ident: &'tcx Path<'tcx>,
++ /// The check, like `x.is_ok()`
++ check: &'tcx Expr<'tcx>,
++ /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
++ safe_to_unwrap: bool,
++}
++
++/// Collects the information about unwrappable variables from an if condition
++/// The `invert` argument tells us whether the condition is negated.
++fn collect_unwrap_info<'a, 'tcx>(
++ cx: &'a LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++ invert: bool,
++) -> Vec<UnwrapInfo<'tcx>> {
++ if let ExprKind::Binary(op, left, right) = &expr.kind {
++ match (invert, op.node) {
++ (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
++ let mut unwrap_info = collect_unwrap_info(cx, left, invert);
++ unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
++ return unwrap_info;
++ },
++ _ => (),
++ }
++ } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind {
++ return collect_unwrap_info(cx, expr, !invert);
++ } else {
++ if_chain! {
++ if let ExprKind::MethodCall(method_name, _, args) = &expr.kind;
++ if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind;
++ let ty = cx.tables.expr_ty(&args[0]);
++ if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type));
++ let name = method_name.ident.as_str();
++ if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
++ then {
++ assert!(args.len() == 1);
++ let unwrappable = match name.as_ref() {
++ "is_some" | "is_ok" => true,
++ "is_err" | "is_none" => false,
++ _ => unreachable!(),
++ };
++ let safe_to_unwrap = unwrappable != invert;
++ return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
++ }
++ }
++ }
++ Vec::new()
++}
++
++impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
++ fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
++ let prev_len = self.unwrappables.len();
++ for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
++ if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
++ || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
++ {
++ // if the variable is mutated, we don't know whether it can be unwrapped:
++ continue;
++ }
++ self.unwrappables.push(unwrap_info);
++ }
++ walk_expr(self, branch);
++ self.unwrappables.truncate(prev_len);
++ }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ // Shouldn't lint when `expr` is in macro.
++ if in_external_macro(self.cx.tcx.sess, expr.span) {
++ return;
++ }
++ if let Some((cond, then, els)) = if_block(&expr) {
++ walk_expr(self, cond);
++ self.visit_branch(cond, then, false);
++ if let Some(els) = els {
++ self.visit_branch(cond, els, true);
++ }
++ } else {
++ // find `unwrap[_err]()` calls:
++ if_chain! {
++ if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.kind;
++ if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
++ if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name);
++ let call_to_unwrap = method_name.ident.name == sym!(unwrap);
++ if let Some(unwrappable) = self.unwrappables.iter()
++ .find(|u| u.ident.res == path.res);
++ then {
++ if call_to_unwrap == unwrappable.safe_to_unwrap {
++ span_lint_and_then(
++ self.cx,
++ UNNECESSARY_UNWRAP,
++ expr.span,
++ &format!("You checked before that `{}()` cannot fail. \
++ Instead of checking and unwrapping, it's better to use `if let` or `match`.",
++ method_name.ident.name),
++ |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
++ );
++ } else {
++ span_lint_and_then(
++ self.cx,
++ PANICKING_UNWRAP,
++ expr.span,
++ &format!("This call to `{}()` will always panic.",
++ method_name.ident.name),
++ |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
++ );
++ }
++ }
++ }
++ walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++ }
++}
++
++declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap {
++ fn check_fn(
++ &mut self,
++ cx: &LateContext<'a, 'tcx>,
++ kind: FnKind<'tcx>,
++ decl: &'tcx FnDecl<'_>,
++ body: &'tcx Body<'_>,
++ span: Span,
++ fn_id: HirId,
++ ) {
++ if span.from_expansion() {
++ return;
++ }
++
++ let mut v = UnwrappableVariablesVisitor {
++ cx,
++ unwrappables: Vec::new(),
++ };
++
++ walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
++ }
++}
--- /dev/null
--- /dev/null
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor};
++use rustc_hir::{
++ def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath,
++ TyKind,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_middle::ty::{DefIdTree, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::kw;
++use rustc_typeck::hir_ty_to_ty;
++
++use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
++
++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:**
++ /// - False positive when using associated types (#2843)
++ /// - False positives in some situations when using generics (#3410)
++ ///
++ /// **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"
++}
++
++declare_lint_pass!(UseSelf => [USE_SELF]);
++
++const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
++
++fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) {
++ let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
++
++ // Path segments only include actual path, no methods or fields.
++ let last_path_span = last_segment.ident.span;
++
++ if differing_macro_contexts(path.span, last_path_span) {
++ return;
++ }
++
++ // Only take path up to the end of last_path_span.
++ let span = path.span.with_hi(last_path_span.hi());
++
++ span_lint_and_sugg(
++ cx,
++ USE_SELF,
++ span,
++ "unnecessary structure name repetition",
++ "use the applicable keyword",
++ "Self".to_owned(),
++ Applicability::MachineApplicable,
++ );
++}
++
++// FIXME: always use this (more correct) visitor, not just in method signatures.
++struct SemanticUseSelfVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ self_ty: Ty<'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
++ if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
++ match path.res {
++ def::Res::SelfTy(..) => {},
++ _ => {
++ if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
++ span_use_self_lint(self.cx, path, None);
++ }
++ },
++ }
++ }
++
++ walk_ty(self, hir_ty)
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn check_trait_method_impl_decl<'a, 'tcx>(
++ cx: &'a LateContext<'a, 'tcx>,
++ impl_item: &ImplItem<'_>,
++ impl_decl: &'tcx FnDecl<'_>,
++ impl_trait_ref: ty::TraitRef<'tcx>,
++) {
++ let trait_method = cx
++ .tcx
++ .associated_items(impl_trait_ref.def_id)
++ .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::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);
++
++ let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
++ Some(&**ty)
++ } else {
++ None
++ };
++
++ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
++ // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
++ // We use `impl_hir_ty` to see if the type was written as `Self`,
++ // `hir_ty_to_ty(...)` to check semantic types of paths, and
++ // `trait_ty` to determine which parts of the signature in the trait, mention
++ // the type being implemented verbatim (as opposed to `Self`).
++ for (impl_hir_ty, trait_ty) in impl_decl
++ .inputs
++ .iter()
++ .chain(output_hir_ty)
++ .zip(trait_method_sig.inputs_and_output)
++ {
++ // Check if the input/output type in the trait method specifies the implemented
++ // type verbatim, and only suggest `Self` if that isn't the case.
++ // 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.
++ let self_ty = impl_trait_ref.self_ty();
++ if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
++ let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
++
++ visitor.visit_ty(&impl_hir_ty);
++ }
++ }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if in_external_macro(cx.sess(), item.span) {
++ return;
++ }
++ if_chain! {
++ if let ItemKind::Impl{ self_ty: ref item_type, items: refs, .. } = item.kind;
++ if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind;
++ then {
++ let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
++ let should_check = if let Some(ref params) = *parameters {
++ !params.parenthesized && !params.args.iter().any(|arg| match arg {
++ GenericArg::Lifetime(_) => true,
++ _ => false,
++ })
++ } else {
++ true
++ };
++
++ if should_check {
++ let visitor = &mut UseSelfVisitor {
++ item_path,
++ cx,
++ };
++ let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ let impl_trait_ref = cx.tcx.impl_trait_ref(impl_def_id);
++
++ if let Some(impl_trait_ref) = impl_trait_ref {
++ for impl_item_ref in refs {
++ let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
++ if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id)
++ = &impl_item.kind {
++ check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref);
++
++ let body = cx.tcx.hir().body(*impl_body_id);
++ visitor.visit_body(body);
++ } else {
++ visitor.visit_impl_item(impl_item);
++ }
++ }
++ } else {
++ for impl_item_ref in refs {
++ let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
++ visitor.visit_impl_item(impl_item);
++ }
++ }
++ }
++ }
++ }
++ }
++}
++
++struct UseSelfVisitor<'a, 'tcx> {
++ item_path: &'a Path<'a>,
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++ if !path.segments.iter().any(|p| p.ident.span.is_dummy()) {
++ if path.segments.len() >= 2 {
++ let last_but_one = &path.segments[path.segments.len() - 2];
++ if last_but_one.ident.name != kw::SelfUpper {
++ let enum_def_id = match path.res {
++ Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
++ Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
++ let variant_def_id = self.cx.tcx.parent(ctor_def_id);
++ variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
++ },
++ _ => None,
++ };
++
++ if self.item_path.res.opt_def_id() == enum_def_id {
++ span_use_self_lint(self.cx, path, Some(last_but_one));
++ }
++ }
++ }
++
++ if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
++ if self.item_path.res == path.res {
++ span_use_self_lint(self.cx, path, None);
++ } else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res {
++ if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
++ span_use_self_lint(self.cx, path, None);
++ }
++ }
++ }
++ }
++
++ walk_path(self, path);
++ }
++
++ fn visit_item(&mut self, item: &'tcx Item<'_>) {
++ match item.kind {
++ ItemKind::Use(..)
++ | ItemKind::Static(..)
++ | ItemKind::Enum(..)
++ | ItemKind::Struct(..)
++ | ItemKind::Union(..)
++ | ItemKind::Impl { .. }
++ | ItemKind::Fn(..) => {
++ // Don't check statements that shadow `Self` or where `Self` can't be used
++ },
++ _ => walk_item(self, item),
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::All(self.cx.tcx.hir())
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_ast::ast;
++use rustc_ast::expand::is_proc_macro_attr;
++use rustc_errors::Applicability;
++use rustc_session::Session;
++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),
++];
++
++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.to_string() == "clippy" {
++ if let Some(deprecation_status) =
++ BUILTIN_ATTRIBUTES
++ .iter()
++ .find_map(|(builtin_name, deprecation_status)| {
++ if *builtin_name == attr_segments[1].ident.to_string() {
++ Some(deprecation_status)
++ } else {
++ None
++ }
++ })
++ {
++ 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.to_string() == name
++ },
++ }
++ } else {
++ sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
++ false
++ }
++ } 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");
++ }
++ }
++}
++
++/// Return true if the attributes contain any of `proc_macro`,
++/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
++pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
++ attrs.iter().any(is_proc_macro_attr)
++}
--- /dev/null
--- /dev/null
++//! A group of attributes that can be attached to Rust code in order
++//! to generate a clippy lint detecting said code automatically.
++
++use crate::utils::{get_attr, higher};
++use rustc_ast::ast::{Attribute, LitFloatType, LitKind};
++use rustc_ast::walk_list;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_session::Session;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Generates clippy code that detects the offending pattern
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// // ./tests/ui/my_lint.rs
++ /// fn foo() {
++ /// // detect the following pattern
++ /// #[clippy::author]
++ /// if x == 42 {
++ /// // but ignore everything from here on
++ /// #![clippy::author = "ignore"]
++ /// }
++ /// ()
++ /// }
++ /// ```
++ ///
++ /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
++ /// a `./tests/ui/new_lint.stdout` file with the generated code:
++ ///
++ /// ```rust,ignore
++ /// // ./tests/ui/new_lint.stdout
++ /// if_chain! {
++ /// if let ExprKind::If(ref cond, ref then, None) = item.kind,
++ /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind,
++ /// if let ExprKind::Path(ref path) = left.kind,
++ /// if let ExprKind::Lit(ref lit) = right.kind,
++ /// if let LitKind::Int(42, _) = lit.node,
++ /// then {
++ /// // report your lint here
++ /// }
++ /// }
++ /// ```
++ pub LINT_AUTHOR,
++ internal_warn,
++ "helper for writing lints"
++}
++
++declare_lint_pass!(Author => [LINT_AUTHOR]);
++
++fn prelude() {
++ println!("if_chain! {{");
++}
++
++fn done() {
++ println!(" then {{");
++ println!(" // report your lint here");
++ println!(" }}");
++ println!("}}");
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Author {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("item").visit_item(item);
++ done();
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("item").visit_impl_item(item);
++ done();
++ }
++
++ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("item").visit_trait_item(item);
++ done();
++ }
++
++ fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant<'_>) {
++ if !has_attr(cx.sess(), &var.attrs) {
++ return;
++ }
++ prelude();
++ let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
++ PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
++ done();
++ }
++
++ fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) {
++ if !has_attr(cx.sess(), &field.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("field").visit_struct_field(field);
++ done();
++ }
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if !has_attr(cx.sess(), &expr.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("expr").visit_expr(expr);
++ done();
++ }
++
++ fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) {
++ if !has_attr(cx.sess(), &arm.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("arm").visit_arm(arm);
++ done();
++ }
++
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
++ if !has_attr(cx.sess(), stmt.kind.attrs()) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("stmt").visit_stmt(stmt);
++ done();
++ }
++
++ fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ prelude();
++ PrintVisitor::new("item").visit_foreign_item(item);
++ done();
++ }
++}
++
++impl PrintVisitor {
++ #[must_use]
++ fn new(s: &'static str) -> Self {
++ Self {
++ ids: FxHashMap::default(),
++ current: s.to_owned(),
++ }
++ }
++
++ fn next(&mut self, s: &'static str) -> String {
++ use std::collections::hash_map::Entry::{Occupied, Vacant};
++ match self.ids.entry(s) {
++ // already there: start numbering from `1`
++ Occupied(mut occ) => {
++ let val = occ.get_mut();
++ *val += 1;
++ format!("{}{}", s, *val)
++ },
++ // not there: insert and return name as given
++ Vacant(vac) => {
++ vac.insert(0);
++ s.to_owned()
++ },
++ }
++ }
++
++ fn print_qpath(&mut self, path: &QPath<'_>) {
++ print!(" if match_qpath({}, &[", self.current);
++ print_path(path, &mut true);
++ println!("]);");
++ }
++}
++
++struct PrintVisitor {
++ /// Fields are the current index that needs to be appended to pattern
++ /// binding names
++ ids: FxHashMap<&'static str, usize>,
++ /// the name that needs to be destructured
++ current: String,
++}
++
++impl<'tcx> Visitor<'tcx> for PrintVisitor {
++ type Map = Map<'tcx>;
++
++ #[allow(clippy::too_many_lines)]
++ fn visit_expr(&mut self, expr: &Expr<'_>) {
++ // handle if desugarings
++ // TODO add more desugarings here
++ if let Some((cond, then, opt_else)) = higher::if_block(&expr) {
++ let cond_pat = self.next("cond");
++ let then_pat = self.next("then");
++ if let Some(else_) = opt_else {
++ let else_pat = self.next("else_");
++ println!(
++ " if let Some((ref {}, ref {}, Some({}))) = higher::if_block(&{});",
++ cond_pat, then_pat, else_pat, self.current
++ );
++ self.current = else_pat;
++ self.visit_expr(else_);
++ } else {
++ println!(
++ " if let Some((ref {}, ref {}, None)) = higher::if_block(&{});",
++ cond_pat, then_pat, self.current
++ );
++ }
++ self.current = cond_pat;
++ self.visit_expr(cond);
++ self.current = then_pat;
++ self.visit_expr(then);
++ return;
++ }
++
++ print!(" if let ExprKind::");
++ let current = format!("{}.kind", self.current);
++ match expr.kind {
++ ExprKind::Box(ref inner) => {
++ let inner_pat = self.next("inner");
++ println!("Box(ref {}) = {};", inner_pat, current);
++ self.current = inner_pat;
++ self.visit_expr(inner);
++ },
++ ExprKind::Array(ref elements) => {
++ let elements_pat = self.next("elements");
++ println!("Array(ref {}) = {};", elements_pat, current);
++ println!(" if {}.len() == {};", elements_pat, elements.len());
++ for (i, element) in elements.iter().enumerate() {
++ self.current = format!("{}[{}]", elements_pat, i);
++ self.visit_expr(element);
++ }
++ },
++ ExprKind::Call(ref func, ref args) => {
++ let func_pat = self.next("func");
++ let args_pat = self.next("args");
++ println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
++ self.current = func_pat;
++ self.visit_expr(func);
++ println!(" if {}.len() == {};", args_pat, args.len());
++ for (i, arg) in args.iter().enumerate() {
++ self.current = format!("{}[{}]", args_pat, i);
++ self.visit_expr(arg);
++ }
++ },
++ ExprKind::MethodCall(ref _method_name, ref _generics, ref _args) => {
++ println!("MethodCall(ref method_name, ref generics, ref args) = {};", current);
++ println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
++ },
++ ExprKind::Tup(ref elements) => {
++ let elements_pat = self.next("elements");
++ println!("Tup(ref {}) = {};", elements_pat, current);
++ println!(" if {}.len() == {};", elements_pat, elements.len());
++ for (i, element) in elements.iter().enumerate() {
++ self.current = format!("{}[{}]", elements_pat, i);
++ self.visit_expr(element);
++ }
++ },
++ ExprKind::Binary(ref op, ref left, ref right) => {
++ let op_pat = self.next("op");
++ let left_pat = self.next("left");
++ let right_pat = self.next("right");
++ println!(
++ "Binary(ref {}, ref {}, ref {}) = {};",
++ op_pat, left_pat, right_pat, current
++ );
++ println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
++ self.current = left_pat;
++ self.visit_expr(left);
++ self.current = right_pat;
++ self.visit_expr(right);
++ },
++ ExprKind::Unary(ref op, ref inner) => {
++ let inner_pat = self.next("inner");
++ println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
++ self.current = inner_pat;
++ self.visit_expr(inner);
++ },
++ ExprKind::Lit(ref lit) => {
++ let lit_pat = self.next("lit");
++ println!("Lit(ref {}) = {};", lit_pat, current);
++ match lit.node {
++ LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
++ LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
++ LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat),
++ LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat),
++ // FIXME: also check int type
++ LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
++ LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
++ " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
++ lit_pat
++ ),
++ LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
++ " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
++ lit_pat
++ ),
++ LitKind::ByteStr(ref vec) => {
++ let vec_pat = self.next("vec");
++ println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
++ println!(" if let [{:?}] = **{};", vec, vec_pat);
++ },
++ LitKind::Str(ref text, _) => {
++ let str_pat = self.next("s");
++ println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
++ println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str())
++ },
++ }
++ },
++ ExprKind::Cast(ref expr, ref ty) => {
++ let cast_pat = self.next("expr");
++ let cast_ty = self.next("cast_ty");
++ let qp_label = self.next("qp");
++
++ println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
++ if let TyKind::Path(ref qp) = ty.kind {
++ println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
++ self.current = qp_label;
++ self.print_qpath(qp);
++ }
++ self.current = cast_pat;
++ self.visit_expr(expr);
++ },
++ ExprKind::Type(ref expr, ref _ty) => {
++ let cast_pat = self.next("expr");
++ println!("Type(ref {}, _) = {};", cast_pat, current);
++ self.current = cast_pat;
++ self.visit_expr(expr);
++ },
++ ExprKind::Loop(ref body, _, desugaring) => {
++ let body_pat = self.next("body");
++ let des = loop_desugaring_name(desugaring);
++ let label_pat = self.next("label");
++ println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current);
++ self.current = body_pat;
++ self.visit_block(body);
++ },
++ ExprKind::Match(ref expr, ref arms, desugaring) => {
++ let des = desugaring_name(desugaring);
++ let expr_pat = self.next("expr");
++ let arms_pat = self.next("arms");
++ println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current);
++ self.current = expr_pat;
++ self.visit_expr(expr);
++ println!(" if {}.len() == {};", arms_pat, arms.len());
++ for (i, arm) in arms.iter().enumerate() {
++ self.current = format!("{}[{}].body", arms_pat, i);
++ self.visit_expr(&arm.body);
++ if let Some(ref guard) = arm.guard {
++ let guard_pat = self.next("guard");
++ println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
++ match guard {
++ hir::Guard::If(ref if_expr) => {
++ let if_expr_pat = self.next("expr");
++ println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
++ self.current = if_expr_pat;
++ self.visit_expr(if_expr);
++ },
++ }
++ }
++ self.current = format!("{}[{}].pat", arms_pat, i);
++ self.visit_pat(&arm.pat);
++ }
++ },
++ ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => {
++ println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
++ println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
++ },
++ ExprKind::Yield(ref sub, _) => {
++ let sub_pat = self.next("sub");
++ println!("Yield(ref sub) = {};", current);
++ self.current = sub_pat;
++ self.visit_expr(sub);
++ },
++ ExprKind::Block(ref block, _) => {
++ let block_pat = self.next("block");
++ println!("Block(ref {}) = {};", block_pat, current);
++ self.current = block_pat;
++ self.visit_block(block);
++ },
++ ExprKind::Assign(ref target, ref value, _) => {
++ let target_pat = self.next("target");
++ let value_pat = self.next("value");
++ println!(
++ "Assign(ref {}, ref {}, ref _span) = {};",
++ target_pat, value_pat, current
++ );
++ self.current = target_pat;
++ self.visit_expr(target);
++ self.current = value_pat;
++ self.visit_expr(value);
++ },
++ ExprKind::AssignOp(ref op, ref target, ref value) => {
++ let op_pat = self.next("op");
++ let target_pat = self.next("target");
++ let value_pat = self.next("value");
++ println!(
++ "AssignOp(ref {}, ref {}, ref {}) = {};",
++ op_pat, target_pat, value_pat, current
++ );
++ println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
++ self.current = target_pat;
++ self.visit_expr(target);
++ self.current = value_pat;
++ self.visit_expr(value);
++ },
++ ExprKind::Field(ref object, ref field_ident) => {
++ let obj_pat = self.next("object");
++ let field_name_pat = self.next("field_name");
++ println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
++ println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
++ self.current = obj_pat;
++ self.visit_expr(object);
++ },
++ ExprKind::Index(ref object, ref index) => {
++ let object_pat = self.next("object");
++ let index_pat = self.next("index");
++ println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
++ self.current = object_pat;
++ self.visit_expr(object);
++ self.current = index_pat;
++ self.visit_expr(index);
++ },
++ ExprKind::Path(ref path) => {
++ let path_pat = self.next("path");
++ println!("Path(ref {}) = {};", path_pat, current);
++ self.current = path_pat;
++ self.print_qpath(path);
++ },
++ ExprKind::AddrOf(kind, mutability, ref inner) => {
++ let inner_pat = self.next("inner");
++ println!(
++ "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
++ kind, mutability, inner_pat, current
++ );
++ self.current = inner_pat;
++ self.visit_expr(inner);
++ },
++ ExprKind::Break(ref _destination, ref opt_value) => {
++ let destination_pat = self.next("destination");
++ if let Some(ref value) = *opt_value {
++ let value_pat = self.next("value");
++ println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
++ self.current = value_pat;
++ self.visit_expr(value);
++ } else {
++ println!("Break(ref {}, None) = {};", destination_pat, current);
++ }
++ // FIXME: implement label printing
++ },
++ ExprKind::Continue(ref _destination) => {
++ let destination_pat = self.next("destination");
++ println!("Again(ref {}) = {};", destination_pat, current);
++ // FIXME: implement label printing
++ },
++ ExprKind::Ret(ref opt_value) => {
++ if let Some(ref value) = *opt_value {
++ let value_pat = self.next("value");
++ println!("Ret(Some(ref {})) = {};", value_pat, current);
++ self.current = value_pat;
++ self.visit_expr(value);
++ } else {
++ println!("Ret(None) = {};", current);
++ }
++ },
++ ExprKind::LlvmInlineAsm(_) => {
++ println!("LlvmInlineAsm(_) = {};", current);
++ println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
++ },
++ ExprKind::Struct(ref path, ref fields, ref opt_base) => {
++ let path_pat = self.next("path");
++ let fields_pat = self.next("fields");
++ if let Some(ref base) = *opt_base {
++ let base_pat = self.next("base");
++ println!(
++ "Struct(ref {}, ref {}, Some(ref {})) = {};",
++ path_pat, fields_pat, base_pat, current
++ );
++ self.current = base_pat;
++ self.visit_expr(base);
++ } else {
++ println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
++ }
++ self.current = path_pat;
++ self.print_qpath(path);
++ println!(" if {}.len() == {};", fields_pat, fields.len());
++ println!(" // unimplemented: field checks");
++ },
++ // FIXME: compute length (needs type info)
++ ExprKind::Repeat(ref value, _) => {
++ let value_pat = self.next("value");
++ println!("Repeat(ref {}, _) = {};", value_pat, current);
++ println!("// unimplemented: repeat count check");
++ self.current = value_pat;
++ self.visit_expr(value);
++ },
++ ExprKind::Err => {
++ println!("Err = {}", current);
++ },
++ ExprKind::DropTemps(ref expr) => {
++ let expr_pat = self.next("expr");
++ println!("DropTemps(ref {}) = {};", expr_pat, current);
++ self.current = expr_pat;
++ self.visit_expr(expr);
++ },
++ }
++ }
++
++ fn visit_block(&mut self, block: &Block<'_>) {
++ let trailing_pat = self.next("trailing_expr");
++ println!(" if let Some({}) = &{}.expr;", trailing_pat, self.current);
++ println!(" if {}.stmts.len() == {};", self.current, block.stmts.len());
++ let current = self.current.clone();
++ for (i, stmt) in block.stmts.iter().enumerate() {
++ self.current = format!("{}.stmts[{}]", current, i);
++ self.visit_stmt(stmt);
++ }
++ }
++
++ #[allow(clippy::too_many_lines)]
++ fn visit_pat(&mut self, pat: &Pat<'_>) {
++ print!(" if let PatKind::");
++ let current = format!("{}.kind", self.current);
++ match pat.kind {
++ PatKind::Wild => println!("Wild = {};", current),
++ PatKind::Binding(anno, .., ident, ref sub) => {
++ let anno_pat = match anno {
++ BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated",
++ BindingAnnotation::Mutable => "BindingAnnotation::Mutable",
++ BindingAnnotation::Ref => "BindingAnnotation::Ref",
++ BindingAnnotation::RefMut => "BindingAnnotation::RefMut",
++ };
++ let name_pat = self.next("name");
++ if let Some(ref sub) = *sub {
++ let sub_pat = self.next("sub");
++ println!(
++ "Binding({}, _, {}, Some(ref {})) = {};",
++ anno_pat, name_pat, sub_pat, current
++ );
++ self.current = sub_pat;
++ self.visit_pat(sub);
++ } else {
++ println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
++ }
++ println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str());
++ },
++ PatKind::Struct(ref path, ref fields, ignore) => {
++ let path_pat = self.next("path");
++ let fields_pat = self.next("fields");
++ println!(
++ "Struct(ref {}, ref {}, {}) = {};",
++ path_pat, fields_pat, ignore, current
++ );
++ self.current = path_pat;
++ self.print_qpath(path);
++ println!(" if {}.len() == {};", fields_pat, fields.len());
++ println!(" // unimplemented: field checks");
++ },
++ PatKind::Or(ref fields) => {
++ let fields_pat = self.next("fields");
++ println!("Or(ref {}) = {};", fields_pat, current);
++ println!(" if {}.len() == {};", fields_pat, fields.len());
++ println!(" // unimplemented: field checks");
++ },
++ PatKind::TupleStruct(ref path, ref fields, skip_pos) => {
++ let path_pat = self.next("path");
++ let fields_pat = self.next("fields");
++ println!(
++ "TupleStruct(ref {}, ref {}, {:?}) = {};",
++ path_pat, fields_pat, skip_pos, current
++ );
++ self.current = path_pat;
++ self.print_qpath(path);
++ println!(" if {}.len() == {};", fields_pat, fields.len());
++ println!(" // unimplemented: field checks");
++ },
++ PatKind::Path(ref path) => {
++ let path_pat = self.next("path");
++ println!("Path(ref {}) = {};", path_pat, current);
++ self.current = path_pat;
++ self.print_qpath(path);
++ },
++ PatKind::Tuple(ref fields, skip_pos) => {
++ let fields_pat = self.next("fields");
++ println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
++ println!(" if {}.len() == {};", fields_pat, fields.len());
++ println!(" // unimplemented: field checks");
++ },
++ PatKind::Box(ref pat) => {
++ let pat_pat = self.next("pat");
++ println!("Box(ref {}) = {};", pat_pat, current);
++ self.current = pat_pat;
++ self.visit_pat(pat);
++ },
++ PatKind::Ref(ref pat, muta) => {
++ let pat_pat = self.next("pat");
++ println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
++ self.current = pat_pat;
++ self.visit_pat(pat);
++ },
++ PatKind::Lit(ref lit_expr) => {
++ let lit_expr_pat = self.next("lit_expr");
++ println!("Lit(ref {}) = {}", lit_expr_pat, current);
++ self.current = lit_expr_pat;
++ self.visit_expr(lit_expr);
++ },
++ PatKind::Range(ref start, ref end, end_kind) => {
++ let start_pat = self.next("start");
++ let end_pat = self.next("end");
++ println!(
++ "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
++ start_pat, end_pat, end_kind, current
++ );
++ self.current = start_pat;
++ walk_list!(self, visit_expr, start);
++ self.current = end_pat;
++ walk_list!(self, visit_expr, end);
++ },
++ PatKind::Slice(ref start, ref middle, ref end) => {
++ let start_pat = self.next("start");
++ let end_pat = self.next("end");
++ if let Some(ref middle) = middle {
++ let middle_pat = self.next("middle");
++ println!(
++ "Slice(ref {}, Some(ref {}), ref {}) = {};",
++ start_pat, middle_pat, end_pat, current
++ );
++ self.current = middle_pat;
++ self.visit_pat(middle);
++ } else {
++ println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
++ }
++ println!(" if {}.len() == {};", start_pat, start.len());
++ for (i, pat) in start.iter().enumerate() {
++ self.current = format!("{}[{}]", start_pat, i);
++ self.visit_pat(pat);
++ }
++ println!(" if {}.len() == {};", end_pat, end.len());
++ for (i, pat) in end.iter().enumerate() {
++ self.current = format!("{}[{}]", end_pat, i);
++ self.visit_pat(pat);
++ }
++ },
++ }
++ }
++
++ fn visit_stmt(&mut self, s: &Stmt<'_>) {
++ print!(" if let StmtKind::");
++ let current = format!("{}.kind", self.current);
++ match s.kind {
++ // A local (let) binding:
++ StmtKind::Local(ref local) => {
++ let local_pat = self.next("local");
++ println!("Local(ref {}) = {};", local_pat, current);
++ if let Some(ref init) = local.init {
++ let init_pat = self.next("init");
++ println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat);
++ self.current = init_pat;
++ self.visit_expr(init);
++ }
++ self.current = format!("{}.pat", local_pat);
++ self.visit_pat(&local.pat);
++ },
++ // An item binding:
++ StmtKind::Item(_) => {
++ println!("Item(item_id) = {};", current);
++ },
++
++ // Expr without trailing semi-colon (must have unit type):
++ StmtKind::Expr(ref e) => {
++ let e_pat = self.next("e");
++ println!("Expr(ref {}, _) = {}", e_pat, current);
++ self.current = e_pat;
++ self.visit_expr(e);
++ },
++
++ // Expr with trailing semi-colon (may have any type):
++ StmtKind::Semi(ref e) => {
++ let e_pat = self.next("e");
++ println!("Semi(ref {}, _) = {}", e_pat, current);
++ self.current = e_pat;
++ self.visit_expr(e);
++ },
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
++ get_attr(sess, attrs, "author").count() > 0
++}
++
++#[must_use]
++fn desugaring_name(des: hir::MatchSource) -> String {
++ match des {
++ hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(),
++ hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(),
++ hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(),
++ hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(),
++ hir::MatchSource::Normal => "MatchSource::Normal".to_string(),
++ hir::MatchSource::IfLetDesugar { contains_else_clause } => format!(
++ "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}",
++ contains_else_clause
++ ),
++ hir::MatchSource::IfDesugar { contains_else_clause } => format!(
++ "MatchSource::IfDesugar {{ contains_else_clause: {} }}",
++ contains_else_clause
++ ),
++ hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(),
++ }
++}
++
++#[must_use]
++fn loop_desugaring_name(des: hir::LoopSource) -> &'static str {
++ match des {
++ hir::LoopSource::ForLoop => "LoopSource::ForLoop",
++ hir::LoopSource::Loop => "LoopSource::Loop",
++ hir::LoopSource::While => "LoopSource::While",
++ hir::LoopSource::WhileLet => "LoopSource::WhileLet",
++ }
++}
++
++fn print_path(path: &QPath<'_>, first: &mut bool) {
++ match *path {
++ QPath::Resolved(_, ref path) => {
++ for segment in path.segments {
++ if *first {
++ *first = false;
++ } else {
++ print!(", ");
++ }
++ print!("{:?}", segment.ident.as_str());
++ }
++ },
++ QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
++ hir::TyKind::Path(ref inner_path) => {
++ print_path(inner_path, first);
++ if *first {
++ *first = false;
++ } else {
++ print!(", ");
++ }
++ print!("{:?}", segment.ident.as_str());
++ },
++ ref other => print!("/* unimplemented: {:?}*/", other),
++ },
++ }
++}
--- /dev/null
--- /dev/null
++/// Returns the index of the character after the first camel-case component of `s`.
++#[must_use]
++pub fn until(s: &str) -> usize {
++ let mut iter = s.char_indices();
++ if let Some((_, first)) = iter.next() {
++ if !first.is_uppercase() {
++ return 0;
++ }
++ } else {
++ return 0;
++ }
++ let mut up = true;
++ let mut last_i = 0;
++ for (i, c) in iter {
++ if up {
++ if c.is_lowercase() {
++ up = false;
++ } else {
++ return last_i;
++ }
++ } else if c.is_uppercase() {
++ up = true;
++ last_i = i;
++ } else if !c.is_lowercase() {
++ return i;
++ }
++ }
++ if up {
++ last_i
++ } else {
++ s.len()
++ }
++}
++
++/// Returns index of the last camel-case component of `s`.
++#[must_use]
++pub fn from(s: &str) -> usize {
++ let mut iter = s.char_indices().rev();
++ if let Some((_, first)) = iter.next() {
++ if !first.is_lowercase() {
++ return s.len();
++ }
++ } else {
++ return s.len();
++ }
++ let mut down = true;
++ let mut last_i = s.len();
++ for (i, c) in iter {
++ if down {
++ if c.is_uppercase() {
++ down = false;
++ last_i = i;
++ } else if !c.is_lowercase() {
++ return last_i;
++ }
++ } else if c.is_lowercase() {
++ down = true;
++ } else {
++ return last_i;
++ }
++ }
++ last_i
++}
++
++#[cfg(test)]
++mod test {
++ use super::{from, until};
++
++ #[test]
++ fn from_full() {
++ assert_eq!(from("AbcDef"), 0);
++ assert_eq!(from("Abc"), 0);
++ }
++
++ #[test]
++ fn from_partial() {
++ assert_eq!(from("abcDef"), 3);
++ assert_eq!(from("aDbc"), 1);
++ }
++
++ #[test]
++ fn from_not() {
++ assert_eq!(from("AbcDef_"), 7);
++ assert_eq!(from("AbcDD"), 5);
++ }
++
++ #[test]
++ fn from_caps() {
++ assert_eq!(from("ABCD"), 4);
++ }
++
++ #[test]
++ fn until_full() {
++ assert_eq!(until("AbcDef"), 6);
++ assert_eq!(until("Abc"), 3);
++ }
++
++ #[test]
++ fn until_not() {
++ assert_eq!(until("abcDef"), 0);
++ assert_eq!(until("aDbc"), 0);
++ }
++
++ #[test]
++ fn until_partial() {
++ assert_eq!(until("AbcDef_"), 6);
++ assert_eq!(until("CallTypeC"), 8);
++ assert_eq!(until("AbcDD"), 3);
++ }
++
++ #[test]
++ fn until_caps() {
++ assert_eq!(until("ABCD"), 0);
++ }
++}
--- /dev/null
--- /dev/null
++//! Utility functions about comparison operators.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use rustc_hir::{BinOpKind, Expr};
++
++#[derive(PartialEq, Eq, Debug, Copy, Clone)]
++/// Represent a normalized comparison operator.
++pub enum Rel {
++ /// `<`
++ Lt,
++ /// `<=`
++ Le,
++ /// `==`
++ Eq,
++ /// `!=`
++ Ne,
++}
++
++/// Put the expression in the form `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or
++/// `lhs != rhs`.
++pub fn normalize_comparison<'a>(
++ op: BinOpKind,
++ lhs: &'a Expr<'a>,
++ rhs: &'a Expr<'a>,
++) -> Option<(Rel, &'a Expr<'a>, &'a Expr<'a>)> {
++ match op {
++ BinOpKind::Lt => Some((Rel::Lt, lhs, rhs)),
++ BinOpKind::Le => Some((Rel::Le, lhs, rhs)),
++ BinOpKind::Gt => Some((Rel::Lt, rhs, lhs)),
++ BinOpKind::Ge => Some((Rel::Le, rhs, lhs)),
++ BinOpKind::Eq => Some((Rel::Eq, rhs, lhs)),
++ BinOpKind::Ne => Some((Rel::Ne, rhs, lhs)),
++ _ => None,
++ }
++}
--- /dev/null
--- /dev/null
++//! Read configurations files.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use lazy_static::lazy_static;
++use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
++use rustc_span::source_map;
++use source_map::Span;
++use std::path::{Path, PathBuf};
++use std::sync::Mutex;
++use std::{env, fmt, fs, io};
++
++/// Gets the configuration file from arguments.
++pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
++ for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
++ if arg.check_name(sym!(conf_file)) {
++ return match arg.kind {
++ MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
++ MetaItemKind::NameValue(ref value) => {
++ if let LitKind::Str(ref file, _) = value.kind {
++ Ok(Some(file.to_string().into()))
++ } else {
++ Err(("`conf_file` value must be a string", value.span))
++ }
++ },
++ };
++ }
++ }
++
++ Ok(None)
++}
++
++/// Error from reading a configuration file.
++#[derive(Debug)]
++pub enum Error {
++ /// An I/O error.
++ Io(io::Error),
++ /// Not valid toml or doesn't fit the expected config format
++ Toml(String),
++}
++
++impl fmt::Display for Error {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ match self {
++ Self::Io(err) => err.fmt(f),
++ Self::Toml(err) => err.fmt(f),
++ }
++ }
++}
++
++impl From<io::Error> for Error {
++ fn from(e: io::Error) -> Self {
++ Self::Io(e)
++ }
++}
++
++lazy_static! {
++ static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
++}
++
++macro_rules! define_Conf {
++ ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
++ mod helpers {
++ use serde::Deserialize;
++ /// Type used to store lint configuration.
++ #[derive(Deserialize)]
++ #[serde(rename_all = "kebab-case", deny_unknown_fields)]
++ pub struct Conf {
++ $(
++ #[$doc]
++ #[serde(default = $config_str)]
++ #[serde(with = $config_str)]
++ pub $config: $Ty,
++ )+
++ #[allow(dead_code)]
++ #[serde(default)]
++ third_party: Option<::toml::Value>,
++ }
++
++ $(
++ mod $config {
++ use serde::Deserialize;
++ pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
++ use super::super::{ERRORS, Error};
++ Ok(
++ <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
++ ERRORS
++ .lock()
++ .expect("no threading here")
++ .push(Error::Toml(e.to_string()));
++ super::$config()
++ })
++ )
++ }
++ }
++
++ #[must_use]
++ fn $config() -> $Ty {
++ let x = $default;
++ x
++ }
++ )+
++ }
++ };
++}
++
++pub use self::helpers::Conf;
++define_Conf! {
++ /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
++ (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
++ /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
++ (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
++ /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
++ (cyclomatic_complexity_threshold, "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, "doc_valid_idents": Vec<String>, [
++ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
++ "DirectX",
++ "ECMAScript",
++ "GPLv2", "GPLv3",
++ "GitHub", "GitLab",
++ "IPv4", "IPv6",
++ "JavaScript",
++ "NaN", "NaNs",
++ "OAuth",
++ "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
++ "TrueType",
++ "iOS", "macOS",
++ "TeX", "LaTeX", "BibTeX", "BibLaTeX",
++ "MinGW",
++ "CamelCase",
++ ].iter().map(ToString::to_string).collect()),
++ /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
++ (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
++ /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
++ (type_complexity_threshold, "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, "single_char_binding_names_threshold": u64, 4),
++ /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
++ (too_large_for_stack, "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, "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, "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, "verbose_bit_mask_threshold": u64, 1),
++ /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
++ (literal_representation_threshold, "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, "trivial_copy_size_limit": Option<u64>, None),
++ /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
++ (too_many_lines_threshold, "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, "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, "vec_box_size_threshold": u64, 4096),
++ /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
++ (max_struct_bools, "max_struct_bools": u64, 3),
++ /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
++ (max_fn_params_bools, "max_fn_params_bools": u64, 3),
++}
++
++impl Default for Conf {
++ #[must_use]
++ fn default() -> Self {
++ toml::from_str("").expect("we never error on empty config files")
++ }
++}
++
++/// Search for the configuration file.
++pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
++ /// Possible filename to search for.
++ const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
++
++ // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
++ // If neither of those exist, use ".".
++ let mut current = env::var_os("CLIPPY_CONF_DIR")
++ .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
++ .map_or_else(|| PathBuf::from("."), PathBuf::from);
++ loop {
++ for config_file_name in &CONFIG_FILE_NAMES {
++ let config_file = current.join(config_file_name);
++ match fs::metadata(&config_file) {
++ // Only return if it's a file to handle the unlikely situation of a directory named
++ // `clippy.toml`.
++ Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
++ // Return the error if it's something other than `NotFound`; otherwise we didn't
++ // find the project file yet, and continue searching.
++ Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
++ _ => {},
++ }
++ }
++
++ // If the current directory has no parent, we're done searching.
++ if !current.pop() {
++ return Ok(None);
++ }
++ }
++}
++
++/// Produces a `Conf` filled with the default values and forwards the errors
++///
++/// Used internally for convenience
++fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
++ (Conf::default(), errors)
++}
++
++/// Read the `toml` configuration file.
++///
++/// In case of error, the function tries to continue as much as possible.
++pub fn read(path: &Path) -> (Conf, Vec<Error>) {
++ let content = match fs::read_to_string(path) {
++ Ok(content) => content,
++ Err(err) => return default(vec![err.into()]),
++ };
++
++ assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
++ match toml::from_str(&content) {
++ Ok(toml) => {
++ let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
++
++ let toml_ref: &Conf = &toml;
++
++ let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;
++
++ if cyc_field.is_some() {
++ let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
++ errors.push(Error::Toml(cyc_err));
++ }
++
++ (toml, errors)
++ },
++ Err(e) => {
++ let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
++ errors.push(Error::Toml(e.to_string()));
++
++ default(errors)
++ },
++ }
++}
--- /dev/null
--- /dev/null
++//! This module contains some useful constants.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++/// List of the built-in types names.
++///
++/// See also [the reference][reference-types] for a list of such types.
++///
++/// [reference-types]: https://doc.rust-lang.org/reference/types.html
++pub const BUILTIN_TYPES: &[&str] = &[
++ "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "isize", "usize", "f32", "f64", "bool",
++ "str", "char",
++];
--- /dev/null
--- /dev/null
++//! Clippy wrappers around rustc's diagnostic functions.
++
++use rustc_errors::{Applicability, CodeSuggestion, DiagnosticBuilder, Substitution, SubstitutionPart, SuggestionStyle};
++use rustc_hir::HirId;
++use rustc_lint::{LateContext, Lint, LintContext};
++use rustc_span::source_map::{MultiSpan, Span};
++use std::env;
++
++fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
++ if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
++ diag.help(&format!(
++ "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
++ &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
++ // extract just major + minor version and ignore patch versions
++ format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
++ }),
++ lint.name_lower().replacen("clippy::", "", 1)
++ ));
++ }
++}
++
++/// Emit a basic lint message with a `msg` and a `span`.
++///
++/// This is the most primitive of our lint emission methods and can
++/// be a good way to get a new lint started.
++///
++/// Usually it's nicer to provide more context for lint messages.
++/// Be sure the output is understandable when you use this method.
++///
++/// # Example
++///
++/// ```ignore
++/// error: usage of mem::forget on Drop type
++/// --> $DIR/mem_forget.rs:17:5
++/// |
++/// 17 | std::mem::forget(seven);
++/// | ^^^^^^^^^^^^^^^^^^^^^^^
++/// ```
++pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
++ cx.struct_span_lint(lint, sp, |diag| {
++ let mut diag = diag.build(msg);
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++/// Same as `span_lint` but with an extra `help` message.
++///
++/// Use this if you want to provide some general help but
++/// can't provide a specific machine applicable suggestion.
++///
++/// The `help` message can be optionally attached to a `Span`.
++///
++/// # Example
++///
++/// ```ignore
++/// error: constant division of 0.0 with 0.0 will always result in NaN
++/// --> $DIR/zero_div_zero.rs:6:25
++/// |
++/// 6 | let other_f64_nan = 0.0f64 / 0.0;
++/// | ^^^^^^^^^^^^
++/// |
++/// = help: Consider using `f64::NAN` if you would like a constant representing NaN
++/// ```
++pub fn span_lint_and_help<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ help_span: Option<Span>,
++ help: &str,
++) {
++ cx.struct_span_lint(lint, span, |diag| {
++ let mut diag = diag.build(msg);
++ if let Some(help_span) = help_span {
++ diag.span_help(help_span, help);
++ } else {
++ diag.help(help);
++ }
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++/// Like `span_lint` but with a `note` section instead of a `help` message.
++///
++/// The `note` message is presented separately from the main lint message
++/// and is attached to a specific span:
++///
++/// # Example
++///
++/// ```ignore
++/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++/// --> $DIR/drop_forget_ref.rs:10:5
++/// |
++/// 10 | forget(&SomeStruct);
++/// | ^^^^^^^^^^^^^^^^^^^
++/// |
++/// = note: `-D clippy::forget-ref` implied by `-D warnings`
++/// note: argument has type &SomeStruct
++/// --> $DIR/drop_forget_ref.rs:10:12
++/// |
++/// 10 | forget(&SomeStruct);
++/// | ^^^^^^^^^^^
++/// ```
++pub fn span_lint_and_note<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ note_span: Option<Span>,
++ note: &str,
++) {
++ cx.struct_span_lint(lint, span, |diag| {
++ let mut diag = diag.build(msg);
++ if let Some(note_span) = note_span {
++ diag.span_note(note_span, note);
++ } else {
++ diag.note(note);
++ }
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
++///
++/// If you need to customize your lint output a lot, use this function.
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++ F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++ cx.struct_span_lint(lint, sp, |diag| {
++ let mut diag = diag.build(msg);
++ f(&mut diag);
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++pub fn span_lint_hir(cx: &LateContext<'_, '_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
++ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
++ let mut diag = diag.build(msg);
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++pub fn span_lint_hir_and_then(
++ cx: &LateContext<'_, '_>,
++ lint: &'static Lint,
++ hir_id: HirId,
++ sp: Span,
++ msg: &str,
++ f: impl FnOnce(&mut DiagnosticBuilder<'_>),
++) {
++ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
++ let mut diag = diag.build(msg);
++ f(&mut diag);
++ docs_link(&mut diag, lint);
++ diag.emit();
++ });
++}
++
++/// Add a span lint with a suggestion on how to fix it.
++///
++/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
++/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
++/// 2)"`.
++///
++/// ```ignore
++/// error: This `.fold` can be more succinctly expressed as `.any`
++/// --> $DIR/methods.rs:390:13
++/// |
++/// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
++/// |
++/// = note: `-D fold-any` implied by `-D warnings`
++/// ```
++#[allow(clippy::collapsible_span_lint_calls)]
++pub fn span_lint_and_sugg<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ sp: Span,
++ msg: &str,
++ help: &str,
++ sugg: String,
++ applicability: Applicability,
++) {
++ span_lint_and_then(cx, lint, sp, msg, |diag| {
++ diag.span_suggestion(sp, help, sugg, applicability);
++ });
++}
++
++/// Create a suggestion made from several `span → replacement`.
++///
++/// Note: in the JSON format (used by `compiletest_rs`), the help message will
++/// appear once per
++/// replacement. In human-readable format though, it only appears once before
++/// the whole suggestion.
++pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I)
++where
++ I: IntoIterator<Item = (Span, String)>,
++{
++ let sugg = CodeSuggestion {
++ substitutions: vec![Substitution {
++ parts: sugg
++ .into_iter()
++ .map(|(span, snippet)| SubstitutionPart { snippet, span })
++ .collect(),
++ }],
++ msg: help_msg,
++ style: SuggestionStyle::ShowCode,
++ applicability: Applicability::Unspecified,
++ };
++ diag.suggestions.push(sugg);
++}
--- /dev/null
--- /dev/null
++//! This module contains functions for retrieve the original AST from lowered
++//! `hir`.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use crate::utils::{is_expn_of, match_def_path, match_qpath, paths};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++
++/// Converts a hir binary operator to the corresponding `ast` type.
++#[must_use]
++pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
++ match op {
++ hir::BinOpKind::Eq => ast::BinOpKind::Eq,
++ hir::BinOpKind::Ge => ast::BinOpKind::Ge,
++ hir::BinOpKind::Gt => ast::BinOpKind::Gt,
++ hir::BinOpKind::Le => ast::BinOpKind::Le,
++ hir::BinOpKind::Lt => ast::BinOpKind::Lt,
++ hir::BinOpKind::Ne => ast::BinOpKind::Ne,
++ hir::BinOpKind::Or => ast::BinOpKind::Or,
++ hir::BinOpKind::Add => ast::BinOpKind::Add,
++ hir::BinOpKind::And => ast::BinOpKind::And,
++ hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
++ hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
++ hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
++ hir::BinOpKind::Div => ast::BinOpKind::Div,
++ hir::BinOpKind::Mul => ast::BinOpKind::Mul,
++ hir::BinOpKind::Rem => ast::BinOpKind::Rem,
++ hir::BinOpKind::Shl => ast::BinOpKind::Shl,
++ hir::BinOpKind::Shr => ast::BinOpKind::Shr,
++ hir::BinOpKind::Sub => ast::BinOpKind::Sub,
++ }
++}
++
++/// Represent a range akin to `ast::ExprKind::Range`.
++#[derive(Debug, Copy, Clone)]
++pub struct Range<'a> {
++ /// The lower bound of the range, or `None` for ranges such as `..X`.
++ pub start: Option<&'a hir::Expr<'a>>,
++ /// The upper bound of the range, or `None` for ranges such as `X..`.
++ pub end: Option<&'a hir::Expr<'a>>,
++ /// Whether the interval is open or closed.
++ pub limits: ast::RangeLimits,
++}
++
++/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
++pub fn range<'a, 'b, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'b hir::Expr<'_>) -> Option<Range<'b>> {
++ /// Finds the field named `name` in the field. Always return `Some` for
++ /// convenience.
++ fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> {
++ let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
++
++ Some(expr)
++ }
++
++ let def_path = match cx.tables.expr_ty(expr).kind {
++ ty::Adt(def, _) => cx.tcx.def_path(def.did),
++ _ => return None,
++ };
++
++ // sanity checks for std::ops::RangeXXXX
++ if def_path.data.len() != 3 {
++ return None;
++ }
++ if def_path.data.get(0)?.data.as_symbol() != sym!(ops) {
++ return None;
++ }
++ if def_path.data.get(1)?.data.as_symbol() != sym!(range) {
++ return None;
++ }
++ let type_name = def_path.data.get(2)?.data.as_symbol();
++ let range_types = [
++ "RangeFrom",
++ "RangeFull",
++ "RangeInclusive",
++ "Range",
++ "RangeTo",
++ "RangeToInclusive",
++ ];
++ if !range_types.contains(&&*type_name.as_str()) {
++ return None;
++ }
++
++ // The range syntax is expanded to literal paths starting with `core` or `std`
++ // depending on
++ // `#[no_std]`. Testing both instead of resolving the paths.
++
++ match expr.kind {
++ hir::ExprKind::Path(ref path) => {
++ if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) {
++ Some(Range {
++ start: None,
++ end: None,
++ limits: ast::RangeLimits::HalfOpen,
++ })
++ } else {
++ None
++ }
++ },
++ hir::ExprKind::Call(ref path, ref args) => {
++ if let hir::ExprKind::Path(ref path) = path.kind {
++ if match_qpath(path, &paths::RANGE_INCLUSIVE_STD_NEW) || match_qpath(path, &paths::RANGE_INCLUSIVE_NEW)
++ {
++ Some(Range {
++ start: Some(&args[0]),
++ end: Some(&args[1]),
++ limits: ast::RangeLimits::Closed,
++ })
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++ },
++ hir::ExprKind::Struct(ref path, ref fields, None) => {
++ if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) {
++ Some(Range {
++ start: Some(get_field("start", fields)?),
++ end: None,
++ limits: ast::RangeLimits::HalfOpen,
++ })
++ } else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) {
++ Some(Range {
++ start: Some(get_field("start", fields)?),
++ end: Some(get_field("end", fields)?),
++ limits: ast::RangeLimits::HalfOpen,
++ })
++ } else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE)
++ {
++ Some(Range {
++ start: None,
++ end: Some(get_field("end", fields)?),
++ limits: ast::RangeLimits::Closed,
++ })
++ } else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) {
++ Some(Range {
++ start: None,
++ end: Some(get_field("end", fields)?),
++ limits: ast::RangeLimits::HalfOpen,
++ })
++ } else {
++ None
++ }
++ },
++ _ => None,
++ }
++}
++
++/// Checks if a `let` statement is from a `for` loop desugaring.
++pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
++ // This will detect plain for-loops without an actual variable binding:
++ //
++ // ```
++ // for x in some_vec {
++ // // do stuff
++ // }
++ // ```
++ if_chain! {
++ if let Some(ref expr) = local.init;
++ if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
++ then {
++ return true;
++ }
++ }
++
++ // This detects a variable binding in for loop to avoid `let_unit_value`
++ // lint (see issue #1964).
++ //
++ // ```
++ // for _ in vec![()] {
++ // // anything
++ // }
++ // ```
++ if let hir::LocalSource::ForLoopDesugar = local.source {
++ return true;
++ }
++
++ false
++}
++
++/// Recover the essential nodes of a desugared for loop:
++/// `for pat in arg { body }` becomes `(pat, arg, body)`.
++pub fn for_loop<'tcx>(
++ expr: &'tcx hir::Expr<'tcx>,
++) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
++ if_chain! {
++ if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
++ if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
++ if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
++ if let hir::ExprKind::Loop(ref block, _, _) = arms[0].body.kind;
++ if block.expr.is_none();
++ if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
++ if let hir::StmtKind::Local(ref local) = let_stmt.kind;
++ if let hir::StmtKind::Expr(ref expr) = body.kind;
++ then {
++ return Some((&*local.pat, &iterargs[0], expr));
++ }
++ }
++ None
++}
++
++/// Recover the essential nodes of a desugared while loop:
++/// `while cond { body }` becomes `(cond, body)`.
++pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
++ if_chain! {
++ if let hir::ExprKind::Loop(block, _, hir::LoopSource::While) = &expr.kind;
++ if let hir::Block { expr: Some(expr), .. } = &**block;
++ if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
++ if let hir::ExprKind::DropTemps(cond) = &cond.kind;
++ if let [arm, ..] = &arms[..];
++ if let hir::Arm { body, .. } = arm;
++ then {
++ return Some((cond, body));
++ }
++ }
++ None
++}
++
++/// Recover the essential nodes of a desugared if block
++/// `if cond { then } else { els }` becomes `(cond, then, Some(els))`
++pub fn if_block<'tcx>(
++ expr: &'tcx hir::Expr<'tcx>,
++) -> Option<(
++ &'tcx hir::Expr<'tcx>,
++ &'tcx hir::Expr<'tcx>,
++ Option<&'tcx hir::Expr<'tcx>>,
++)> {
++ if let hir::ExprKind::Match(ref cond, ref arms, hir::MatchSource::IfDesugar { contains_else_clause }) = expr.kind {
++ let cond = if let hir::ExprKind::DropTemps(ref cond) = cond.kind {
++ cond
++ } else {
++ panic!("If block desugar must contain DropTemps");
++ };
++ let then = &arms[0].body;
++ let els = if contains_else_clause {
++ Some(&*arms[1].body)
++ } else {
++ None
++ };
++ Some((cond, then, els))
++ } else {
++ None
++ }
++}
++
++/// Represent the pre-expansion arguments of a `vec!` invocation.
++pub enum VecArgs<'a> {
++ /// `vec![elem; len]`
++ Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
++ /// `vec![a, b, c]`
++ Vec(&'a [hir::Expr<'a>]),
++}
++
++/// Returns the arguments of the `vec!` macro if this expression was expanded
++/// from `vec!`.
++pub fn vec_macro<'e>(cx: &LateContext<'_, '_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
++ if_chain! {
++ if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
++ if let hir::ExprKind::Path(ref qpath) = fun.kind;
++ if is_expn_of(fun.span, "vec").is_some();
++ if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
++ then {
++ return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
++ // `vec![elem; size]` case
++ Some(VecArgs::Repeat(&args[0], &args[1]))
++ }
++ else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
++ // `vec![a, b, c]` case
++ if_chain! {
++ if let hir::ExprKind::Box(ref boxed) = args[0].kind;
++ if let hir::ExprKind::Array(ref args) = boxed.kind;
++ then {
++ return Some(VecArgs::Vec(&*args));
++ }
++ }
++
++ None
++ }
++ else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
++ Some(VecArgs::Vec(&[]))
++ }
++ else {
++ None
++ };
++ }
++ }
++
++ None
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant_context, constant_simple};
++use crate::utils::differing_macro_contexts;
++use rustc_ast::ast::Name;
++use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
++use rustc_hir::{
++ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg,
++ GenericArgs, Guard, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty,
++ TyKind, TypeBinding,
++};
++use rustc_lint::LateContext;
++use rustc_middle::ich::StableHashingContextProvider;
++use rustc_middle::ty::TypeckTables;
++use std::hash::Hash;
++
++/// Type used to check whether two ast are the same. This is different from the
++/// operator
++/// `==` on ast types as this operator would compare true equality with ID and
++/// span.
++///
++/// Note that some expressions kinds are not considered but could be added.
++pub struct SpanlessEq<'a, 'tcx> {
++ /// Context used to evaluate constant expressions.
++ cx: &'a LateContext<'a, 'tcx>,
++ tables: &'a TypeckTables<'tcx>,
++ /// If is true, never consider as equal expressions containing function
++ /// calls.
++ ignore_fn: bool,
++}
++
++impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
++ pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++ Self {
++ cx,
++ tables: cx.tables,
++ ignore_fn: false,
++ }
++ }
++
++ pub fn ignore_fn(self) -> Self {
++ Self {
++ cx: self.cx,
++ tables: self.cx.tables,
++ ignore_fn: true,
++ }
++ }
++
++ /// Checks whether two statements are the same.
++ pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
++ match (&left.kind, &right.kind) {
++ (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
++ self.eq_pat(&l.pat, &r.pat)
++ && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
++ && both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
++ },
++ (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => {
++ self.eq_expr(l, r)
++ },
++ _ => false,
++ }
++ }
++
++ /// Checks whether two blocks are the same.
++ pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
++ over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
++ && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
++ }
++
++ #[allow(clippy::similar_names)]
++ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
++ if self.ignore_fn && differing_macro_contexts(left.span, right.span) {
++ return false;
++ }
++
++ if let (Some(l), Some(r)) = (
++ constant_simple(self.cx, self.tables, left),
++ constant_simple(self.cx, self.tables, right),
++ ) {
++ if l == r {
++ return true;
++ }
++ }
++
++ match (&left.kind, &right.kind) {
++ (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
++ lb == rb && l_mut == r_mut && self.eq_expr(le, re)
++ },
++ (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
++ both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
++ },
++ (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
++ self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++ },
++ (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
++ lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++ },
++ (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
++ (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
++ l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++ || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
++ l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++ })
++ },
++ (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
++ both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
++ && both(le, re, |l, r| self.eq_expr(l, r))
++ },
++ (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
++ (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
++ !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
++ },
++ (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
++ | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
++ self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
++ },
++ (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => {
++ l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
++ },
++ (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
++ self.eq_expr(la, ra) && self.eq_expr(li, ri)
++ },
++ (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
++ (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => {
++ lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str())
++ },
++ (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
++ ls == rs
++ && self.eq_expr(le, re)
++ && over(la, ra, |l, r| {
++ self.eq_expr(&l.body, &r.body)
++ && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
++ && self.eq_pat(&l.pat, &r.pat)
++ })
++ },
++ (&ExprKind::MethodCall(l_path, _, l_args), &ExprKind::MethodCall(r_path, _, r_args)) => {
++ !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
++ },
++ (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
++ let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body));
++ let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
++ let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body));
++ let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
++
++ self.eq_expr(le, re) && ll == rl
++ },
++ (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
++ (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
++ (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => {
++ self.eq_qpath(l_path, r_path)
++ && both(lo, ro, |l, r| self.eq_expr(l, r))
++ && over(lf, rf, |l, r| self.eq_field(l, r))
++ },
++ (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
++ (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
++ (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
++ (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
++ _ => false,
++ }
++ }
++
++ fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
++ over(left, right, |l, r| self.eq_expr(l, r))
++ }
++
++ fn eq_field(&mut self, left: &Field<'_>, right: &Field<'_>) -> bool {
++ left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
++ }
++
++ fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
++ match (left, right) {
++ (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
++ }
++ }
++
++ fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
++ match (left, right) {
++ (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
++ (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
++ _ => false,
++ }
++ }
++
++ fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
++ left.name == right.name
++ }
++
++ /// Checks whether two patterns are the same.
++ pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
++ match (&left.kind, &right.kind) {
++ (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
++ (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
++ self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
++ },
++ (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => {
++ lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r))
++ },
++ (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
++ (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
++ (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => {
++ ls == rs && over(l, r, |l, r| self.eq_pat(l, r))
++ },
++ (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
++ both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
++ },
++ (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
++ (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => {
++ over(ls, rs, |l, r| self.eq_pat(l, r))
++ && over(le, re, |l, r| self.eq_pat(l, r))
++ && both(li, ri, |l, r| self.eq_pat(l, r))
++ },
++ (&PatKind::Wild, &PatKind::Wild) => true,
++ _ => false,
++ }
++ }
++
++ #[allow(clippy::similar_names)]
++ fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
++ match (left, right) {
++ (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
++ both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
++ },
++ (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
++ self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
++ },
++ _ => false,
++ }
++ }
++
++ fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
++ left.is_global() == right.is_global()
++ && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
++ }
++
++ fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
++ if !(left.parenthesized || right.parenthesized) {
++ over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
++ && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
++ } else if left.parenthesized && right.parenthesized {
++ over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
++ && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
++ self.eq_ty(l, r)
++ })
++ } else {
++ false
++ }
++ }
++
++ pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
++ left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
++ }
++
++ pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
++ // The == of idents doesn't work with different contexts,
++ // we have to be explicit about hygiene
++ if left.ident.as_str() != right.ident.as_str() {
++ return false;
++ }
++ match (&left.args, &right.args) {
++ (&None, &None) => true,
++ (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
++ _ => false,
++ }
++ }
++
++ pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
++ self.eq_ty_kind(&left.kind, &right.kind)
++ }
++
++ #[allow(clippy::similar_names)]
++ pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
++ match (left, right) {
++ (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
++ (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
++ let full_table = self.tables;
++
++ let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body));
++ self.tables = self.cx.tcx.body_tables(ll_id.body);
++ let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
++
++ let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body));
++ self.tables = self.cx.tcx.body_tables(rl_id.body);
++ let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
++
++ let eq_ty = self.eq_ty(lt, rt);
++ self.tables = full_table;
++ eq_ty && ll == rl
++ },
++ (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
++ l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
++ },
++ (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
++ l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
++ },
++ (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
++ (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)),
++ (&TyKind::Infer, &TyKind::Infer) => true,
++ _ => false,
++ }
++ }
++
++ fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
++ left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty())
++ }
++}
++
++fn swap_binop<'a>(
++ binop: BinOpKind,
++ lhs: &'a Expr<'a>,
++ rhs: &'a Expr<'a>,
++) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
++ match binop {
++ BinOpKind::Add
++ | BinOpKind::Mul
++ | BinOpKind::Eq
++ | BinOpKind::Ne
++ | BinOpKind::BitAnd
++ | BinOpKind::BitXor
++ | BinOpKind::BitOr => Some((binop, rhs, lhs)),
++ BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
++ BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
++ BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
++ BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
++ BinOpKind::Shl
++ | BinOpKind::Shr
++ | BinOpKind::Rem
++ | BinOpKind::Sub
++ | BinOpKind::Div
++ | BinOpKind::And
++ | BinOpKind::Or => None,
++ }
++}
++
++/// Checks if the two `Option`s are both `None` or some equal values as per
++/// `eq_fn`.
++fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
++where
++ F: FnMut(&X, &X) -> bool,
++{
++ l.as_ref()
++ .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
++}
++
++/// Checks if two slices are equal as per `eq_fn`.
++fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
++where
++ F: FnMut(&X, &X) -> bool,
++{
++ left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
++}
++
++/// Type used to hash an ast element. This is different from the `Hash` trait
++/// on ast types as this
++/// trait would consider IDs and spans.
++///
++/// All expressions kind are hashed, but some might have a weaker hash.
++pub struct SpanlessHash<'a, 'tcx> {
++ /// Context used to evaluate constant expressions.
++ cx: &'a LateContext<'a, 'tcx>,
++ tables: &'a TypeckTables<'tcx>,
++ s: StableHasher,
++}
++
++impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
++ pub fn new(cx: &'a LateContext<'a, 'tcx>, tables: &'a TypeckTables<'tcx>) -> Self {
++ Self {
++ cx,
++ tables,
++ s: StableHasher::new(),
++ }
++ }
++
++ pub fn finish(self) -> u64 {
++ self.s.finish()
++ }
++
++ pub fn hash_block(&mut self, b: &Block<'_>) {
++ for s in b.stmts {
++ self.hash_stmt(s);
++ }
++
++ if let Some(ref e) = b.expr {
++ self.hash_expr(e);
++ }
++
++ match b.rules {
++ BlockCheckMode::DefaultBlock => 0,
++ BlockCheckMode::UnsafeBlock(_) => 1,
++ BlockCheckMode::PushUnsafeBlock(_) => 2,
++ BlockCheckMode::PopUnsafeBlock(_) => 3,
++ }
++ .hash(&mut self.s);
++ }
++
++ #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
++ pub fn hash_expr(&mut self, e: &Expr<'_>) {
++ let simple_const = constant_simple(self.cx, self.tables, e);
++
++ // const hashing may result in the same hash as some unrelated node, so add a sort of
++ // discriminant depending on which path we're choosing next
++ simple_const.is_some().hash(&mut self.s);
++
++ if let Some(e) = simple_const {
++ return e.hash(&mut self.s);
++ }
++
++ std::mem::discriminant(&e.kind).hash(&mut self.s);
++
++ match e.kind {
++ ExprKind::AddrOf(kind, m, ref e) => {
++ match kind {
++ BorrowKind::Ref => 0,
++ BorrowKind::Raw => 1,
++ }
++ .hash(&mut self.s);
++ m.hash(&mut self.s);
++ self.hash_expr(e);
++ },
++ ExprKind::Continue(i) => {
++ if let Some(i) = i.label {
++ self.hash_name(i.ident.name);
++ }
++ },
++ ExprKind::Assign(ref l, ref r, _) => {
++ self.hash_expr(l);
++ self.hash_expr(r);
++ },
++ ExprKind::AssignOp(ref o, ref l, ref r) => {
++ o.node
++ .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++ self.hash_expr(l);
++ self.hash_expr(r);
++ },
++ ExprKind::Block(ref b, _) => {
++ self.hash_block(b);
++ },
++ ExprKind::Binary(op, ref l, ref r) => {
++ op.node
++ .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++ self.hash_expr(l);
++ self.hash_expr(r);
++ },
++ ExprKind::Break(i, ref j) => {
++ if let Some(i) = i.label {
++ self.hash_name(i.ident.name);
++ }
++ if let Some(ref j) = *j {
++ self.hash_expr(&*j);
++ }
++ },
++ ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => {
++ self.hash_expr(e);
++ },
++ ExprKind::Call(ref fun, args) => {
++ self.hash_expr(fun);
++ self.hash_exprs(args);
++ },
++ ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
++ self.hash_expr(e);
++ self.hash_ty(ty);
++ },
++ ExprKind::Closure(cap, _, eid, _, _) => {
++ match cap {
++ CaptureBy::Value => 0,
++ CaptureBy::Ref => 1,
++ }
++ .hash(&mut self.s);
++ // closures inherit TypeckTables
++ self.hash_expr(&self.cx.tcx.hir().body(eid).value);
++ },
++ ExprKind::Field(ref e, ref f) => {
++ self.hash_expr(e);
++ self.hash_name(f.name);
++ },
++ ExprKind::Index(ref a, ref i) => {
++ self.hash_expr(a);
++ self.hash_expr(i);
++ },
++ ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
++ ExprKind::Lit(ref l) => {
++ l.node.hash(&mut self.s);
++ },
++ ExprKind::Loop(ref b, ref i, _) => {
++ self.hash_block(b);
++ if let Some(i) = *i {
++ self.hash_name(i.ident.name);
++ }
++ },
++ ExprKind::Match(ref e, arms, ref s) => {
++ self.hash_expr(e);
++
++ for arm in arms {
++ // TODO: arm.pat?
++ if let Some(ref e) = arm.guard {
++ self.hash_guard(e);
++ }
++ self.hash_expr(&arm.body);
++ }
++
++ s.hash(&mut self.s);
++ },
++ ExprKind::MethodCall(ref path, ref _tys, args) => {
++ self.hash_name(path.ident.name);
++ self.hash_exprs(args);
++ },
++ ExprKind::Repeat(ref e, ref l_id) => {
++ self.hash_expr(e);
++ self.hash_body(l_id.body);
++ },
++ ExprKind::Ret(ref e) => {
++ if let Some(ref e) = *e {
++ self.hash_expr(e);
++ }
++ },
++ ExprKind::Path(ref qpath) => {
++ self.hash_qpath(qpath);
++ },
++ ExprKind::Struct(ref path, fields, ref expr) => {
++ self.hash_qpath(path);
++
++ for f in fields {
++ self.hash_name(f.ident.name);
++ self.hash_expr(&f.expr);
++ }
++
++ if let Some(ref e) = *expr {
++ self.hash_expr(e);
++ }
++ },
++ ExprKind::Tup(tup) => {
++ self.hash_exprs(tup);
++ },
++ ExprKind::Array(v) => {
++ self.hash_exprs(v);
++ },
++ ExprKind::Unary(lop, ref le) => {
++ lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++ self.hash_expr(le);
++ },
++ }
++ }
++
++ pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
++ for e in e {
++ self.hash_expr(e);
++ }
++ }
++
++ pub fn hash_name(&mut self, n: Name) {
++ n.as_str().hash(&mut self.s);
++ }
++
++ pub fn hash_qpath(&mut self, p: &QPath<'_>) {
++ match *p {
++ QPath::Resolved(_, ref path) => {
++ self.hash_path(path);
++ },
++ QPath::TypeRelative(_, ref path) => {
++ self.hash_name(path.ident.name);
++ },
++ }
++ // self.cx.tables.qpath_res(p, id).hash(&mut self.s);
++ }
++
++ pub fn hash_path(&mut self, p: &Path<'_>) {
++ p.is_global().hash(&mut self.s);
++ for p in p.segments {
++ self.hash_name(p.ident.name);
++ }
++ }
++
++ pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
++ std::mem::discriminant(&b.kind).hash(&mut self.s);
++
++ match &b.kind {
++ StmtKind::Local(local) => {
++ if let Some(ref init) = local.init {
++ self.hash_expr(init);
++ }
++ },
++ StmtKind::Item(..) => {},
++ StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
++ self.hash_expr(expr);
++ },
++ }
++ }
++
++ pub fn hash_guard(&mut self, g: &Guard<'_>) {
++ match g {
++ Guard::If(ref expr) => {
++ self.hash_expr(expr);
++ },
++ }
++ }
++
++ pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
++ std::mem::discriminant(&lifetime.name).hash(&mut self.s);
++ if let LifetimeName::Param(ref name) = lifetime.name {
++ std::mem::discriminant(name).hash(&mut self.s);
++ match name {
++ ParamName::Plain(ref ident) => {
++ ident.name.hash(&mut self.s);
++ },
++ ParamName::Fresh(ref size) => {
++ size.hash(&mut self.s);
++ },
++ ParamName::Error => {},
++ }
++ }
++ }
++
++ pub fn hash_ty(&mut self, ty: &Ty<'_>) {
++ self.hash_tykind(&ty.kind);
++ }
++
++ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
++ std::mem::discriminant(ty).hash(&mut self.s);
++ match ty {
++ TyKind::Slice(ty) => {
++ self.hash_ty(ty);
++ },
++ TyKind::Array(ty, anon_const) => {
++ self.hash_ty(ty);
++ self.hash_body(anon_const.body);
++ },
++ TyKind::Ptr(mut_ty) => {
++ self.hash_ty(&mut_ty.ty);
++ mut_ty.mutbl.hash(&mut self.s);
++ },
++ TyKind::Rptr(lifetime, mut_ty) => {
++ self.hash_lifetime(lifetime);
++ self.hash_ty(&mut_ty.ty);
++ mut_ty.mutbl.hash(&mut self.s);
++ },
++ TyKind::BareFn(bfn) => {
++ bfn.unsafety.hash(&mut self.s);
++ bfn.abi.hash(&mut self.s);
++ for arg in bfn.decl.inputs {
++ self.hash_ty(&arg);
++ }
++ match bfn.decl.output {
++ FnRetTy::DefaultReturn(_) => {
++ ().hash(&mut self.s);
++ },
++ FnRetTy::Return(ref ty) => {
++ self.hash_ty(ty);
++ },
++ }
++ bfn.decl.c_variadic.hash(&mut self.s);
++ },
++ TyKind::Tup(ty_list) => {
++ for ty in *ty_list {
++ self.hash_ty(ty);
++ }
++ },
++ TyKind::Path(qpath) => match qpath {
++ QPath::Resolved(ref maybe_ty, ref path) => {
++ if let Some(ref ty) = maybe_ty {
++ self.hash_ty(ty);
++ }
++ for segment in path.segments {
++ segment.ident.name.hash(&mut self.s);
++ }
++ },
++ QPath::TypeRelative(ref ty, ref segment) => {
++ self.hash_ty(ty);
++ segment.ident.name.hash(&mut self.s);
++ },
++ },
++ TyKind::Def(_, arg_list) => {
++ for arg in *arg_list {
++ match arg {
++ GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
++ GenericArg::Type(ref ty) => self.hash_ty(&ty),
++ GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
++ }
++ }
++ },
++ TyKind::TraitObject(_, lifetime) => {
++ self.hash_lifetime(lifetime);
++ },
++ TyKind::Typeof(anon_const) => {
++ self.hash_body(anon_const.body);
++ },
++ TyKind::Err | TyKind::Infer | TyKind::Never => {},
++ }
++ }
++
++ pub fn hash_body(&mut self, body_id: BodyId) {
++ // swap out TypeckTables when hashing a body
++ let old_tables = self.tables;
++ self.tables = self.cx.tcx.body_tables(body_id);
++ self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
++ self.tables = old_tables;
++ }
++}
--- /dev/null
--- /dev/null
++//! checks for attributes
++
++use crate::utils::get_attr;
++use rustc_ast::ast::Attribute;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::Session;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]`
++ /// attribute
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// #[clippy::dump]
++ /// extern crate foo;
++ /// ```
++ ///
++ /// prints
++ ///
++ /// ```text
++ /// item `foo`
++ /// visibility inherited from outer item
++ /// extern crate dylib source: "/path/to/foo.so"
++ /// ```
++ pub DEEP_CODE_INSPECTION,
++ internal_warn,
++ "helper to dump info about code"
++}
++
++declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DeepCodeInspector {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ print_item(cx, item);
++ }
++
++ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++ if !has_attr(cx.sess(), &item.attrs) {
++ return;
++ }
++ println!("impl item `{}`", item.ident.name);
++ match item.vis.node {
++ hir::VisibilityKind::Public => println!("public"),
++ hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
++ hir::VisibilityKind::Restricted { ref path, .. } => println!(
++ "visible in module `{}`",
++ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
++ ),
++ hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
++ }
++ if item.defaultness.is_default() {
++ println!("default");
++ }
++ match item.kind {
++ hir::ImplItemKind::Const(_, body_id) => {
++ println!("associated constant");
++ print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
++ },
++ hir::ImplItemKind::Fn(..) => println!("method"),
++ hir::ImplItemKind::TyAlias(_) => println!("associated type"),
++ hir::ImplItemKind::OpaqueTy(_) => println!("existential type"),
++ }
++ }
++ // fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
++ // hir::TraitItem) {
++ // if !has_attr(&item.attrs) {
++ // return;
++ // }
++ // }
++ //
++ // fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx
++ // hir::Variant, _:
++ // &hir::Generics) {
++ // if !has_attr(&var.node.attrs) {
++ // return;
++ // }
++ // }
++ //
++ // fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx
++ // hir::StructField) {
++ // if !has_attr(&field.attrs) {
++ // return;
++ // }
++ // }
++ //
++
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if !has_attr(cx.sess(), &expr.attrs) {
++ return;
++ }
++ print_expr(cx, expr, 0);
++ }
++
++ fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) {
++ if !has_attr(cx.sess(), &arm.attrs) {
++ return;
++ }
++ print_pat(cx, &arm.pat, 1);
++ if let Some(ref guard) = arm.guard {
++ println!("guard:");
++ print_guard(cx, guard, 1);
++ }
++ println!("body:");
++ print_expr(cx, &arm.body, 1);
++ }
++
++ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
++ if !has_attr(cx.sess(), stmt.kind.attrs()) {
++ return;
++ }
++ match stmt.kind {
++ hir::StmtKind::Local(ref local) => {
++ println!("local variable of type {}", cx.tables.node_type(local.hir_id));
++ println!("pattern:");
++ print_pat(cx, &local.pat, 0);
++ if let Some(ref e) = local.init {
++ println!("init expression:");
++ print_expr(cx, e, 0);
++ }
++ },
++ hir::StmtKind::Item(_) => println!("item decl"),
++ hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0),
++ }
++ }
++ // fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
++ // hir::ForeignItem) {
++ // if !has_attr(&item.attrs) {
++ // return;
++ // }
++ // }
++ //
++}
++
++fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
++ get_attr(sess, attrs, "dump").count() > 0
++}
++
++#[allow(clippy::similar_names)]
++#[allow(clippy::too_many_lines)]
++fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) {
++ let ind = " ".repeat(indent);
++ println!("{}+", ind);
++ println!("{}ty: {}", ind, cx.tables.expr_ty(expr));
++ println!("{}adjustments: {:?}", ind, cx.tables.adjustments().get(expr.hir_id));
++ match expr.kind {
++ hir::ExprKind::Box(ref e) => {
++ println!("{}Box", ind);
++ print_expr(cx, e, indent + 1);
++ },
++ hir::ExprKind::Array(v) => {
++ println!("{}Array", ind);
++ for e in v {
++ print_expr(cx, e, indent + 1);
++ }
++ },
++ hir::ExprKind::Call(ref func, args) => {
++ println!("{}Call", ind);
++ println!("{}function:", ind);
++ print_expr(cx, func, indent + 1);
++ println!("{}arguments:", ind);
++ for arg in args {
++ print_expr(cx, arg, indent + 1);
++ }
++ },
++ hir::ExprKind::MethodCall(ref path, _, args) => {
++ println!("{}MethodCall", ind);
++ println!("{}method name: {}", ind, path.ident.name);
++ for arg in args {
++ print_expr(cx, arg, indent + 1);
++ }
++ },
++ hir::ExprKind::Tup(v) => {
++ println!("{}Tup", ind);
++ for e in v {
++ print_expr(cx, e, indent + 1);
++ }
++ },
++ hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
++ println!("{}Binary", ind);
++ println!("{}op: {:?}", ind, op.node);
++ println!("{}lhs:", ind);
++ print_expr(cx, lhs, indent + 1);
++ println!("{}rhs:", ind);
++ print_expr(cx, rhs, indent + 1);
++ },
++ hir::ExprKind::Unary(op, ref inner) => {
++ println!("{}Unary", ind);
++ println!("{}op: {:?}", ind, op);
++ print_expr(cx, inner, indent + 1);
++ },
++ hir::ExprKind::Lit(ref lit) => {
++ println!("{}Lit", ind);
++ println!("{}{:?}", ind, lit);
++ },
++ hir::ExprKind::Cast(ref e, ref target) => {
++ println!("{}Cast", ind);
++ print_expr(cx, e, indent + 1);
++ println!("{}target type: {:?}", ind, target);
++ },
++ hir::ExprKind::Type(ref e, ref target) => {
++ println!("{}Type", ind);
++ print_expr(cx, e, indent + 1);
++ println!("{}target type: {:?}", ind, target);
++ },
++ hir::ExprKind::Loop(..) => {
++ println!("{}Loop", ind);
++ },
++ hir::ExprKind::Match(ref cond, _, ref source) => {
++ println!("{}Match", ind);
++ println!("{}condition:", ind);
++ print_expr(cx, cond, indent + 1);
++ println!("{}source: {:?}", ind, source);
++ },
++ hir::ExprKind::Closure(ref clause, _, _, _, _) => {
++ println!("{}Closure", ind);
++ println!("{}clause: {:?}", ind, clause);
++ },
++ hir::ExprKind::Yield(ref sub, _) => {
++ println!("{}Yield", ind);
++ print_expr(cx, sub, indent + 1);
++ },
++ hir::ExprKind::Block(_, _) => {
++ println!("{}Block", ind);
++ },
++ hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
++ println!("{}Assign", ind);
++ println!("{}lhs:", ind);
++ print_expr(cx, lhs, indent + 1);
++ println!("{}rhs:", ind);
++ print_expr(cx, rhs, indent + 1);
++ },
++ hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => {
++ println!("{}AssignOp", ind);
++ println!("{}op: {:?}", ind, binop.node);
++ println!("{}lhs:", ind);
++ print_expr(cx, lhs, indent + 1);
++ println!("{}rhs:", ind);
++ print_expr(cx, rhs, indent + 1);
++ },
++ hir::ExprKind::Field(ref e, ident) => {
++ println!("{}Field", ind);
++ println!("{}field name: {}", ind, ident.name);
++ println!("{}struct expr:", ind);
++ print_expr(cx, e, indent + 1);
++ },
++ hir::ExprKind::Index(ref arr, ref idx) => {
++ println!("{}Index", ind);
++ println!("{}array expr:", ind);
++ print_expr(cx, arr, indent + 1);
++ println!("{}index expr:", ind);
++ print_expr(cx, idx, indent + 1);
++ },
++ hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => {
++ println!("{}Resolved Path, {:?}", ind, ty);
++ println!("{}path: {:?}", ind, path);
++ },
++ hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => {
++ println!("{}Relative Path, {:?}", ind, ty);
++ println!("{}seg: {:?}", ind, seg);
++ },
++ hir::ExprKind::AddrOf(kind, ref muta, ref e) => {
++ println!("{}AddrOf", ind);
++ println!("kind: {:?}", kind);
++ println!("mutability: {:?}", muta);
++ print_expr(cx, e, indent + 1);
++ },
++ hir::ExprKind::Break(_, ref e) => {
++ println!("{}Break", ind);
++ if let Some(ref e) = *e {
++ print_expr(cx, e, indent + 1);
++ }
++ },
++ hir::ExprKind::Continue(_) => println!("{}Again", ind),
++ hir::ExprKind::Ret(ref e) => {
++ println!("{}Ret", ind);
++ if let Some(ref e) = *e {
++ print_expr(cx, e, indent + 1);
++ }
++ },
++ hir::ExprKind::LlvmInlineAsm(ref asm) => {
++ let inputs = &asm.inputs_exprs;
++ let outputs = &asm.outputs_exprs;
++ println!("{}LlvmInlineAsm", ind);
++ println!("{}inputs:", ind);
++ for e in inputs.iter() {
++ print_expr(cx, e, indent + 1);
++ }
++ println!("{}outputs:", ind);
++ for e in outputs.iter() {
++ print_expr(cx, e, indent + 1);
++ }
++ },
++ hir::ExprKind::Struct(ref path, fields, ref base) => {
++ println!("{}Struct", ind);
++ println!("{}path: {:?}", ind, path);
++ for field in fields {
++ println!("{}field \"{}\":", ind, field.ident.name);
++ print_expr(cx, &field.expr, indent + 1);
++ }
++ if let Some(ref base) = *base {
++ println!("{}base:", ind);
++ print_expr(cx, base, indent + 1);
++ }
++ },
++ hir::ExprKind::Repeat(ref val, ref anon_const) => {
++ println!("{}Repeat", ind);
++ println!("{}value:", ind);
++ print_expr(cx, val, indent + 1);
++ println!("{}repeat count:", ind);
++ print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
++ },
++ hir::ExprKind::Err => {
++ println!("{}Err", ind);
++ },
++ hir::ExprKind::DropTemps(ref e) => {
++ println!("{}DropTemps", ind);
++ print_expr(cx, e, indent + 1);
++ },
++ }
++}
++
++fn print_item(cx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
++ let did = cx.tcx.hir().local_def_id(item.hir_id);
++ println!("item `{}`", item.ident.name);
++ match item.vis.node {
++ hir::VisibilityKind::Public => println!("public"),
++ hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
++ hir::VisibilityKind::Restricted { ref path, .. } => println!(
++ "visible in module `{}`",
++ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
++ ),
++ hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
++ }
++ match item.kind {
++ hir::ItemKind::ExternCrate(ref _renamed_from) => {
++ let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++ if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(def_id) {
++ let source = cx.tcx.used_crate_source(crate_id);
++ if let Some(ref src) = source.dylib {
++ println!("extern crate dylib source: {:?}", src.0);
++ }
++ if let Some(ref src) = source.rlib {
++ println!("extern crate rlib source: {:?}", src.0);
++ }
++ } else {
++ println!("weird extern crate without a crate id");
++ }
++ },
++ hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind),
++ hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
++ hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
++ hir::ItemKind::Fn(..) => {
++ let item_ty = cx.tcx.type_of(did);
++ println!("function of type {:#?}", item_ty);
++ },
++ hir::ItemKind::Mod(..) => println!("module"),
++ hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi),
++ hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm),
++ hir::ItemKind::TyAlias(..) => {
++ println!("type alias for {:?}", cx.tcx.type_of(did));
++ },
++ hir::ItemKind::OpaqueTy(..) => {
++ println!("existential type with real type {:?}", cx.tcx.type_of(did));
++ },
++ hir::ItemKind::Enum(..) => {
++ println!("enum definition of type {:?}", cx.tcx.type_of(did));
++ },
++ hir::ItemKind::Struct(..) => {
++ println!("struct definition of type {:?}", cx.tcx.type_of(did));
++ },
++ hir::ItemKind::Union(..) => {
++ println!("union definition of type {:?}", cx.tcx.type_of(did));
++ },
++ hir::ItemKind::Trait(..) => {
++ println!("trait decl");
++ if cx.tcx.trait_is_auto(did.to_def_id()) {
++ println!("trait is auto");
++ } else {
++ println!("trait is not auto");
++ }
++ },
++ hir::ItemKind::TraitAlias(..) => {
++ println!("trait alias");
++ },
++ hir::ItemKind::Impl {
++ of_trait: Some(ref _trait_ref),
++ ..
++ } => {
++ println!("trait impl");
++ },
++ hir::ItemKind::Impl { of_trait: None, .. } => {
++ println!("impl");
++ },
++ }
++}
++
++#[allow(clippy::similar_names)]
++#[allow(clippy::too_many_lines)]
++fn print_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, indent: usize) {
++ let ind = " ".repeat(indent);
++ println!("{}+", ind);
++ match pat.kind {
++ hir::PatKind::Wild => println!("{}Wild", ind),
++ hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
++ println!("{}Binding", ind);
++ println!("{}mode: {:?}", ind, mode);
++ println!("{}name: {}", ind, ident.name);
++ if let Some(ref inner) = *inner {
++ println!("{}inner:", ind);
++ print_pat(cx, inner, indent + 1);
++ }
++ },
++ hir::PatKind::Or(fields) => {
++ println!("{}Or", ind);
++ for field in fields {
++ print_pat(cx, field, indent + 1);
++ }
++ },
++ hir::PatKind::Struct(ref path, fields, ignore) => {
++ println!("{}Struct", ind);
++ println!(
++ "{}name: {}",
++ ind,
++ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++ );
++ println!("{}ignore leftover fields: {}", ind, ignore);
++ println!("{}fields:", ind);
++ for field in fields {
++ println!("{} field name: {}", ind, field.ident.name);
++ if field.is_shorthand {
++ println!("{} in shorthand notation", ind);
++ }
++ print_pat(cx, &field.pat, indent + 1);
++ }
++ },
++ hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
++ println!("{}TupleStruct", ind);
++ println!(
++ "{}path: {}",
++ ind,
++ rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++ );
++ if let Some(dot_position) = opt_dots_position {
++ println!("{}dot position: {}", ind, dot_position);
++ }
++ for field in fields {
++ print_pat(cx, field, indent + 1);
++ }
++ },
++ hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => {
++ println!("{}Resolved Path, {:?}", ind, ty);
++ println!("{}path: {:?}", ind, path);
++ },
++ hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => {
++ println!("{}Relative Path, {:?}", ind, ty);
++ println!("{}seg: {:?}", ind, seg);
++ },
++ hir::PatKind::Tuple(pats, opt_dots_position) => {
++ println!("{}Tuple", ind);
++ if let Some(dot_position) = opt_dots_position {
++ println!("{}dot position: {}", ind, dot_position);
++ }
++ for field in pats {
++ print_pat(cx, field, indent + 1);
++ }
++ },
++ hir::PatKind::Box(ref inner) => {
++ println!("{}Box", ind);
++ print_pat(cx, inner, indent + 1);
++ },
++ hir::PatKind::Ref(ref inner, ref muta) => {
++ println!("{}Ref", ind);
++ println!("{}mutability: {:?}", ind, muta);
++ print_pat(cx, inner, indent + 1);
++ },
++ hir::PatKind::Lit(ref e) => {
++ println!("{}Lit", ind);
++ print_expr(cx, e, indent + 1);
++ },
++ hir::PatKind::Range(ref l, ref r, ref range_end) => {
++ println!("{}Range", ind);
++ if let Some(expr) = l {
++ print_expr(cx, expr, indent + 1);
++ }
++ if let Some(expr) = r {
++ print_expr(cx, expr, indent + 1);
++ }
++ match *range_end {
++ hir::RangeEnd::Included => println!("{} end included", ind),
++ hir::RangeEnd::Excluded => println!("{} end excluded", ind),
++ }
++ },
++ hir::PatKind::Slice(first_pats, ref range, last_pats) => {
++ println!("{}Slice [a, b, ..i, y, z]", ind);
++ println!("[a, b]:");
++ for pat in first_pats {
++ print_pat(cx, pat, indent + 1);
++ }
++ println!("i:");
++ if let Some(ref pat) = *range {
++ print_pat(cx, pat, indent + 1);
++ }
++ println!("[y, z]:");
++ for pat in last_pats {
++ print_pat(cx, pat, indent + 1);
++ }
++ },
++ }
++}
++
++fn print_guard(cx: &LateContext<'_, '_>, guard: &hir::Guard<'_>, indent: usize) {
++ let ind = " ".repeat(indent);
++ println!("{}+", ind);
++ match guard {
++ hir::Guard::If(expr) => {
++ println!("{}If", ind);
++ print_expr(cx, expr, indent + 1);
++ },
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::SpanlessEq;
++use crate::utils::{
++ is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
++ span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, Name, NodeId};
++use rustc_ast::visit::FnKind;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::hir_id::CRATE_HIR_ID;
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{Span, Spanned};
++use rustc_span::symbol::SymbolStr;
++
++use std::borrow::{Borrow, Cow};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for various things we like to keep tidy in clippy.
++ ///
++ /// **Why is this bad?** We like to pretend we're an example of tidy code.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:** Wrong ordering of the util::paths constants.
++ pub CLIPPY_LINTS_INTERNAL,
++ internal,
++ "various things that will negatively affect your clippy experience"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Ensures every lint is associated to a `LintPass`.
++ ///
++ /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
++ /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
++ /// know the name of the lint.
++ ///
++ /// **Known problems:** Only checks for lints associated using the
++ /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// declare_lint! { pub LINT_1, ... }
++ /// declare_lint! { pub LINT_2, ... }
++ /// declare_lint! { pub FORGOTTEN_LINT, ... }
++ /// // ...
++ /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
++ /// // missing FORGOTTEN_LINT
++ /// ```
++ pub LINT_WITHOUT_LINT_PASS,
++ internal,
++ "declaring a lint without associating it in a LintPass"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
++ /// variant of the function.
++ ///
++ /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the
++ /// warning/error messages.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// cx.span_lint(LINT_NAME, "message");
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// utils::span_lint(cx, LINT_NAME, "message");
++ /// ```
++ pub COMPILER_LINT_FUNCTIONS,
++ internal,
++ "usage of the lint functions of the compiler instead of the utils::* variant"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use
++ /// the `cx.outer_expn_data()`
++ ///
++ /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// expr.span.ctxt().outer().expn_data()
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// expr.span.ctxt().outer_expn_data()
++ /// ```
++ pub OUTER_EXPN_EXPN_DATA,
++ internal,
++ "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler
++ /// error message by calling `panic`.
++ ///
++ /// **Why is this bad?** ICE in large quantities can damage your teeth
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// 🍦🍦🍦🍦🍦
++ /// ```
++ pub PRODUCE_ICE,
++ internal,
++ "this message should not appear anywhere as we ICE before and don't emit the lint"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for cases of an auto-generated lint without an updated description,
++ /// i.e. `default lint description`.
++ ///
++ /// **Why is this bad?** Indicates that the lint is not finished.
++ ///
++ /// **Known problems:** None
++ ///
++ /// **Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
++ /// ```
++ pub DEFAULT_LINT,
++ internal,
++ "found 'default lint description' in a lint declaration"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Lints `span_lint_and_then` function calls, where the
++ /// closure argument has only one statement and that statement is a method
++ /// call to `span_suggestion`, `span_help`, `span_note` (using the same
++ /// span), `help` or `note`.
++ ///
++ /// These usages of `span_lint_and_then` should be replaced with one of the
++ /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
++ /// `span_lint_and_note`.
++ ///
++ /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more
++ /// convenient, readable and less error prone.
++ ///
++ /// **Known problems:** None
++ ///
++ /// *Example:**
++ /// Bad:
++ /// ```rust,ignore
++ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++ /// diag.span_suggestion(
++ /// expr.span,
++ /// help_msg,
++ /// sugg.to_string(),
++ /// Applicability::MachineApplicable,
++ /// );
++ /// });
++ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++ /// diag.span_help(expr.span, help_msg);
++ /// });
++ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++ /// diag.help(help_msg);
++ /// });
++ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++ /// diag.span_note(expr.span, note_msg);
++ /// });
++ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++ /// diag.note(note_msg);
++ /// });
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// span_lint_and_sugg(
++ /// cx,
++ /// TEST_LINT,
++ /// expr.span,
++ /// lint_msg,
++ /// help_msg,
++ /// sugg.to_string(),
++ /// Applicability::MachineApplicable,
++ /// );
++ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
++ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
++ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
++ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
++ /// ```
++ pub COLLAPSIBLE_SPAN_LINT_CALLS,
++ internal,
++ "found collapsible `span_lint_and_then` calls"
++}
++
++declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
++
++impl EarlyLintPass for ClippyLintsInternal {
++ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
++ if let Some(utils) = krate
++ .module
++ .items
++ .iter()
++ .find(|item| item.ident.name.as_str() == "utils")
++ {
++ if let ItemKind::Mod(ref utils_mod) = utils.kind {
++ if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
++ if let ItemKind::Mod(ref paths_mod) = paths.kind {
++ let mut last_name: Option<SymbolStr> = None;
++ for item in &*paths_mod.items {
++ let name = item.ident.as_str();
++ if let Some(ref last_name) = last_name {
++ if **last_name > *name {
++ span_lint(
++ cx,
++ CLIPPY_LINTS_INTERNAL,
++ item.span,
++ "this constant should be before the previous constant due to lexical \
++ ordering",
++ );
++ }
++ }
++ last_name = Some(name);
++ }
++ }
++ }
++ }
++ }
++ }
++}
++
++#[derive(Clone, Debug, Default)]
++pub struct LintWithoutLintPass {
++ declared_lints: FxHashMap<Name, Span>,
++ registered_lints: FxHashSet<Name>,
++}
++
++impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
++ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++ if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) {
++ return;
++ }
++
++ if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind {
++ if is_lint_ref_type(cx, ty) {
++ let expr = &cx.tcx.hir().body(body_id).value;
++ if_chain! {
++ if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind;
++ if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind;
++ let field = fields
++ .iter()
++ .find(|f| f.ident.as_str() == "desc")
++ .expect("lints must have a description field");
++ if let ExprKind::Lit(Spanned {
++ node: LitKind::Str(ref sym, _),
++ ..
++ }) = field.expr.kind;
++ if sym.as_str() == "default lint description";
++
++ then {
++ span_lint(
++ cx,
++ DEFAULT_LINT,
++ item.span,
++ &format!("the lint `{}` has the default lint description", item.ident.name),
++ );
++ }
++ }
++ self.declared_lints.insert(item.ident.name, item.span);
++ }
++ } else if is_expn_of(item.span, "impl_lint_pass").is_some()
++ || is_expn_of(item.span, "declare_lint_pass").is_some()
++ {
++ if let hir::ItemKind::Impl {
++ of_trait: None,
++ items: ref impl_item_refs,
++ ..
++ } = item.kind
++ {
++ let mut collector = LintCollector {
++ output: &mut self.registered_lints,
++ cx,
++ };
++ let body_id = cx.tcx.hir().body_owned_by(
++ impl_item_refs
++ .iter()
++ .find(|iiref| iiref.ident.as_str() == "get_lints")
++ .expect("LintPass needs to implement get_lints")
++ .id
++ .hir_id,
++ );
++ collector.visit_expr(&cx.tcx.hir().body(body_id).value);
++ }
++ }
++ }
++
++ fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) {
++ if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
++ return;
++ }
++
++ for (lint_name, &lint_span) in &self.declared_lints {
++ // When using the `declare_tool_lint!` macro, the original `lint_span`'s
++ // file points to "<rustc macros>".
++ // `compiletest-rs` thinks that's an error in a different file and
++ // just ignores it. This causes the test in compile-fail/lint_pass
++ // not able to capture the error.
++ // Therefore, we need to climb the macro expansion tree and find the
++ // actual span that invoked `declare_tool_lint!`:
++ let lint_span = lint_span.ctxt().outer_expn_data().call_site;
++
++ if !self.registered_lints.contains(lint_name) {
++ span_lint(
++ cx,
++ LINT_WITHOUT_LINT_PASS,
++ lint_span,
++ &format!("the lint `{}` is not added to any `LintPass`", lint_name),
++ );
++ }
++ }
++ }
++}
++
++fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool {
++ if let TyKind::Rptr(
++ _,
++ MutTy {
++ ty: ref inner,
++ mutbl: Mutability::Not,
++ },
++ ) = ty.kind
++ {
++ if let TyKind::Path(ref path) = inner.kind {
++ if let Res::Def(DefKind::Struct, def_id) = cx.tables.qpath_res(path, inner.hir_id) {
++ return match_def_path(cx, def_id, &paths::LINT);
++ }
++ }
++ }
++
++ false
++}
++
++struct LintCollector<'a, 'tcx> {
++ output: &'a mut FxHashSet<Name>,
++ cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
++ type Map = Map<'tcx>;
++
++ fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
++ if path.segments.len() == 1 {
++ self.output.insert(path.segments[0].ident.name);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::All(self.cx.tcx.hir())
++ }
++}
++
++#[derive(Clone, Default)]
++pub struct CompilerLintFunctions {
++ map: FxHashMap<&'static str, &'static str>,
++}
++
++impl CompilerLintFunctions {
++ #[must_use]
++ pub fn new() -> Self {
++ let mut map = FxHashMap::default();
++ map.insert("span_lint", "utils::span_lint");
++ map.insert("struct_span_lint", "utils::span_lint");
++ map.insert("lint", "utils::span_lint");
++ map.insert("span_lint_note", "utils::span_lint_and_note");
++ map.insert("span_lint_help", "utils::span_lint_and_help");
++ Self { map }
++ }
++}
++
++impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
++ return;
++ }
++
++ if_chain! {
++ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++ let fn_name = path.ident;
++ if let Some(sugg) = self.map.get(&*fn_name.as_str());
++ let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
++ if match_type(cx, ty, &paths::EARLY_CONTEXT)
++ || match_type(cx, ty, &paths::LATE_CONTEXT);
++ then {
++ span_lint_and_help(
++ cx,
++ COMPILER_LINT_FUNCTIONS,
++ path.ident.span,
++ "usage of a compiler lint function",
++ None,
++ &format!("please use the Clippy variant of this function: `{}`", sugg),
++ );
++ }
++ }
++ }
++}
++
++declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
++ return;
++ }
++
++ let (method_names, arg_lists, spans) = method_calls(expr, 2);
++ let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
++ let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
++ if_chain! {
++ if let ["expn_data", "outer_expn"] = method_names.as_slice();
++ let args = arg_lists[1];
++ if args.len() == 1;
++ let self_arg = &args[0];
++ let self_ty = walk_ptrs_ty(cx.tables.expr_ty(self_arg));
++ if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
++ then {
++ span_lint_and_sugg(
++ cx,
++ OUTER_EXPN_EXPN_DATA,
++ spans[1].with_hi(expr.span.hi()),
++ "usage of `outer_expn().expn_data()`",
++ "try",
++ "outer_expn_data()".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ }
++}
++
++declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
++
++impl EarlyLintPass for ProduceIce {
++ fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
++ if is_trigger_fn(fn_kind) {
++ panic!("Would you like some help with that?");
++ }
++ }
++}
++
++fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
++ match fn_kind {
++ FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
++ FnKind::Closure(..) => false,
++ }
++}
++
++declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CollapsibleCalls {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++ if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
++ return;
++ }
++
++ if_chain! {
++ if let ExprKind::Call(ref func, ref and_then_args) = expr.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &["span_lint_and_then"]);
++ if and_then_args.len() == 5;
++ if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
++ let body = cx.tcx.hir().body(*body_id);
++ if let ExprKind::Block(block, _) = &body.value.kind;
++ let stmts = &block.stmts;
++ if stmts.len() == 1 && block.expr.is_none();
++ if let StmtKind::Semi(only_expr) = &stmts[0].kind;
++ if let ExprKind::MethodCall(ref ps, _, ref span_call_args) = &only_expr.kind;
++ let and_then_snippets = get_and_then_snippets(cx, and_then_args);
++ let mut sle = SpanlessEq::new(cx).ignore_fn();
++ then {
++ match &*ps.ident.as_str() {
++ "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++ suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
++ },
++ "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++ let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++ suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
++ },
++ "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++ let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++ suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
++ },
++ "help" => {
++ let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++ suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
++ }
++ "note" => {
++ let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++ suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
++ }
++ _ => (),
++ }
++ }
++ }
++ }
++}
++
++struct AndThenSnippets<'a> {
++ cx: Cow<'a, str>,
++ lint: Cow<'a, str>,
++ span: Cow<'a, str>,
++ msg: Cow<'a, str>,
++}
++
++fn get_and_then_snippets<'a, 'hir>(
++ cx: &LateContext<'_, '_>,
++ and_then_snippets: &'hir [Expr<'hir>],
++) -> AndThenSnippets<'a> {
++ let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
++ let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
++ let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
++ let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
++
++ AndThenSnippets {
++ cx: cx_snippet,
++ lint: lint_snippet,
++ span: span_snippet,
++ msg: msg_snippet,
++ }
++}
++
++struct SpanSuggestionSnippets<'a> {
++ help: Cow<'a, str>,
++ sugg: Cow<'a, str>,
++ applicability: Cow<'a, str>,
++}
++
++fn span_suggestion_snippets<'a, 'hir>(
++ cx: &LateContext<'_, '_>,
++ span_call_args: &'hir [Expr<'hir>],
++) -> SpanSuggestionSnippets<'a> {
++ let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++ let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
++ let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
++
++ SpanSuggestionSnippets {
++ help: help_snippet,
++ sugg: sugg_snippet,
++ applicability: applicability_snippet,
++ }
++}
++
++fn suggest_suggestion(
++ cx: &LateContext<'_, '_>,
++ expr: &Expr<'_>,
++ and_then_snippets: &AndThenSnippets<'_>,
++ span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
++) {
++ span_lint_and_sugg(
++ cx,
++ COLLAPSIBLE_SPAN_LINT_CALLS,
++ expr.span,
++ "this call is collapsible",
++ "collapse into",
++ format!(
++ "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
++ and_then_snippets.cx,
++ and_then_snippets.lint,
++ and_then_snippets.span,
++ and_then_snippets.msg,
++ span_suggestion_snippets.help,
++ span_suggestion_snippets.sugg,
++ span_suggestion_snippets.applicability
++ ),
++ Applicability::MachineApplicable,
++ );
++}
++
++fn suggest_help(
++ cx: &LateContext<'_, '_>,
++ expr: &Expr<'_>,
++ and_then_snippets: &AndThenSnippets<'_>,
++ help: &str,
++ with_span: bool,
++) {
++ let option_span = if with_span {
++ format!("Some({})", and_then_snippets.span)
++ } else {
++ "None".to_string()
++ };
++
++ span_lint_and_sugg(
++ cx,
++ COLLAPSIBLE_SPAN_LINT_CALLS,
++ expr.span,
++ "this call is collapsible",
++ "collapse into",
++ format!(
++ "span_lint_and_help({}, {}, {}, {}, {}, {})",
++ and_then_snippets.cx,
++ and_then_snippets.lint,
++ and_then_snippets.span,
++ and_then_snippets.msg,
++ &option_span,
++ help
++ ),
++ Applicability::MachineApplicable,
++ );
++}
++
++fn suggest_note(
++ cx: &LateContext<'_, '_>,
++ expr: &Expr<'_>,
++ and_then_snippets: &AndThenSnippets<'_>,
++ note: &str,
++ with_span: bool,
++) {
++ let note_span = if with_span {
++ format!("Some({})", and_then_snippets.span)
++ } else {
++ "None".to_string()
++ };
++
++ span_lint_and_sugg(
++ cx,
++ COLLAPSIBLE_SPAN_LINT_CALLS,
++ expr.span,
++ "this call is collspible",
++ "collapse into",
++ format!(
++ "span_lint_and_note({}, {}, {}, {}, {}, {})",
++ and_then_snippets.cx,
++ and_then_snippets.lint,
++ and_then_snippets.span,
++ and_then_snippets.msg,
++ note_span,
++ note
++ ),
++ Applicability::MachineApplicable,
++ );
++}
--- /dev/null
--- /dev/null
++#[macro_use]
++pub mod sym;
++
++pub mod attrs;
++pub mod author;
++pub mod camel_case;
++pub mod comparisons;
++pub mod conf;
++pub mod constants;
++mod diagnostics;
++pub mod higher;
++mod hir_utils;
++pub mod inspector;
++pub mod internal_lints;
++pub mod numeric_literal;
++pub mod paths;
++pub mod ptr;
++pub mod sugg;
++pub mod usage;
++pub use self::attrs::*;
++pub use self::diagnostics::*;
++pub use self::hir_utils::{SpanlessEq, SpanlessHash};
++
++use std::borrow::Cow;
++use std::mem;
++
++use if_chain::if_chain;
++use rustc_ast::ast::{self, Attribute, LitKind};
++use rustc_attr as attr;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::Node;
++use rustc_hir::{
++ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
++ MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
++};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, Level, Lint, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::traits;
++use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable};
++use rustc_span::hygiene::{ExpnKind, MacroKind};
++use rustc_span::source_map::original_sp;
++use rustc_span::symbol::{self, kw, Symbol};
++use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
++use rustc_target::abi::Integer;
++use rustc_trait_selection::traits::predicate_for_trait_def;
++use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
++use rustc_trait_selection::traits::query::normalize::AtExt;
++use smallvec::SmallVec;
++
++use crate::consts::{constant, Constant};
++use crate::reexport::Name;
++
++/// 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()
++}
++
++/// 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(..),
++ ..
++ })
++ | Node::TraitItem(&TraitItem {
++ kind: TraitItemKind::Const(..),
++ ..
++ })
++ | Node::ImplItem(&ImplItem {
++ kind: ImplItemKind::Const(..),
++ ..
++ })
++ | Node::AnonConst(_)
++ | Node::Item(&Item {
++ kind: ItemKind::Static(..),
++ ..
++ }) => true,
++ Node::Item(&Item {
++ kind: ItemKind::Fn(ref sig, ..),
++ ..
++ })
++ | Node::ImplItem(&ImplItem {
++ kind: ImplItemKind::Fn(ref sig, _),
++ ..
++ }) => sig.header.constness == Constness::Const,
++ _ => false,
++ }
++}
++
++/// Returns `true` if this `span` was expanded by any macro.
++#[must_use]
++pub fn in_macro(span: Span) -> bool {
++ if span.from_expansion() {
++ if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
++ false
++ } else {
++ true
++ }
++ } else {
++ false
++ }
++}
++// If the snippet is empty, it's an attribute that was inserted during macro
++// expansion and we want to ignore those, because they could come from external
++// sources that the user has no control over.
++// For some reason these attributes don't have any expansion info on them, so
++// we have to check it this way until there is a better way.
++pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
++ if let Some(snippet) = snippet_opt(cx, span) {
++ if snippet.is_empty() {
++ return false;
++ }
++ }
++ true
++}
++
++/// Checks if given pattern is a wildcard (`_`)
++pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
++ match pat.kind {
++ PatKind::Wild => true,
++ _ => false,
++ }
++}
++
++/// Checks if type is struct, enum or union type with the given def path.
++pub fn match_type(cx: &LateContext<'_, '_>, ty: Ty<'_>, path: &[&str]) -> bool {
++ match ty.kind {
++ ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
++ _ => false,
++ }
++}
++
++/// Checks if the type is equal to a diagnostic item
++pub fn is_type_diagnostic_item(cx: &LateContext<'_, '_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
++ match ty.kind {
++ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
++ _ => false,
++ }
++}
++
++/// Checks if the method call given in `expr` belongs to the given trait.
++pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&str]) -> bool {
++ let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
++ let trt_id = cx.tcx.trait_of_item(def_id);
++ if let Some(trt_id) = trt_id {
++ match_def_path(cx, trt_id, path)
++ } else {
++ false
++ }
++}
++
++/// Checks if an expression references a variable of the given name.
++pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
++ if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
++ if path.segments.len() == 1 && path.segments[0].ident.name == var {
++ return true;
++ }
++ }
++ false
++}
++
++pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
++ match *path {
++ QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
++ QPath::TypeRelative(_, ref seg) => seg,
++ }
++}
++
++pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
++ match *path {
++ QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]),
++ QPath::Resolved(..) => None,
++ QPath::TypeRelative(_, ref seg) => Some(seg),
++ }
++}
++
++/// Matches a `QPath` against a slice of segment string literals.
++///
++/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
++/// `rustc_hir::QPath`.
++///
++/// # Examples
++/// ```rust,ignore
++/// match_qpath(path, &["std", "rt", "begin_unwind"])
++/// ```
++pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
++ match *path {
++ QPath::Resolved(_, ref path) => match_path(path, segments),
++ QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
++ TyKind::Path(ref inner_path) => {
++ !segments.is_empty()
++ && match_qpath(inner_path, &segments[..(segments.len() - 1)])
++ && segment.ident.name.as_str() == segments[segments.len() - 1]
++ },
++ _ => false,
++ },
++ }
++}
++
++/// 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)
++}
++
++/// Matches a `Path` against a slice of segment string literals, e.g.
++///
++/// # Examples
++/// ```rust,ignore
++/// match_path_ast(path, &["std", "rt", "begin_unwind"])
++/// ```
++pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
++ path.segments
++ .iter()
++ .rev()
++ .zip(segments.iter().rev())
++ .all(|(a, b)| a.ident.name.as_str() == *b)
++}
++
++/// Gets the definition associated to a path.
++pub fn path_to_res(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<def::Res> {
++ let crates = cx.tcx.crates();
++ let krate = crates
++ .iter()
++ .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
++ if let Some(krate) = krate {
++ let krate = DefId {
++ krate: *krate,
++ index: CRATE_DEF_INDEX,
++ };
++ let mut items = cx.tcx.item_children(krate);
++ let mut path_it = path.iter().skip(1).peekable();
++
++ loop {
++ let segment = match path_it.next() {
++ Some(segment) => segment,
++ None => return None,
++ };
++
++ let result = SmallVec::<[_; 8]>::new();
++ for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
++ if item.ident.name.as_str() == *segment {
++ if path_it.peek().is_none() {
++ return Some(item.res);
++ }
++
++ items = cx.tcx.item_children(item.res.def_id());
++ break;
++ }
++ }
++ }
++ } else {
++ None
++ }
++}
++
++pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
++ match qpath {
++ hir::QPath::Resolved(_, path) => path.res,
++ hir::QPath::TypeRelative(..) => {
++ if cx.tcx.has_typeck_tables(id.owner.to_def_id()) {
++ cx.tcx
++ .typeck_tables_of(id.owner.to_def_id().expect_local())
++ .qpath_res(qpath, id)
++ } else {
++ Res::Err
++ }
++ },
++ }
++}
++
++/// Convenience function to get the `DefId` of a trait by path.
++/// It could be a trait or trait alias.
++pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<DefId> {
++ let res = match path_to_res(cx, path) {
++ Some(res) => res,
++ None => return None,
++ };
++
++ match res {
++ Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
++ Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path),
++ _ => None,
++ }
++}
++
++/// Checks whether a type implements a trait.
++/// See also `get_trait_def_id`.
++pub fn implements_trait<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ ty: Ty<'tcx>,
++ trait_id: DefId,
++ ty_params: &[GenericArg<'tcx>],
++) -> bool {
++ let ty = cx.tcx.erase_regions(&ty);
++ let obligation = predicate_for_trait_def(
++ cx.tcx,
++ cx.param_env,
++ traits::ObligationCause::dummy(),
++ trait_id,
++ 0,
++ ty,
++ ty_params,
++ );
++ cx.tcx
++ .infer_ctxt()
++ .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
++}
++
++/// 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{ of_trait: trait_ref, .. } = &item.kind;
++ then { return trait_ref.as_ref(); }
++ }
++ None
++}
++
++/// Checks whether this type implements `Drop`.
++pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++ match ty.ty_adt_def() {
++ Some(def) => def.has_dtor(cx.tcx),
++ _ => false,
++ }
++}
++
++/// 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) = ¤t.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()`,
++/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
++/// containing the `Expr`s for
++/// `.bar()` and `.baz()`
++pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
++ let mut current = expr;
++ let mut matched = Vec::with_capacity(methods.len());
++ for method_name in methods.iter().rev() {
++ // method chains are stored last -> first
++ if let ExprKind::MethodCall(ref path, _, ref args) = current.kind {
++ if path.ident.name.as_str() == *method_name {
++ if args.iter().any(|e| e.span.from_expansion()) {
++ return None;
++ }
++ matched.push(&**args); // build up `matched` backwards
++ current = &args[0] // go to parent expression
++ } else {
++ return None;
++ }
++ } else {
++ return None;
++ }
++ }
++ // Reverse `matched` so that it is in the same order as `methods`.
++ matched.reverse();
++ Some(matched)
++}
++
++/// Returns `true` if the provided `def_id` is an entrypoint to a program.
++pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
++ cx.tcx
++ .entry_fn(LOCAL_CRATE)
++ .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
++}
++
++/// Gets the name of the item the expression is in, if available.
++pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<Name> {
++ let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
++ match cx.tcx.hir().find(parent_id) {
++ Some(
++ Node::Item(Item { ident, .. })
++ | Node::TraitItem(TraitItem { ident, .. })
++ | Node::ImplItem(ImplItem { ident, .. }),
++ ) => Some(ident.name),
++ _ => None,
++ }
++}
++
++/// Gets the name of a `Pat`, if any.
++pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
++ match pat.kind {
++ PatKind::Binding(.., ref spname, _) => Some(spname.name),
++ PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
++ PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
++ _ => None,
++ }
++}
++
++struct ContainsName {
++ name: Name,
++ result: bool,
++}
++
++impl<'tcx> Visitor<'tcx> for ContainsName {
++ type Map = Map<'tcx>;
++
++ fn visit_name(&mut self, _: Span, name: Name) {
++ 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: Name, expr: &Expr<'_>) -> bool {
++ let mut cn = ContainsName { name, result: false };
++ cn.visit_expr(expr);
++ cn.result
++}
++
++/// Converts a span to a code snippet if available, otherwise use default.
++///
++/// This is useful if you want to provide suggestions for your lint or more generally, if you want
++/// to convert a given `Span` to a `str`.
++///
++/// # Example
++/// ```rust,ignore
++/// snippet(cx, expr.span, "..")
++/// ```
++pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
++ snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
++}
++
++/// Same as `snippet`, but it adapts the applicability level by following rules:
++///
++/// - Applicability level `Unspecified` will never be changed.
++/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
++/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
++/// `HasPlaceholders`
++pub fn snippet_with_applicability<'a, T: LintContext>(
++ cx: &T,
++ span: Span,
++ default: &'a str,
++ applicability: &mut Applicability,
++) -> Cow<'a, str> {
++ if *applicability != Applicability::Unspecified && span.from_expansion() {
++ *applicability = Applicability::MaybeIncorrect;
++ }
++ snippet_opt(cx, span).map_or_else(
++ || {
++ if *applicability == Applicability::MachineApplicable {
++ *applicability = Applicability::HasPlaceholders;
++ }
++ Cow::Borrowed(default)
++ },
++ From::from,
++ )
++}
++
++/// Same as `snippet`, but should only be used when it's clear that the input span is
++/// not a macro argument.
++pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
++ snippet(cx, span.source_callsite(), default)
++}
++
++/// Converts a span to a code snippet. Returns `None` if not available.
++pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
++ cx.sess().source_map().span_to_snippet(span).ok()
++}
++
++/// Converts a span (from a block) to a code snippet if available, otherwise use default.
++///
++/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
++/// things which need to be printed as such.
++///
++/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
++/// resulting snippet of the given span.
++///
++/// # Example
++///
++/// ```rust,ignore
++/// snippet_block(cx, block.span, "..", None)
++/// // where, `block` is the block of the if expr
++/// if x {
++/// y;
++/// }
++/// // will return the snippet
++/// {
++/// y;
++/// }
++/// ```
++///
++/// ```rust,ignore
++/// snippet_block(cx, block.span, "..", Some(if_expr.span))
++/// // where, `block` is the block of the if expr
++/// if x {
++/// y;
++/// }
++/// // will return the snippet
++/// {
++/// y;
++/// } // aligned with `if`
++/// ```
++/// Note that the first line of the snippet always has 0 indentation.
++pub fn snippet_block<'a, T: LintContext>(
++ cx: &T,
++ span: Span,
++ default: &'a str,
++ indent_relative_to: Option<Span>,
++) -> Cow<'a, str> {
++ let snip = snippet(cx, span, default);
++ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
++ trim_multiline(snip, true, indent)
++}
++
++/// Same as `snippet_block`, but adapts the applicability level by the rules of
++/// `snippet_with_applicabiliy`.
++pub fn snippet_block_with_applicability<'a, T: LintContext>(
++ cx: &T,
++ span: Span,
++ default: &'a str,
++ indent_relative_to: Option<Span>,
++ applicability: &mut Applicability,
++) -> Cow<'a, str> {
++ let snip = snippet_with_applicability(cx, span, default, applicability);
++ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
++ trim_multiline(snip, true, indent)
++}
++
++/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
++/// line.
++///
++/// ```rust,ignore
++/// let x = ();
++/// // ^^
++/// // will be converted to
++/// let x = ();
++/// // ^^^^^^^^^^
++/// ```
++pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
++ if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
++ span.with_lo(first_char_pos)
++ } else {
++ span
++ }
++}
++
++fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
++ let line_span = line_span(cx, span);
++ if let Some(snip) = snippet_opt(cx, line_span) {
++ snip.find(|c: char| !c.is_whitespace())
++ .map(|pos| line_span.lo() + BytePos::from_usize(pos))
++ } else {
++ None
++ }
++}
++
++/// Returns the indentation of the line of a span
++///
++/// ```rust,ignore
++/// let x = ();
++/// // ^^ -- will return 0
++/// let x = ();
++/// // ^^ -- will return 4
++/// ```
++pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
++ if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
++ snip.find(|c: char| !c.is_whitespace())
++ } else {
++ None
++ }
++}
++
++/// 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())
++}
++
++/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
++/// Also takes an `Option<String>` which can be put inside the braces.
++pub fn expr_block<'a, T: LintContext>(
++ cx: &T,
++ expr: &Expr<'_>,
++ option: Option<String>,
++ default: &'a str,
++ indent_relative_to: Option<Span>,
++) -> Cow<'a, str> {
++ let code = snippet_block(cx, expr.span, default, indent_relative_to);
++ let string = option.unwrap_or_default();
++ if expr.span.from_expansion() {
++ Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
++ } else if let ExprKind::Block(_, _) = expr.kind {
++ Cow::Owned(format!("{}{}", code, string))
++ } else if string.is_empty() {
++ Cow::Owned(format!("{{ {} }}", code))
++ } else {
++ Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
++ }
++}
++
++/// Trim indentation from a multiline string with possibility of ignoring the
++/// first line.
++fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
++ let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
++ let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
++ trim_multiline_inner(s_tab, ignore_first, indent, ' ')
++}
++
++fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>, ch: char) -> Cow<'_, str> {
++ let mut x = s
++ .lines()
++ .skip(ignore_first as usize)
++ .filter_map(|l| {
++ if l.is_empty() {
++ None
++ } else {
++ // ignore empty lines
++ Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
++ }
++ })
++ .min()
++ .unwrap_or(0);
++ if let Some(indent) = indent {
++ x = x.saturating_sub(indent);
++ }
++ if x > 0 {
++ Cow::Owned(
++ s.lines()
++ .enumerate()
++ .map(|(i, l)| {
++ if (ignore_first && i == 0) || l.is_empty() {
++ l
++ } else {
++ l.split_at(x).1
++ }
++ })
++ .collect::<Vec<_>>()
++ .join("\n"),
++ )
++ } else {
++ s
++ }
++}
++
++/// Gets the parent expression, if any –- this is useful to constrain a lint.
++pub fn get_parent_expr<'c>(cx: &'c LateContext<'_, '_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> {
++ let map = &cx.tcx.hir();
++ let hir_id = e.hir_id;
++ let parent_id = map.get_parent_node(hir_id);
++ if hir_id == parent_id {
++ return None;
++ }
++ map.find(parent_id).and_then(|node| {
++ if let Node::Expr(parent) = node {
++ Some(parent)
++ } else {
++ None
++ }
++ })
++}
++
++pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, '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));
++ if let Some(node) = enclosing_node {
++ match node {
++ Node::Block(block) => Some(block),
++ Node::Item(&Item {
++ kind: ItemKind::Fn(_, _, eid),
++ ..
++ })
++ | Node::ImplItem(&ImplItem {
++ kind: ImplItemKind::Fn(_, eid),
++ ..
++ }) => match cx.tcx.hir().body(eid).value.kind {
++ ExprKind::Block(ref block, _) => Some(block),
++ _ => None,
++ },
++ _ => None,
++ }
++ } else {
++ None
++ }
++}
++
++/// Returns the base type for HIR references and pointers.
++pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
++ match ty.kind {
++ TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
++ _ => ty,
++ }
++}
++
++/// Returns the base type for references and raw pointers.
++pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
++ match ty.kind {
++ ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
++ _ => ty,
++ }
++}
++
++/// Returns the base type for references and raw pointers, and count reference
++/// depth.
++pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
++ fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
++ match ty.kind {
++ ty::Ref(_, ty, _) => inner(ty, depth + 1),
++ _ => (ty, depth),
++ }
++ }
++ inner(ty, 0)
++}
++
++/// 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.body_tables(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.tables.adjustments().get(e.hir_id).is_some()
++}
++
++/// Returns the pre-expansion span if is this comes from an expansion of the
++/// macro `name`.
++/// See also `is_direct_expn_of`.
++#[must_use]
++pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
++ loop {
++ if span.from_expansion() {
++ let data = span.ctxt().outer_expn_data();
++ let new_span = data.call_site;
++
++ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
++ if mac_name.as_str() == name {
++ return Some(new_span);
++ }
++ }
++
++ span = new_span;
++ } else {
++ return None;
++ }
++ }
++}
++
++/// Returns the pre-expansion span if the span directly comes from an expansion
++/// of the macro `name`.
++/// The difference with `is_expn_of` is that in
++/// ```rust,ignore
++/// foo!(bar!(42));
++/// ```
++/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
++/// `bar!` by
++/// `is_direct_expn_of`.
++#[must_use]
++pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
++ if span.from_expansion() {
++ let data = span.ctxt().outer_expn_data();
++ let new_span = data.call_site;
++
++ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
++ if mac_name.as_str() == name {
++ return Some(new_span);
++ }
++ }
++ }
++
++ None
++}
++
++/// Convenience function to get the return type of a function.
++pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, '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 two types are the same.
++///
++/// This discards any lifetime annotations, too.
++//
++// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` ==
++// `for <'b> Foo<'b>`, but not for type parameters).
++pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
++ let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a));
++ let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b));
++ cx.tcx
++ .infer_ctxt()
++ .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
++}
++
++/// Returns `true` if the given type is an `unsafe` function.
++pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++ match ty.kind {
++ ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
++ _ => false,
++ }
++}
++
++pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++ ty.is_copy_modulo_regions(cx.tcx, cx.param_env, DUMMY_SP)
++}
++
++/// Checks if an expression is constructing a tuple-like enum variant or struct
++pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ if let ExprKind::Call(ref fun, _) = expr.kind {
++ if let ExprKind::Path(ref qp) = fun.kind {
++ let res = cx.tables.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.
++pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
++ fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
++ matches!(
++ cx.tables.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::Binding(..) | PatKind::Wild => false,
++ PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
++ PatKind::Lit(..) | PatKind::Range(..) => true,
++ PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
++ PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
++ PatKind::Struct(ref qpath, ref fields, _) => {
++ if is_enum_variant(cx, qpath, pat.hir_id) {
++ true
++ } else {
++ are_refutable(cx, fields.iter().map(|field| &*field.pat))
++ }
++ },
++ PatKind::TupleStruct(ref qpath, ref pats, _) => {
++ if is_enum_variant(cx, qpath, pat.hir_id) {
++ true
++ } else {
++ are_refutable(cx, pats.iter().map(|pat| &**pat))
++ }
++ },
++ PatKind::Slice(ref head, ref middle, ref tail) => {
++ are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
++ },
++ }
++}
++
++/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
++/// implementations have.
++pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
++ attr::contains_name(attrs, sym!(automatically_derived))
++}
++
++/// Remove blocks around an expression.
++///
++/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
++/// themselves.
++pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
++ while let ExprKind::Block(ref block, ..) = expr.kind {
++ match (block.stmts.is_empty(), block.expr.as_ref()) {
++ (true, Some(e)) => expr = e,
++ _ => break,
++ }
++ }
++ expr
++}
++
++pub fn is_self(slf: &Param<'_>) -> bool {
++ if let PatKind::Binding(.., name, _) = slf.pat.kind {
++ name.name == kw::SelfLower
++ } else {
++ false
++ }
++}
++
++pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
++ if_chain! {
++ if let TyKind::Path(ref qp) = slf.kind;
++ if let QPath::Resolved(None, ref path) = *qp;
++ 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>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++ fn is_ok(arm: &Arm<'_>) -> bool {
++ if_chain! {
++ if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
++ if match_qpath(path, &paths::RESULT_OK[1..]);
++ if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
++ if let ExprKind::Path(QPath::Resolved(None, ref path)) = arm.body.kind;
++ if let Res::Local(lid) = path.res;
++ if lid == hir_id;
++ then {
++ return true;
++ }
++ }
++ false
++ }
++
++ fn is_err(arm: &Arm<'_>) -> bool {
++ if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
++ match_qpath(path, &paths::RESULT_ERR[1..])
++ } else {
++ false
++ }
++ }
++
++ if let ExprKind::Match(_, ref arms, ref source) = expr.kind {
++ // desugared from a `?` operator
++ if let MatchSource::TryDesugar = *source {
++ return Some(expr);
++ }
++
++ if_chain! {
++ if arms.len() == 2;
++ if arms[0].guard.is_none();
++ if arms[1].guard.is_none();
++ if (is_ok(&arms[0]) && is_err(&arms[1])) ||
++ (is_ok(&arms[1]) && is_err(&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 get_arg_name(pat: &Pat<'_>) -> Option<ast::Name> {
++ match pat.kind {
++ PatKind::Binding(.., ident, None) => Some(ident.name),
++ PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
++ _ => None,
++ }
++}
++
++pub fn int_bits(tcx: TyCtxt<'_>, ity: ast::IntTy) -> u64 {
++ Integer::from_attr(&tcx, attr::IntType::SignedInt(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: ast::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: ast::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: ast::UintTy) -> u128 {
++ let bits = Integer::from_attr(&tcx, attr::IntType::UnsignedInt(ity)).size().bits();
++ let amt = 128 - bits;
++ (u << amt) >> amt
++}
++
++/// Removes block comments from the given `Vec` of lines.
++///
++/// # Examples
++///
++/// ```rust,ignore
++/// without_block_comments(vec!["/*", "foo", "*/"]);
++/// // => vec![]
++///
++/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
++/// // => vec!["bar"]
++/// ```
++pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
++ let mut without = vec![];
++
++ let mut nest_level = 0;
++
++ for line in lines {
++ if line.contains("/*") {
++ nest_level += 1;
++ continue;
++ } else if line.contains("*/") {
++ nest_level -= 1;
++ continue;
++ }
++
++ if nest_level == 0 {
++ without.push(line);
++ }
++ }
++
++ without
++}
++
++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
++}
++
++/// Returns true if ty has `iter` or `iter_mut` methods
++pub fn has_iter_method(cx: &LateContext<'_, '_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> {
++ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
++ // exists and has the desired signature. Unfortunately FnCtxt is not exported
++ // so we can't use its `lookup_method` method.
++ let into_iter_collections: [&[&str]; 13] = [
++ &paths::VEC,
++ &paths::OPTION,
++ &paths::RESULT,
++ &paths::BTREESET,
++ &paths::BTREEMAP,
++ &paths::VEC_DEQUE,
++ &paths::LINKED_LIST,
++ &paths::BINARY_HEAP,
++ &paths::HASHSET,
++ &paths::HASHMAP,
++ &paths::PATH_BUF,
++ &paths::PATH,
++ &paths::RECEIVER,
++ ];
++
++ let ty_to_check = match probably_ref_ty.kind {
++ ty::Ref(_, ty_to_check, _) => ty_to_check,
++ _ => probably_ref_ty,
++ };
++
++ let def_id = match ty_to_check.kind {
++ ty::Array(..) => return Some("array"),
++ ty::Slice(..) => return Some("slice"),
++ ty::Adt(adt, _) => adt.did,
++ _ => return None,
++ };
++
++ for path in &into_iter_collections {
++ if match_def_path(cx, def_id, path) {
++ return Some(*path.last().unwrap());
++ }
++ }
++ None
++}
++
++/// Matches a function call with the given path and returns the arguments.
++///
++/// Usage:
++///
++/// ```rust,ignore
++/// if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
++/// ```
++pub fn match_function_call<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ expr: &'tcx Expr<'_>,
++ path: &[&str],
++) -> Option<&'tcx [Expr<'tcx>]> {
++ if_chain! {
++ if let ExprKind::Call(ref fun, ref args) = expr.kind;
++ if let ExprKind::Path(ref qpath) = fun.kind;
++ if let Some(fun_def_id) = cx.tables.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 `Ty` is normalizable. This function is useful
++/// to avoid crashes on `layout_of`.
++pub fn is_normalizable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ let cause = rustc_middle::traits::ObligationCause::dummy();
++ infcx.at(&cause, param_env).normalize(&ty).is_ok()
++ })
++}
++
++pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, did: DefId, syms: &[&str]) -> bool {
++ // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
++ // accepts only that. We should probably move to Symbols in Clippy as well.
++ let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
++ cx.match_def_path(did, &syms)
++}
++
++/// 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>,
++) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
++ let mut conds = SmallVec::new();
++ let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
++
++ while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) {
++ conds.push(&**cond);
++ if let ExprKind::Block(ref block, _) = then_expr.kind {
++ blocks.push(block);
++ } else {
++ panic!("ExprKind::If node is not an ExprKind::Block");
++ }
++
++ if let Some(ref else_expr) = *else_expr {
++ expr = else_expr;
++ } else {
++ break;
++ }
++ }
++
++ // final `else {..}`
++ if !blocks.is_empty() {
++ if let ExprKind::Block(ref block, _) = expr.kind {
++ blocks.push(&**block);
++ }
++ }
++
++ (conds, blocks)
++}
++
++pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr<'_>, cx: &LateContext<'a, 'b>) -> bool {
++ let map = cx.tcx.hir();
++ let parent_id = map.get_parent_node(expr.hir_id);
++ let parent_node = map.get(parent_id);
++
++ match parent_node {
++ Node::Expr(e) => higher::if_block(&e).is_some(),
++ Node::Arm(e) => higher::if_block(&e.body).is_some(),
++ _ => false,
++ }
++}
++
++// Finds the attribute with the given name, if any
++pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> {
++ attrs
++ .iter()
++ .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name))
++}
++
++// Finds the `#[must_use]` attribute, if any
++pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
++ attr_by_name(attrs, "must_use")
++}
++
++// Returns whether the type has #[must_use] attribute
++pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++ match ty.kind {
++ ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
++ ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
++ ty::Slice(ref ty)
++ | ty::Array(ref ty, _)
++ | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
++ | ty::Ref(_, ref ty, _) => {
++ // for the Array case we don't need to care for the len == 0 case
++ // because we don't want to lint functions returning empty arrays
++ is_must_use_ty(cx, *ty)
++ },
++ ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
++ ty::Opaque(ref def_id, _) => {
++ for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates {
++ if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate {
++ if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
++ return true;
++ }
++ }
++ }
++ false
++ },
++ ty::Dynamic(binder, _) => {
++ for predicate in binder.skip_binder().iter() {
++ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
++ if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
++ return true;
++ }
++ }
++ }
++ false
++ },
++ _ => false,
++ }
++}
++
++// check if expr is calling method or function with #[must_use] attribyte
++pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++ let did = match expr.kind {
++ ExprKind::Call(ref path, _) => if_chain! {
++ if let ExprKind::Path(ref qpath) = path.kind;
++ if let def::Res::Def(_, did) = cx.tables.qpath_res(qpath, path.hir_id);
++ then {
++ Some(did)
++ } else {
++ None
++ }
++ },
++ ExprKind::MethodCall(_, _, _) => cx.tables.type_dependent_def_id(expr.hir_id),
++ _ => None,
++ };
++
++ if let Some(did) = did {
++ must_use_attr(&cx.tcx.get_attrs(did)).is_some()
++ } else {
++ false
++ }
++}
++
++pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
++ krate.item.attrs.iter().any(|attr| {
++ if let ast::AttrKind::Normal(ref attr) = attr.kind {
++ attr.path == symbol::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{ 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::normalize_and_test_predicates(
++ cx.tcx,
++ traits::elaborate_predicates(cx.tcx, predicates)
++ .map(|o| o.predicate)
++ .collect::<Vec<_>>(),
++ )
++}
++
++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, _)
++ )
++ })
++}
++
++#[cfg(test)]
++mod test {
++ use super::{trim_multiline, without_block_comments};
++
++ #[test]
++ fn test_trim_multiline_single_line() {
++ assert_eq!("", trim_multiline("".into(), false, None));
++ assert_eq!("...", trim_multiline("...".into(), false, None));
++ assert_eq!("...", trim_multiline(" ...".into(), false, None));
++ assert_eq!("...", trim_multiline("\t...".into(), false, None));
++ assert_eq!("...", trim_multiline("\t\t...".into(), false, None));
++ }
++
++ #[test]
++ #[rustfmt::skip]
++ fn test_trim_multiline_block() {
++ assert_eq!("\
++ if x {
++ y
++ } else {
++ z
++ }", trim_multiline(" if x {
++ y
++ } else {
++ z
++ }".into(), false, None));
++ assert_eq!("\
++ if x {
++ \ty
++ } else {
++ \tz
++ }", trim_multiline(" if x {
++ \ty
++ } else {
++ \tz
++ }".into(), false, None));
++ }
++
++ #[test]
++ #[rustfmt::skip]
++ fn test_trim_multiline_empty_line() {
++ assert_eq!("\
++ if x {
++ y
++
++ } else {
++ z
++ }", trim_multiline(" if x {
++ y
++
++ } else {
++ z
++ }".into(), false, None));
++ }
++
++ #[test]
++ fn test_without_block_comments_lines_without_block_comments() {
++ let result = without_block_comments(vec!["/*", "", "*/"]);
++ println!("result: {:?}", result);
++ assert!(result.is_empty());
++
++ let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
++ assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
++
++ let result = without_block_comments(vec!["/* rust", "", "*/"]);
++ assert!(result.is_empty());
++
++ let result = without_block_comments(vec!["/* one-line comment */"]);
++ assert!(result.is_empty());
++
++ let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
++ assert!(result.is_empty());
++
++ let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
++ assert!(result.is_empty());
++
++ let result = without_block_comments(vec!["foo", "bar", "baz"]);
++ assert_eq!(result, vec!["foo", "bar", "baz"]);
++ }
++}
--- /dev/null
--- /dev/null
++use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
++
++#[derive(Debug, PartialEq)]
++pub enum Radix {
++ Binary,
++ Octal,
++ Decimal,
++ Hexadecimal,
++}
++
++impl Radix {
++ /// Returns a reasonable digit group size for this radix.
++ #[must_use]
++ fn suggest_grouping(&self) -> usize {
++ match *self {
++ Self::Binary | Self::Hexadecimal => 4,
++ Self::Octal | Self::Decimal => 3,
++ }
++ }
++}
++
++/// A helper method to format numeric literals with digit grouping.
++/// `lit` must be a valid numeric literal without suffix.
++pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
++ NumericLiteral::new(lit, type_suffix, float).format()
++}
++
++#[derive(Debug)]
++pub struct NumericLiteral<'a> {
++ /// Which radix the literal was represented in.
++ pub radix: Radix,
++ /// The radix prefix, if present.
++ pub prefix: Option<&'a str>,
++
++ /// The integer part of the number.
++ pub integer: &'a str,
++ /// The fraction part of the number.
++ pub fraction: Option<&'a str>,
++ /// The character used as exponent seperator (b'e' or b'E') and the exponent part.
++ pub exponent: Option<(char, &'a str)>,
++
++ /// The type suffix, including preceding underscore if present.
++ pub suffix: Option<&'a str>,
++}
++
++impl<'a> NumericLiteral<'a> {
++ pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
++ NumericLiteral::from_lit_kind(src, &lit.kind)
++ }
++
++ pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
++ if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
++ let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
++ let float = if let LitKind::Float(..) = lit_kind { true } else { false };
++ Some(NumericLiteral::new(unsuffixed, suffix, float))
++ } else {
++ None
++ }
++ }
++
++ #[must_use]
++ pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
++ // Determine delimiter for radix prefix, if present, and radix.
++ let radix = if lit.starts_with("0x") {
++ Radix::Hexadecimal
++ } else if lit.starts_with("0b") {
++ Radix::Binary
++ } else if lit.starts_with("0o") {
++ Radix::Octal
++ } else {
++ Radix::Decimal
++ };
++
++ // Grab part of the literal after prefix, if present.
++ let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
++ (None, lit)
++ } else {
++ let (p, s) = lit.split_at(2);
++ (Some(p), s)
++ };
++
++ if suffix.is_some() && sans_prefix.ends_with('_') {
++ // The '_' before the suffix isn't part of the digits
++ sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
++ }
++
++ let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
++
++ Self {
++ radix,
++ prefix,
++ integer,
++ fraction,
++ exponent,
++ suffix,
++ }
++ }
++
++ pub fn is_decimal(&self) -> bool {
++ self.radix == Radix::Decimal
++ }
++
++ pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
++ let mut integer = digits;
++ let mut fraction = None;
++ let mut exponent = None;
++
++ if float {
++ for (i, c) in digits.char_indices() {
++ match c {
++ '.' => {
++ integer = &digits[..i];
++ fraction = Some(&digits[i + 1..]);
++ },
++ 'e' | 'E' => {
++ if integer.len() > i {
++ integer = &digits[..i];
++ } else {
++ fraction = Some(&digits[integer.len() + 1..i]);
++ };
++ exponent = Some((c, &digits[i + 1..]));
++ break;
++ },
++ _ => {},
++ }
++ }
++ }
++
++ (integer, fraction, exponent)
++ }
++
++ /// Returns literal formatted in a sensible way.
++ pub fn format(&self) -> String {
++ let mut output = String::new();
++
++ if let Some(prefix) = self.prefix {
++ output.push_str(prefix);
++ }
++
++ let group_size = self.radix.suggest_grouping();
++
++ Self::group_digits(
++ &mut output,
++ self.integer,
++ group_size,
++ true,
++ self.radix == Radix::Hexadecimal,
++ );
++
++ if let Some(fraction) = self.fraction {
++ output.push('.');
++ Self::group_digits(&mut output, fraction, group_size, false, false);
++ }
++
++ if let Some((separator, exponent)) = self.exponent {
++ output.push(separator);
++ Self::group_digits(&mut output, exponent, group_size, true, false);
++ }
++
++ if let Some(suffix) = self.suffix {
++ output.push('_');
++ output.push_str(suffix);
++ }
++
++ output
++ }
++
++ pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
++ debug_assert!(group_size > 0);
++
++ let mut digits = input.chars().filter(|&c| c != '_');
++
++ let first_group_size;
++
++ if partial_group_first {
++ first_group_size = (digits.clone().count() - 1) % group_size + 1;
++ if pad {
++ for _ in 0..group_size - first_group_size {
++ output.push('0');
++ }
++ }
++ } else {
++ first_group_size = group_size;
++ }
++
++ for _ in 0..first_group_size {
++ if let Some(digit) = digits.next() {
++ output.push(digit);
++ }
++ }
++
++ for (c, i) in digits.zip((0..group_size).cycle()) {
++ if i == 0 {
++ output.push('_');
++ }
++ output.push(c);
++ }
++ }
++}
++
++fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
++ debug_assert!(lit_kind.is_numeric());
++ if let Some(suffix_length) = lit_suffix_length(lit_kind) {
++ let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
++ (unsuffixed, Some(suffix))
++ } else {
++ (src, None)
++ }
++}
++
++fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
++ debug_assert!(lit_kind.is_numeric());
++ let suffix = match lit_kind {
++ LitKind::Int(_, int_lit_kind) => match int_lit_kind {
++ LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
++ LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
++ LitIntType::Unsuffixed => None,
++ },
++ LitKind::Float(_, float_lit_kind) => match float_lit_kind {
++ LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
++ LitFloatType::Unsuffixed => None,
++ },
++ _ => None,
++ };
++
++ suffix.map(str::len)
++}
--- /dev/null
--- /dev/null
++//! This module contains paths to types and functions Clippy needs to know
++//! about.
++//!
++//! Whenever possible, please consider diagnostic items over hardcoded paths.
++//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
++
++pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
++pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
++pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
++pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
++pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
++pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
++pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
++pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
++pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
++pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
++pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
++pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
++pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
++pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
++pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
++pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
++pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
++pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"];
++pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
++pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
++pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
++pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
++pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
++pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
++pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
++pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
++pub const DROP: [&str; 3] = ["core", "mem", "drop"];
++pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
++pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
++pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
++pub const EXIT: [&str; 3] = ["std", "process", "exit"];
++pub const FILE: [&str; 3] = ["std", "fs", "File"];
++pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
++pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
++pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
++pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
++pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
++pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
++pub const HASH: [&str; 2] = ["hash", "Hash"];
++pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
++pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
++pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
++pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
++pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
++pub const INTO: [&str; 3] = ["core", "convert", "Into"];
++pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
++pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
++pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
++pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
++pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
++pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
++pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
++pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
++pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
++pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
++pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
++pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
++pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
++pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
++pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
++pub const OPTION: [&str; 3] = ["core", "option", "Option"];
++pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
++pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
++pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
++pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
++pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
++pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
++pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
++pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
++pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
++pub const PATH: [&str; 3] = ["std", "path", "Path"];
++pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
++pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
++pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
++pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
++pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
++pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
++pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
++pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
++pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
++pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
++pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
++pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
++pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
++pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
++pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
++pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
++pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
++pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
++pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
++pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
++pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
++pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
++pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
++pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
++pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
++pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
++pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
++pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
++pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
++pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
++pub const RESULT: [&str; 3] = ["core", "result", "Result"];
++pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
++pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
++pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
++pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
++pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
++pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
++pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
++pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
++pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
++pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
++pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
++pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
++pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
++pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
++pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
++pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
++pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
++pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
++pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
++pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
++pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
++pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
++pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
++pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
++pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
++pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
++pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
++pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
++pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
++pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
++pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
--- /dev/null
--- /dev/null
++use crate::utils::{get_pat_name, match_var, snippet};
++use rustc_ast::ast::Name;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_span::source_map::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)) {
++ get_binding_name(&body.params[idx]).map_or_else(
++ || Some(vec![]),
++ |name| extract_clone_suggestions(cx, name, replacements, body),
++ )
++ } else {
++ Some(vec![])
++ }
++}
++
++fn extract_clone_suggestions<'a, 'tcx>(
++ cx: &LateContext<'a, 'tcx>,
++ name: Name,
++ replace: &[(&'static str, &'static str)],
++ body: &'tcx Body<'_>,
++) -> Option<Vec<(Span, Cow<'static, str>)>> {
++ let mut visitor = PtrCloneVisitor {
++ cx,
++ name,
++ replace,
++ spans: vec![],
++ abort: false,
++ };
++ visitor.visit_body(body);
++ if visitor.abort {
++ None
++ } else {
++ Some(visitor.spans)
++ }
++}
++
++struct PtrCloneVisitor<'a, 'tcx> {
++ cx: &'a LateContext<'a, 'tcx>,
++ name: Name,
++ 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;
++ }
++ if let ExprKind::MethodCall(ref seg, _, ref args) = expr.kind {
++ if args.len() == 1 && match_var(&args[0], self.name) {
++ 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 {
++ self.spans
++ .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
++ return;
++ }
++ }
++ }
++ return;
++ }
++ walk_expr(self, expr);
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++fn get_binding_name(arg: &Param<'_>) -> Option<Name> {
++ get_pat_name(&arg.pat)
++}
--- /dev/null
--- /dev/null
++//! Contains utility functions to generate suggestions.
++#![deny(clippy::missing_docs_in_private_items)]
++
++use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
++use rustc_ast::util::parser::AssocOp;
++use rustc_ast::{ast, token};
++use rustc_ast_pretty::pprust::token_kind_to_string;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{EarlyContext, LateContext, LintContext};
++use rustc_span::source_map::{CharPos, Span};
++use rustc_span::{BytePos, Pos};
++use std::borrow::Cow;
++use std::convert::TryInto;
++use std::fmt::Display;
++
++/// A helper type to build suggestion correctly handling parenthesis.
++pub enum Sugg<'a> {
++ /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
++ NonParen(Cow<'a, str>),
++ /// An expression that does not fit in other variants.
++ MaybeParen(Cow<'a, str>),
++ /// A binary operator expression, including `as`-casts and explicit type
++ /// coercion.
++ BinOp(AssocOp, Cow<'a, str>),
++}
++
++/// Literal constant `1`, for convenience.
++pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
++
++impl Display for Sugg<'_> {
++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
++ match *self {
++ Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
++ }
++ }
++}
++
++#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
++impl<'a> Sugg<'a> {
++ /// Prepare a suggestion from an expression.
++ pub fn hir_opt(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> Option<Self> {
++ snippet_opt(cx, expr.span).map(|snippet| {
++ let snippet = Cow::Owned(snippet);
++ Self::hir_from_snippet(cx, expr, snippet)
++ })
++ }
++
++ /// Convenience function around `hir_opt` for suggestions with a default
++ /// text.
++ pub fn hir(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
++ Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
++ }
++
++ /// Same as `hir`, but it adapts the applicability level by following rules:
++ ///
++ /// - Applicability level `Unspecified` will never be changed.
++ /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
++ /// - If the default value is used and the applicability level is `MachineApplicable`, change it
++ /// to
++ /// `HasPlaceholders`
++ pub fn hir_with_applicability(
++ cx: &LateContext<'_, '_>,
++ expr: &hir::Expr<'_>,
++ default: &'a str,
++ applicability: &mut Applicability,
++ ) -> Self {
++ if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
++ *applicability = Applicability::MaybeIncorrect;
++ }
++ Self::hir_opt(cx, expr).unwrap_or_else(|| {
++ if *applicability == Applicability::MachineApplicable {
++ *applicability = Applicability::HasPlaceholders;
++ }
++ Sugg::NonParen(Cow::Borrowed(default))
++ })
++ }
++
++ /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
++ pub fn hir_with_macro_callsite(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
++ let snippet = snippet_with_macro_callsite(cx, expr.span, default);
++
++ Self::hir_from_snippet(cx, expr, snippet)
++ }
++
++ /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
++ /// function variants of `Sugg`, since these use different snippet functions.
++ fn hir_from_snippet(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
++ if let Some(range) = higher::range(cx, expr) {
++ let op = match range.limits {
++ ast::RangeLimits::HalfOpen => AssocOp::DotDot,
++ ast::RangeLimits::Closed => AssocOp::DotDotEq,
++ };
++ return Sugg::BinOp(op, snippet);
++ }
++
++ match expr.kind {
++ hir::ExprKind::AddrOf(..)
++ | hir::ExprKind::Box(..)
++ | hir::ExprKind::Closure(..)
++ | hir::ExprKind::Unary(..)
++ | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
++ hir::ExprKind::Continue(..)
++ | hir::ExprKind::Yield(..)
++ | hir::ExprKind::Array(..)
++ | hir::ExprKind::Block(..)
++ | hir::ExprKind::Break(..)
++ | hir::ExprKind::Call(..)
++ | hir::ExprKind::Field(..)
++ | hir::ExprKind::Index(..)
++ | hir::ExprKind::LlvmInlineAsm(..)
++ | hir::ExprKind::Lit(..)
++ | hir::ExprKind::Loop(..)
++ | hir::ExprKind::MethodCall(..)
++ | hir::ExprKind::Path(..)
++ | hir::ExprKind::Repeat(..)
++ | hir::ExprKind::Ret(..)
++ | hir::ExprKind::Struct(..)
++ | hir::ExprKind::Tup(..)
++ | hir::ExprKind::DropTemps(_)
++ | hir::ExprKind::Err => Sugg::NonParen(snippet),
++ hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
++ hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
++ hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
++ hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
++ hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
++ }
++ }
++
++ /// Prepare a suggestion from an expression.
++ pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
++ use rustc_ast::ast::RangeLimits;
++
++ let snippet = snippet(cx, expr.span, default);
++
++ match expr.kind {
++ ast::ExprKind::AddrOf(..)
++ | ast::ExprKind::Box(..)
++ | ast::ExprKind::Closure(..)
++ | ast::ExprKind::If(..)
++ | ast::ExprKind::Let(..)
++ | ast::ExprKind::Unary(..)
++ | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
++ ast::ExprKind::Async(..)
++ | ast::ExprKind::Block(..)
++ | ast::ExprKind::Break(..)
++ | ast::ExprKind::Call(..)
++ | ast::ExprKind::Continue(..)
++ | ast::ExprKind::Yield(..)
++ | ast::ExprKind::Field(..)
++ | ast::ExprKind::ForLoop(..)
++ | ast::ExprKind::Index(..)
++ | ast::ExprKind::LlvmInlineAsm(..)
++ | ast::ExprKind::Lit(..)
++ | ast::ExprKind::Loop(..)
++ | ast::ExprKind::MacCall(..)
++ | ast::ExprKind::MethodCall(..)
++ | ast::ExprKind::Paren(..)
++ | ast::ExprKind::Path(..)
++ | ast::ExprKind::Repeat(..)
++ | ast::ExprKind::Ret(..)
++ | ast::ExprKind::Struct(..)
++ | ast::ExprKind::Try(..)
++ | ast::ExprKind::TryBlock(..)
++ | ast::ExprKind::Tup(..)
++ | ast::ExprKind::Array(..)
++ | ast::ExprKind::While(..)
++ | ast::ExprKind::Await(..)
++ | ast::ExprKind::Err => Sugg::NonParen(snippet),
++ ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
++ ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
++ ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
++ ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
++ ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
++ ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
++ ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
++ }
++ }
++
++ /// Convenience method to create the `<lhs> && <rhs>` suggestion.
++ pub fn and(self, rhs: &Self) -> Sugg<'static> {
++ make_binop(ast::BinOpKind::And, &self, rhs)
++ }
++
++ /// Convenience method to create the `<lhs> & <rhs>` suggestion.
++ pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
++ make_binop(ast::BinOpKind::BitAnd, &self, rhs)
++ }
++
++ /// Convenience method to create the `<lhs> as <rhs>` suggestion.
++ pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
++ make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
++ }
++
++ /// Convenience method to create the `&<expr>` suggestion.
++ pub fn addr(self) -> Sugg<'static> {
++ make_unop("&", self)
++ }
++
++ /// Convenience method to create the `&mut <expr>` suggestion.
++ pub fn mut_addr(self) -> Sugg<'static> {
++ make_unop("&mut ", self)
++ }
++
++ /// Convenience method to create the `*<expr>` suggestion.
++ pub fn deref(self) -> Sugg<'static> {
++ make_unop("*", self)
++ }
++
++ /// Convenience method to create the `&*<expr>` suggestion. Currently this
++ /// is needed because `sugg.deref().addr()` produces an unnecessary set of
++ /// parentheses around the deref.
++ pub fn addr_deref(self) -> Sugg<'static> {
++ make_unop("&*", self)
++ }
++
++ /// Convenience method to create the `&mut *<expr>` suggestion. Currently
++ /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
++ /// set of parentheses around the deref.
++ pub fn mut_addr_deref(self) -> Sugg<'static> {
++ make_unop("&mut *", self)
++ }
++
++ /// Convenience method to transform suggestion into a return call
++ pub fn make_return(self) -> Sugg<'static> {
++ Sugg::NonParen(Cow::Owned(format!("return {}", self)))
++ }
++
++ /// Convenience method to transform suggestion into a block
++ /// where the suggestion is a trailing expression
++ pub fn blockify(self) -> Sugg<'static> {
++ Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
++ }
++
++ /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
++ /// suggestion.
++ #[allow(dead_code)]
++ pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
++ match limit {
++ ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
++ ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
++ }
++ }
++
++ /// Adds parenthesis to any expression that might need them. Suitable to the
++ /// `self` argument of a method call
++ /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
++ pub fn maybe_par(self) -> Self {
++ match self {
++ Sugg::NonParen(..) => self,
++ // `(x)` and `(x).y()` both don't need additional parens.
++ Sugg::MaybeParen(sugg) => {
++ if sugg.starts_with('(') && sugg.ends_with(')') {
++ Sugg::MaybeParen(sugg)
++ } else {
++ Sugg::NonParen(format!("({})", sugg).into())
++ }
++ },
++ Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
++ }
++ }
++}
++
++impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
++ type Output = Sugg<'static>;
++ fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
++ make_binop(ast::BinOpKind::Add, &self, &rhs)
++ }
++}
++
++impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
++ type Output = Sugg<'static>;
++ fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
++ make_binop(ast::BinOpKind::Sub, &self, &rhs)
++ }
++}
++
++impl<'a> std::ops::Not for Sugg<'a> {
++ type Output = Sugg<'static>;
++ fn not(self) -> Sugg<'static> {
++ make_unop("!", self)
++ }
++}
++
++/// Helper type to display either `foo` or `(foo)`.
++struct ParenHelper<T> {
++ /// `true` if parentheses are needed.
++ paren: bool,
++ /// The main thing to display.
++ wrapped: T,
++}
++
++impl<T> ParenHelper<T> {
++ /// Builds a `ParenHelper`.
++ fn new(paren: bool, wrapped: T) -> Self {
++ Self { paren, wrapped }
++ }
++}
++
++impl<T: Display> Display for ParenHelper<T> {
++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
++ if self.paren {
++ write!(f, "({})", self.wrapped)
++ } else {
++ self.wrapped.fmt(f)
++ }
++ }
++}
++
++/// Builds the string for `<op><expr>` adding parenthesis when necessary.
++///
++/// For convenience, the operator is taken as a string because all unary
++/// operators have the same
++/// precedence.
++pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
++ Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
++}
++
++/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
++///
++/// Precedence of shift operator relative to other arithmetic operation is
++/// often confusing so
++/// parenthesis will always be added for a mix of these.
++pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
++ /// Returns `true` if the operator is a shift operator `<<` or `>>`.
++ fn is_shift(op: &AssocOp) -> bool {
++ matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
++ }
++
++ /// Returns `true` if the operator is a arithmetic operator
++ /// (i.e., `+`, `-`, `*`, `/`, `%`).
++ fn is_arith(op: &AssocOp) -> bool {
++ matches!(
++ *op,
++ AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
++ )
++ }
++
++ /// Returns `true` if the operator `op` needs parenthesis with the operator
++ /// `other` in the direction `dir`.
++ fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
++ other.precedence() < op.precedence()
++ || (other.precedence() == op.precedence()
++ && ((op != other && associativity(op) != dir)
++ || (op == other && associativity(op) != Associativity::Both)))
++ || is_shift(op) && is_arith(other)
++ || is_shift(other) && is_arith(op)
++ }
++
++ let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
++ needs_paren(&op, lop, Associativity::Left)
++ } else {
++ false
++ };
++
++ let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
++ needs_paren(&op, rop, Associativity::Right)
++ } else {
++ false
++ };
++
++ let lhs = ParenHelper::new(lhs_paren, lhs);
++ let rhs = ParenHelper::new(rhs_paren, rhs);
++ let sugg = match op {
++ AssocOp::Add
++ | AssocOp::BitAnd
++ | AssocOp::BitOr
++ | AssocOp::BitXor
++ | AssocOp::Divide
++ | AssocOp::Equal
++ | AssocOp::Greater
++ | AssocOp::GreaterEqual
++ | AssocOp::LAnd
++ | AssocOp::LOr
++ | AssocOp::Less
++ | AssocOp::LessEqual
++ | AssocOp::Modulus
++ | AssocOp::Multiply
++ | AssocOp::NotEqual
++ | AssocOp::ShiftLeft
++ | AssocOp::ShiftRight
++ | AssocOp::Subtract => format!(
++ "{} {} {}",
++ lhs,
++ op.to_ast_binop().expect("Those are AST ops").to_string(),
++ rhs
++ ),
++ AssocOp::Assign => format!("{} = {}", lhs, rhs),
++ AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
++ AssocOp::As => format!("{} as {}", lhs, rhs),
++ AssocOp::DotDot => format!("{}..{}", lhs, rhs),
++ AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
++ AssocOp::Colon => format!("{}: {}", lhs, rhs),
++ };
++
++ Sugg::BinOp(op, sugg.into())
++}
++
++/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
++pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
++ make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
++}
++
++#[derive(PartialEq, Eq, Clone, Copy)]
++/// Operator associativity.
++enum Associativity {
++ /// The operator is both left-associative and right-associative.
++ Both,
++ /// The operator is left-associative.
++ Left,
++ /// The operator is not associative.
++ None,
++ /// The operator is right-associative.
++ Right,
++}
++
++/// Returns the associativity/fixity of an operator. The difference with
++/// `AssocOp::fixity` is that an operator can be both left and right associative
++/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
++///
++/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
++/// they are considered
++/// associative.
++#[must_use]
++fn associativity(op: &AssocOp) -> Associativity {
++ use rustc_ast::util::parser::AssocOp::{
++ Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
++ GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
++ };
++
++ match *op {
++ Assign | AssignOp(_) => Associativity::Right,
++ Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
++ Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
++ | Subtract => Associativity::Left,
++ DotDot | DotDotEq => Associativity::None,
++ }
++}
++
++/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
++fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
++ use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
++
++ AssocOp::AssignOp(match op.node {
++ hir::BinOpKind::Add => Plus,
++ hir::BinOpKind::BitAnd => And,
++ hir::BinOpKind::BitOr => Or,
++ hir::BinOpKind::BitXor => Caret,
++ hir::BinOpKind::Div => Slash,
++ hir::BinOpKind::Mul => Star,
++ hir::BinOpKind::Rem => Percent,
++ hir::BinOpKind::Shl => Shl,
++ hir::BinOpKind::Shr => Shr,
++ hir::BinOpKind::Sub => Minus,
++
++ hir::BinOpKind::And
++ | hir::BinOpKind::Eq
++ | hir::BinOpKind::Ge
++ | hir::BinOpKind::Gt
++ | hir::BinOpKind::Le
++ | hir::BinOpKind::Lt
++ | hir::BinOpKind::Ne
++ | hir::BinOpKind::Or => panic!("This operator does not exist"),
++ })
++}
++
++/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
++fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
++ use rustc_ast::ast::BinOpKind::{
++ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
++ };
++ use rustc_ast::token::BinOpToken;
++
++ AssocOp::AssignOp(match op.node {
++ Add => BinOpToken::Plus,
++ BitAnd => BinOpToken::And,
++ BitOr => BinOpToken::Or,
++ BitXor => BinOpToken::Caret,
++ Div => BinOpToken::Slash,
++ Mul => BinOpToken::Star,
++ Rem => BinOpToken::Percent,
++ Shl => BinOpToken::Shl,
++ Shr => BinOpToken::Shr,
++ Sub => BinOpToken::Minus,
++ And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
++ })
++}
++
++/// Returns the indentation before `span` if there are nothing but `[ \t]`
++/// before it on its line.
++fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
++ let lo = cx.sess().source_map().lookup_char_pos(span.lo());
++ if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
++ if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
++ // We can mix char and byte positions here because we only consider `[ \t]`.
++ if lo.col == CharPos(pos) {
++ Some(line[..pos].into())
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++ } else {
++ None
++ }
++}
++
++/// Convenience extension trait for `DiagnosticBuilder`.
++pub trait DiagnosticBuilderExt<'a, T: LintContext> {
++ /// Suggests to add an attribute to an item.
++ ///
++ /// Correctly handles indentation of the attribute and item.
++ ///
++ /// # Example
++ ///
++ /// ```rust,ignore
++ /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
++ /// ```
++ fn suggest_item_with_attr<D: Display + ?Sized>(
++ &mut self,
++ cx: &T,
++ item: Span,
++ msg: &str,
++ attr: &D,
++ applicability: Applicability,
++ );
++
++ /// Suggest to add an item before another.
++ ///
++ /// The item should not be indented (expect for inner indentation).
++ ///
++ /// # Example
++ ///
++ /// ```rust,ignore
++ /// diag.suggest_prepend_item(cx, item,
++ /// "fn foo() {
++ /// bar();
++ /// }");
++ /// ```
++ fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
++
++ /// Suggest to completely remove an item.
++ ///
++ /// This will remove an item and all following whitespace until the next non-whitespace
++ /// character. This should work correctly if item is on the same indentation level as the
++ /// following item.
++ ///
++ /// # Example
++ ///
++ /// ```rust,ignore
++ /// diag.suggest_remove_item(cx, item, "remove this")
++ /// ```
++ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
++}
++
++impl<'a, 'b, 'c, T: LintContext> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> {
++ fn suggest_item_with_attr<D: Display + ?Sized>(
++ &mut self,
++ cx: &T,
++ item: Span,
++ msg: &str,
++ attr: &D,
++ applicability: Applicability,
++ ) {
++ if let Some(indent) = indentation(cx, item) {
++ let span = item.with_hi(item.lo());
++
++ self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
++ }
++ }
++
++ fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
++ if let Some(indent) = indentation(cx, item) {
++ let span = item.with_hi(item.lo());
++
++ let mut first = true;
++ let new_item = new_item
++ .lines()
++ .map(|l| {
++ if first {
++ first = false;
++ format!("{}\n", l)
++ } else {
++ format!("{}{}\n", indent, l)
++ }
++ })
++ .collect::<String>();
++
++ self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
++ }
++ }
++
++ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
++ let mut remove_span = item;
++ let hi = cx.sess().source_map().next_point(remove_span).hi();
++ let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
++
++ if let Some(ref src) = fmpos.sf.src {
++ let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
++
++ if let Some(non_whitespace_offset) = non_whitespace_offset {
++ remove_span = remove_span
++ .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")))
++ }
++ }
++
++ self.span_suggestion(remove_span, msg, String::new(), applicability);
++ }
++}
++
++#[cfg(test)]
++mod test {
++ use super::Sugg;
++ use std::borrow::Cow;
++
++ const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
++
++ #[test]
++ fn make_return_transform_sugg_into_a_return_call() {
++ assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
++ }
++
++ #[test]
++ fn blockify_transforms_sugg_into_a_block() {
++ assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
++ }
++}
--- /dev/null
--- /dev/null
++#[macro_export]
++macro_rules! sym {
++ ($tt:tt) => {
++ rustc_span::symbol::Symbol::intern(stringify!($tt))
++ };
++}
--- /dev/null
--- /dev/null
++use crate::utils::match_var;
++use rustc_ast::ast;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Expr, HirId, Path};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_span::symbol::Ident;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++
++/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
++pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, 'tcx>) -> Option<FxHashSet<HirId>> {
++ let mut delegate = MutVarsDelegate {
++ used_mutably: FxHashSet::default(),
++ skip: false,
++ };
++ let def_id = expr.hir_id.owner.to_def_id();
++ cx.tcx.infer_ctxt().enter(|infcx| {
++ ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(expr);
++ });
++
++ if delegate.skip {
++ return None;
++ }
++ Some(delegate.used_mutably)
++}
++
++pub fn is_potentially_mutated<'a, 'tcx>(
++ variable: &'tcx Path<'_>,
++ expr: &'tcx Expr<'_>,
++ cx: &'a LateContext<'a, 'tcx>,
++) -> bool {
++ if let Res::Local(id) = variable.res {
++ mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
++ } else {
++ true
++ }
++}
++
++struct MutVarsDelegate {
++ used_mutably: FxHashSet<HirId>,
++ skip: bool,
++}
++
++impl<'tcx> MutVarsDelegate {
++ #[allow(clippy::similar_names)]
++ fn update(&mut self, cat: &Place<'tcx>) {
++ match cat.base {
++ PlaceBase::Local(id) => {
++ self.used_mutably.insert(id);
++ },
++ PlaceBase::Upvar(_) => {
++ //FIXME: This causes false negatives. We can't get the `NodeId` from
++ //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
++ //`while`-body, not just the ones in the condition.
++ self.skip = true
++ },
++ _ => {},
++ }
++ }
++}
++
++impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
++ fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {}
++
++ fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) {
++ if let ty::BorrowKind::MutBorrow = bk {
++ self.update(&cmt)
++ }
++ }
++
++ fn mutate(&mut self, cmt: &Place<'tcx>) {
++ self.update(&cmt)
++ }
++}
++
++pub struct UsedVisitor {
++ pub var: ast::Name, // var to look for
++ pub used: bool, // has the var been used otherwise?
++}
++
++impl<'tcx> Visitor<'tcx> for UsedVisitor {
++ type Map = Map<'tcx>;
++
++ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++ if match_var(expr, self.var) {
++ self.used = true;
++ } else {
++ walk_expr(self, expr);
++ }
++ }
++
++ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++ NestedVisitorMap::None
++ }
++}
++
++pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
++ let mut visitor = UsedVisitor {
++ var: ident.name,
++ used: false,
++ };
++ walk_expr(&mut visitor, body);
++ !visitor.used
++}
--- /dev/null
--- /dev/null
++use crate::consts::constant;
++use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++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 usage of `&vec![..]` when using `&[..]` would
++ /// be possible.
++ ///
++ /// **Why is this bad?** This is less efficient.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust,ignore
++ /// foo(&vec![1, 2])
++ /// ```
++ pub USELESS_VEC,
++ perf,
++ "useless `vec!`"
++}
++
++declare_lint_pass!(UselessVec => [USELESS_VEC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessVec {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // search for `&vec![_]` expressions where the adjusted type is `&[_]`
++ if_chain! {
++ if let ty::Ref(_, ty, _) = cx.tables.expr_ty_adjusted(expr).kind;
++ if let ty::Slice(..) = ty.kind;
++ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
++ if let Some(vec_args) = higher::vec_macro(cx, addressee);
++ then {
++ check_vec_macro(cx, &vec_args, expr.span);
++ }
++ }
++
++ // search for `for _ in vec![…]`
++ if_chain! {
++ if let Some((_, arg, _)) = higher::for_loop(expr);
++ if let Some(vec_args) = higher::vec_macro(cx, arg);
++ if is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg)));
++ then {
++ // report the error around the `vec!` not inside `<std macros>:`
++ let span = arg.span
++ .ctxt()
++ .outer_expn_data()
++ .call_site
++ .ctxt()
++ .outer_expn_data()
++ .call_site;
++ check_vec_macro(cx, &vec_args, span);
++ }
++ }
++ }
++}
++
++fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
++ let mut applicability = Applicability::MachineApplicable;
++ let snippet = match *vec_args {
++ higher::VecArgs::Repeat(elem, len) => {
++ if constant(cx, cx.tables, len).is_some() {
++ format!(
++ "&[{}; {}]",
++ snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
++ snippet_with_applicability(cx, len.span, "len", &mut applicability)
++ )
++ } else {
++ return;
++ }
++ },
++ higher::VecArgs::Vec(args) => {
++ if let Some(last) = args.iter().last() {
++ let span = args[0].span.to(last.span);
++
++ format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
++ } else {
++ "&[]".into()
++ }
++ },
++ };
++
++ span_lint_and_sugg(
++ cx,
++ USELESS_VEC,
++ span,
++ "useless use of `vec!`",
++ "you can use a slice directly",
++ snippet,
++ applicability,
++ );
++}
++
++/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
++fn vec_type(ty: Ty<'_>) -> Ty<'_> {
++ if let ty::Adt(_, substs) = ty.kind {
++ substs.type_at(0)
++ } else {
++ panic!("The type of `vec!` is a not a struct?");
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{match_type, paths, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of File::read_to_end and File::read_to_string.
++ ///
++ /// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
++ /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust,no_run
++ /// # use std::io::Read;
++ /// # use std::fs::File;
++ /// let mut f = File::open("foo.txt").unwrap();
++ /// let mut bytes = Vec::new();
++ /// f.read_to_end(&mut bytes).unwrap();
++ /// ```
++ /// Can be written more concisely as
++ /// ```rust,no_run
++ /// # use std::fs;
++ /// let mut bytes = fs::read("foo.txt").unwrap();
++ /// ```
++ pub VERBOSE_FILE_READS,
++ restriction,
++ "use of `File::read_to_end` or `File::read_to_string`"
++}
++
++declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VerboseFileReads {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++ if is_file_read_to_end(cx, expr) {
++ span_lint_and_help(
++ cx,
++ VERBOSE_FILE_READS,
++ expr.span,
++ "use of `File::read_to_end`",
++ None,
++ "consider using `fs::read` instead",
++ );
++ } else if is_file_read_to_string(cx, expr) {
++ span_lint_and_help(
++ cx,
++ VERBOSE_FILE_READS,
++ expr.span,
++ "use of `File::read_to_string`",
++ None,
++ "consider using `fs::read_to_string` instead",
++ )
++ }
++ }
++}
++
++fn is_file_read_to_end<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++ if_chain! {
++ if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind;
++ if method_name.ident.as_str() == "read_to_end";
++ if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
++ let ty = cx.tables.expr_ty(&exprs[0]);
++ if match_type(cx, ty, &paths::FILE);
++ then {
++ return true
++ }
++ }
++ false
++}
++
++fn is_file_read_to_string<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++ if_chain! {
++ if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind;
++ if method_name.ident.as_str() == "read_to_string";
++ if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
++ let ty = cx.tables.expr_ty(&exprs[0]);
++ if match_type(cx, ty, &paths::FILE);
++ then {
++ return true
++ }
++ }
++ false
++}
--- /dev/null
--- /dev/null
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++use if_chain::if_chain;
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.
++ ///
++ /// **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
++ /// it is highly unlikely that you work with any possible version of your dependency,
++ /// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```toml
++ /// [dependencies]
++ /// regex = "*"
++ /// ```
++ pub WILDCARD_DEPENDENCIES,
++ cargo,
++ "wildcard dependencies being used"
++}
++
++declare_lint_pass!(WildcardDependencies => [WILDCARD_DEPENDENCIES]);
++
++impl LateLintPass<'_, '_> for WildcardDependencies {
++ fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++ if !run_lints(cx, &[WILDCARD_DEPENDENCIES], CRATE_HIR_ID) {
++ return;
++ }
++
++ let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
++ metadata
++ } else {
++ span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata");
++ return;
++ };
++
++ for dep in &metadata.packages[0].dependencies {
++ // VersionReq::any() does not work
++ if_chain! {
++ if let Ok(wildcard_ver) = semver::VersionReq::parse("*");
++ if let Some(ref source) = dep.source;
++ if !source.starts_with("git");
++ if dep.req == wildcard_ver;
++ then {
++ span_lint(
++ cx,
++ WILDCARD_DEPENDENCIES,
++ DUMMY_SP,
++ &format!("wildcard dependency for `{}`", dep.name),
++ );
++ }
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{
++ def::{DefKind, Res},
++ Item, ItemKind, UseKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::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
++ /// use std::cmp::Ordering::*;
++ /// ```
++ 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 polute 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.
++ ///
++ /// Note that this will not warn about wildcard imports from modules named `prelude`; many
++ /// crates (including the standard library) provide modules named "prelude" specifically
++ /// designed for wildcard import.
++ ///
++ /// **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:**
++ ///
++ /// Bad:
++ /// ```rust,ignore
++ /// use crate1::*;
++ ///
++ /// foo();
++ /// ```
++ ///
++ /// Good:
++ /// ```rust,ignore
++ /// use crate1::foo;
++ ///
++ /// foo();
++ /// ```
++ pub WILDCARD_IMPORTS,
++ pedantic,
++ "lint `use _::*` statements"
++}
++
++declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
++
++impl LateLintPass<'_, '_> for WildcardImports {
++ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++ if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
++ return;
++ }
++ if_chain! {
++ if !in_macro(item.span);
++ if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
++ // don't lint prelude glob imports
++ if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
++ let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner);
++ 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,
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++use std::borrow::Cow;
++use std::ops::Range;
++
++use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
++use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle};
++use rustc_ast::token;
++use rustc_ast::tokenstream::TokenStream;
++use rustc_errors::Applicability;
++use rustc_lexer::unescape::{self, EscapeError};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_parse::parser;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::symbol::Symbol;
++use rustc_span::{BytePos, Span};
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns when you use `println!("")` to
++ /// print a newline.
++ ///
++ /// **Why is this bad?** You should use `println!()`, which is simpler.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// println!("");
++ /// ```
++ pub PRINTLN_EMPTY_STRING,
++ style,
++ "using `println!(\"\")` with an empty string"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns when you use `print!()` with a format
++ /// string that
++ /// ends in a newline.
++ ///
++ /// **Why is this bad?** You should use `println!()` instead, which appends the
++ /// newline.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let name = "World";
++ /// print!("Hello {}!\n", name);
++ /// ```
++ /// use println!() instead
++ /// ```rust
++ /// # let name = "World";
++ /// println!("Hello {}!", name);
++ /// ```
++ pub PRINT_WITH_NEWLINE,
++ style,
++ "using `print!()` with a format string that ends in a single newline"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
++ /// is to catch debugging remnants.
++ ///
++ /// **Why is this bad?** People often print on *stdout* while debugging an
++ /// application and might forget to remove those prints afterward.
++ ///
++ /// **Known problems:** Only catches `print!` and `println!` calls.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// println!("Hello world!");
++ /// ```
++ pub PRINT_STDOUT,
++ restriction,
++ "printing on stdout"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
++ /// lint is to catch debugging remnants.
++ ///
++ /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
++ /// debugging Rust code. It should not be used in user-facing output.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # let foo = "bar";
++ /// println!("{:?}", foo);
++ /// ```
++ pub USE_DEBUG,
++ restriction,
++ "use of `Debug`-based formatting"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
++ ///
++ /// **Why is this bad?** Using literals as `println!` args is inefficient
++ /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++ /// (i.e., just put the literal in the format string)
++ ///
++ /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
++ /// -- e.g., `println!("{}", env!("FOO"))`.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// println!("{}", "foo");
++ /// ```
++ /// use the literal without formatting:
++ /// ```rust
++ /// println!("foo");
++ /// ```
++ pub PRINT_LITERAL,
++ style,
++ "printing a literal with a format string"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
++ /// print a newline.
++ ///
++ /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::fmt::Write;
++ /// # let mut buf = String::new();
++ /// writeln!(buf, "");
++ /// ```
++ pub WRITELN_EMPTY_STRING,
++ style,
++ "using `writeln!(buf, \"\")` with an empty string"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns when you use `write!()` with a format
++ /// string that
++ /// ends in a newline.
++ ///
++ /// **Why is this bad?** You should use `writeln!()` instead, which appends the
++ /// newline.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::fmt::Write;
++ /// # let mut buf = String::new();
++ /// # let name = "World";
++ /// write!(buf, "Hello {}!\n", name);
++ /// ```
++ pub WRITE_WITH_NEWLINE,
++ style,
++ "using `write!()` with a format string that ends in a single newline"
++}
++
++declare_clippy_lint! {
++ /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
++ ///
++ /// **Why is this bad?** Using literals as `writeln!` args is inefficient
++ /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++ /// (i.e., just put the literal in the format string)
++ ///
++ /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
++ /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// # use std::fmt::Write;
++ /// # let mut buf = String::new();
++ /// writeln!(buf, "{}", "foo");
++ /// ```
++ pub WRITE_LITERAL,
++ style,
++ "writing a literal with a format string"
++}
++
++#[derive(Default)]
++pub struct Write {
++ in_debug_impl: bool,
++}
++
++impl_lint_pass!(Write => [
++ PRINT_WITH_NEWLINE,
++ PRINTLN_EMPTY_STRING,
++ PRINT_STDOUT,
++ USE_DEBUG,
++ PRINT_LITERAL,
++ WRITE_WITH_NEWLINE,
++ WRITELN_EMPTY_STRING,
++ WRITE_LITERAL
++]);
++
++impl EarlyLintPass for Write {
++ fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
++ if let ItemKind::Impl {
++ of_trait: Some(trait_ref),
++ ..
++ } = &item.kind
++ {
++ let trait_name = trait_ref
++ .path
++ .segments
++ .iter()
++ .last()
++ .expect("path has at least one segment")
++ .ident
++ .name;
++ if trait_name == sym!(Debug) {
++ self.in_debug_impl = true;
++ }
++ }
++ }
++
++ fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
++ self.in_debug_impl = false;
++ }
++
++ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
++ if mac.path == sym!(println) {
++ span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
++ if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
++ if fmt_str.symbol == Symbol::intern("") {
++ span_lint_and_sugg(
++ cx,
++ PRINTLN_EMPTY_STRING,
++ mac.span(),
++ "using `println!(\"\")`",
++ "replace it with",
++ "println!()".to_string(),
++ Applicability::MachineApplicable,
++ );
++ }
++ }
++ } else if mac.path == sym!(print) {
++ span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
++ if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
++ if check_newlines(&fmt_str) {
++ span_lint_and_then(
++ cx,
++ PRINT_WITH_NEWLINE,
++ mac.span(),
++ "using `print!()` with a format string that ends in a single newline",
++ |err| {
++ err.multipart_suggestion(
++ "use `println!` instead",
++ vec![
++ (mac.path.span, String::from("println")),
++ (newline_span(&fmt_str), String::new()),
++ ],
++ Applicability::MachineApplicable,
++ );
++ },
++ );
++ }
++ }
++ } else if mac.path == sym!(write) {
++ if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
++ if check_newlines(&fmt_str) {
++ span_lint_and_then(
++ cx,
++ WRITE_WITH_NEWLINE,
++ mac.span(),
++ "using `write!()` with a format string that ends in a single newline",
++ |err| {
++ err.multipart_suggestion(
++ "use `writeln!()` instead",
++ vec![
++ (mac.path.span, String::from("writeln")),
++ (newline_span(&fmt_str), String::new()),
++ ],
++ Applicability::MachineApplicable,
++ );
++ },
++ )
++ }
++ }
++ } else if mac.path == sym!(writeln) {
++ if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
++ if fmt_str.symbol == Symbol::intern("") {
++ let mut applicability = Applicability::MachineApplicable;
++ let suggestion = expr.map_or_else(
++ move || {
++ applicability = Applicability::HasPlaceholders;
++ Cow::Borrowed("v")
++ },
++ move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
++ );
++
++ span_lint_and_sugg(
++ cx,
++ WRITELN_EMPTY_STRING,
++ mac.span(),
++ format!("using `writeln!({}, \"\")`", suggestion).as_str(),
++ "replace it with",
++ format!("writeln!({})", suggestion),
++ applicability,
++ );
++ }
++ }
++ }
++ }
++}
++
++/// Given a format string that ends in a newline and its span, calculates the span of the
++/// newline.
++fn newline_span(fmtstr: &StrLit) -> Span {
++ let sp = fmtstr.span;
++ let contents = &fmtstr.symbol.as_str();
++
++ let newline_sp_hi = sp.hi()
++ - match fmtstr.style {
++ StrStyle::Cooked => BytePos(1),
++ StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
++ };
++
++ let newline_sp_len = if contents.ends_with('\n') {
++ BytePos(1)
++ } else if contents.ends_with(r"\n") {
++ BytePos(2)
++ } else {
++ panic!("expected format string to contain a newline");
++ };
++
++ sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
++}
++
++impl Write {
++ /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
++ /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
++ /// the contents of the string, whether it's a raw string, and the span of the literal in the
++ /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
++ /// `format_str` should be written to.
++ ///
++ /// Example:
++ ///
++ /// Calling this function on
++ /// ```rust
++ /// # use std::fmt::Write;
++ /// # let mut buf = String::new();
++ /// # let something = "something";
++ /// writeln!(buf, "string to write: {}", something);
++ /// ```
++ /// will return
++ /// ```rust,ignore
++ /// (Some("string to write: {}"), Some(buf))
++ /// ```
++ #[allow(clippy::too_many_lines)]
++ fn check_tts<'a>(
++ &self,
++ cx: &EarlyContext<'a>,
++ tts: &TokenStream,
++ is_write: bool,
++ ) -> (Option<StrLit>, Option<Expr>) {
++ use fmt_macros::{
++ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, Parser, Piece,
++ };
++ let tts = tts.clone();
++
++ let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
++ let mut expr: Option<Expr> = None;
++ if is_write {
++ expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
++ Ok(p) => Some(p.into_inner()),
++ Err(_) => return (None, None),
++ };
++ // might be `writeln!(foo)`
++ if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
++ return (None, expr);
++ }
++ }
++
++ let fmtstr = match parser.parse_str_lit() {
++ Ok(fmtstr) => fmtstr,
++ Err(_) => return (None, expr),
++ };
++ let tmp = fmtstr.symbol.as_str();
++ let mut args = vec![];
++ let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
++ while let Some(piece) = fmt_parser.next() {
++ if !fmt_parser.errors.is_empty() {
++ return (None, expr);
++ }
++ if let Piece::NextArgument(arg) = piece {
++ if !self.in_debug_impl && arg.format.ty == "?" {
++ // FIXME: modify rustc's fmt string parser to give us the current span
++ span_lint(cx, USE_DEBUG, parser.prev_token.span, "use of `Debug`-based formatting");
++ }
++ args.push(arg);
++ }
++ }
++ let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
++ let mut idx = 0;
++ loop {
++ const SIMPLE: FormatSpec<'_> = FormatSpec {
++ fill: None,
++ align: AlignUnknown,
++ flags: 0,
++ precision: CountImplied,
++ precision_span: None,
++ width: CountImplied,
++ width_span: None,
++ ty: "",
++ ty_span: None,
++ };
++ if !parser.eat(&token::Comma) {
++ return (Some(fmtstr), expr);
++ }
++ let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
++ expr
++ } else {
++ return (Some(fmtstr), None);
++ };
++ match &token_expr.kind {
++ ExprKind::Lit(_) => {
++ let mut all_simple = true;
++ let mut seen = false;
++ for arg in &args {
++ match arg.position {
++ ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
++ if n == idx {
++ all_simple &= arg.format == SIMPLE;
++ seen = true;
++ }
++ },
++ ArgumentNamed(_) => {},
++ }
++ }
++ if all_simple && seen {
++ span_lint(cx, lint, token_expr.span, "literal with an empty format string");
++ }
++ idx += 1;
++ },
++ ExprKind::Assign(lhs, rhs, _) => {
++ if let ExprKind::Lit(_) = rhs.kind {
++ if let ExprKind::Path(_, p) = &lhs.kind {
++ let mut all_simple = true;
++ let mut seen = false;
++ for arg in &args {
++ match arg.position {
++ ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
++ ArgumentNamed(name) => {
++ if *p == name {
++ seen = true;
++ all_simple &= arg.format == SIMPLE;
++ }
++ },
++ }
++ }
++ if all_simple && seen {
++ span_lint(cx, lint, rhs.span, "literal with an empty format string");
++ }
++ }
++ }
++ },
++ _ => idx += 1,
++ }
++ }
++ }
++}
++
++/// Checks if the format string contains a single newline that terminates it.
++///
++/// Literal and escaped newlines are both checked (only literal for raw strings).
++fn check_newlines(fmtstr: &StrLit) -> bool {
++ let mut has_internal_newline = false;
++ let mut last_was_cr = false;
++ let mut should_lint = false;
++
++ let contents = &fmtstr.symbol.as_str();
++
++ let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
++ let c = c.unwrap();
++
++ if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
++ should_lint = true;
++ } else {
++ last_was_cr = c == '\r';
++ if c == '\n' {
++ has_internal_newline = true;
++ }
++ }
++ };
++
++ match fmtstr.style {
++ StrStyle::Cooked => unescape::unescape_str(contents, &mut cb),
++ StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb),
++ }
++
++ should_lint
++}
--- /dev/null
--- /dev/null
++use crate::consts::{constant_simple, Constant};
++use crate::utils::span_lint_and_help;
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++ /// **What it does:** Checks for `0.0 / 0.0`.
++ ///
++ /// **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ /// ```rust
++ /// 0.0f32 / 0.0;
++ /// ```
++ pub ZERO_DIVIDED_BY_ZERO,
++ complexity,
++ "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
++}
++
++declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ZeroDiv {
++ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++ // check for instances of 0.0/0.0
++ if_chain! {
++ if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind;
++ if let BinOpKind::Div = op.node;
++ // TODO - constant_simple does not fold many operations involving floats.
++ // That's probably fine for this lint - it's pretty unlikely that someone would
++ // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
++ if let Some(lhs_value) = constant_simple(cx, cx.tables, left);
++ if let Some(rhs_value) = constant_simple(cx, cx.tables, right);
++ if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value;
++ if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value;
++ then {
++ // since we're about to suggest a use of f32::NAN or f64::NAN,
++ // match the precision of the literals that are given.
++ let float_type = match (lhs_value, rhs_value) {
++ (Constant::F64(_), _)
++ | (_, Constant::F64(_)) => "f64",
++ _ => "f32"
++ };
++ span_lint_and_help(
++ cx,
++ ZERO_DIVIDED_BY_ZERO,
++ expr.span,
++ "constant division of `0.0` with `0.0` will always result in NaN",
++ None,
++ &format!(
++ "Consider using `{}::NAN` if you would like a constant representing NaN",
++ float_type,
++ ),
++ );
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++[package]
++name = "clippy_workspace_tests"
++version = "0.1.0"
++edition = "2018"
++
++[workspace]
++members = ["subcrate"]
--- /dev/null
--- /dev/null
++#![deny(rust_2018_idioms)]
++
++fn main() {}
--- /dev/null
--- /dev/null
++[package]
++name = "subcrate"
++version = "0.1.0"
--- /dev/null
--- /dev/null
++
--- /dev/null
--- /dev/null
++# Adding a new lint
++
++You are probably here because you want to add a new lint to Clippy. If this is
++the first time you're contributing to Clippy, this document guides you through
++creating an example lint from scratch.
++
++To get started, we will create a lint that detects functions called `foo`,
++because that's clearly a non-descriptive name.
++
++- [Adding a new lint](#adding-a-new-lint)
++ - [Setup](#setup)
++ - [Getting Started](#getting-started)
++ - [Testing](#testing)
++ - [Rustfix tests](#rustfix-tests)
++ - [Edition 2018 tests](#edition-2018-tests)
++ - [Testing manually](#testing-manually)
++ - [Lint declaration](#lint-declaration)
++ - [Lint passes](#lint-passes)
++ - [Emitting a lint](#emitting-a-lint)
++ - [Adding the lint logic](#adding-the-lint-logic)
++ - [Author lint](#author-lint)
++ - [Documentation](#documentation)
++ - [Running rustfmt](#running-rustfmt)
++ - [Debugging](#debugging)
++ - [PR Checklist](#pr-checklist)
++ - [Cheatsheet](#cheatsheet)
++
++## Setup
++
++When working on Clippy, you will need the current git master version of rustc,
++which can change rapidly. Make sure you're working near rust-clippy's master,
++and use the `setup-toolchain.sh` script to configure the appropriate toolchain
++for the Clippy directory.
++
++## Getting Started
++
++There is a bit of boilerplate code that needs to be set up when creating a new
++lint. Fortunately, you can use the clippy dev tools to handle this for you. We
++are naming our new lint `foo_functions` (lints are generally written in snake
++case), and we don't need type information so it will have an early pass type
++(more on this later on). To get started on this lint you can run
++`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
++(category will default to nursery if not provided). This command will create
++two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
++as well as run `cargo dev update_lints` to register the new lint. Next, we'll
++open up these files and add our lint!
++
++## Testing
++
++Let's write some tests first that we can execute while we iterate on our lint.
++
++Clippy uses UI tests for testing. UI tests check that the output of Clippy is
++exactly as expected. Each test is just a plain Rust file that contains the code
++we want to check. The output of Clippy is compared against a `.stderr` file.
++Note that you don't have to create this file yourself, we'll get to
++generating the `.stderr` files further down.
++
++We start by opening the test file created at `tests/ui/foo_functions.rs`.
++
++Update the file with some examples to get started:
++
++```rust
++#![warn(clippy::foo_functions)]
++
++// Impl methods
++struct A;
++impl A {
++ pub fn fo(&self) {}
++ pub fn foo(&self) {}
++ pub fn food(&self) {}
++}
++
++// Default trait methods
++trait B {
++ fn fo(&self) {}
++ fn foo(&self) {}
++ fn food(&self) {}
++}
++
++// Plain functions
++fn fo() {}
++fn foo() {}
++fn food() {}
++
++fn main() {
++ // We also don't want to lint method calls
++ foo();
++ let a = A;
++ a.foo();
++}
++```
++
++Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
++currently this test is meaningless though.
++
++While we are working on implementing our lint, we can keep running the UI
++test. That allows us to check if the output is turning into what we want.
++
++Once we are satisfied with the output, we need to run
++`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
++Please note that, we should run `TESTNAME=foo_functions cargo uitest`
++every time before running `tests/ui/update-all-references.sh`.
++Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
++our lint, we need to commit the generated `.stderr` files, too. In general, you
++should only commit files changed by `tests/ui/update-all-references.sh` for the
++specific lint you are creating/editing.
++
++## Rustfix tests
++
++If the lint you are working on is making use of structured suggestions, the
++test file should include a `// run-rustfix` comment at the top. This will
++additionally run [rustfix] for that test. Rustfix will apply the suggestions
++from the lint to the code of the test file and compare that to the contents of
++a `.fixed` file.
++
++Use `tests/ui/update-all-references.sh` to automatically generate the
++`.fixed` file after running the tests.
++
++[rustfix]: https://github.com/rust-lang/rustfix
++
++## Edition 2018 tests
++
++Some features require the 2018 edition to work (e.g. `async_await`), but
++compile-test tests run on the 2015 edition by default. To change this behavior
++add `// edition:2018` at the top of the test file (note that it's space-sensitive).
++
++## Testing manually
++
++Manually testing against an example file can be useful if you have added some
++`println!`s and the test suite output becomes unreadable. To try Clippy with
++your local modifications, run `env CLIPPY_TESTS=true cargo run --bin
++clippy-driver -- -L ./target/debug input.rs` from the working copy root.
++
++With tests in place, let's have a look at implementing our lint now.
++
++## Lint declaration
++
++Let's start by opening the new file created in the `clippy_lints` crate
++at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
++lint code is. This file has already imported some initial things we will need:
++
++```rust
++use rustc_lint::{EarlyLintPass, EarlyContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_ast::ast::*;
++```
++
++The next step is to update the lint declaration. Lints are declared using the
++[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
++the auto-generated lint declaration to have a real description, something like this:
++
++```rust
++declare_clippy_lint! {
++ /// **What it does:**
++ ///
++ /// **Why is this bad?**
++ ///
++ /// **Known problems:** None.
++ ///
++ /// **Example:**
++ ///
++ /// ```rust
++ /// // example code
++ /// ```
++ pub FOO_FUNCTIONS,
++ pedantic,
++ "function named `foo`, which is not a descriptive name"
++}
++```
++
++* The section of lines prefixed with `///` constitutes the lint documentation
++ section. This is the default documentation style and will be displayed
++ [like this][example_lint_page].
++* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
++ [lint naming guidelines][lint_naming] here when naming your lint.
++ In short, the name should state the thing that is being checked for and
++ read well when used with `allow`/`warn`/`deny`.
++* `pedantic` sets the lint level to `Allow`.
++ The exact mapping can be found [here][category_level_mapping]
++* The last part should be a text that explains what exactly is wrong with the
++ code
++
++The rest of this file contains an empty implementation for our lint pass,
++which in this case is `EarlyLintPass` and should look like this:
++
++```rust
++// clippy_lints/src/foo_functions.rs
++
++// .. imports and lint declaration ..
++
++declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
++
++impl EarlyLintPass for FooFunctions {}
++```
++
++Normally after declaring the lint, we have to run `cargo dev update_lints`,
++which updates some files, so Clippy knows about the new lint. Since we used
++`cargo dev new_lint ...` to generate the lint declaration, this was done
++automatically. While `update_lints` automates most of the things, it doesn't
++automate everything. We will have to register our lint pass manually in the
++`register_plugins` function in `clippy_lints/src/lib.rs`:
++
++```rust
++store.register_early_pass(|| box foo_functions::FooFunctions);
++```
++
++[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
++[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
++[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
++
++## Lint passes
++
++Writing a lint that only checks for the name of a function means that we only
++have to deal with the AST and don't have to deal with the type system at all.
++This is good, because it makes writing this particular lint less complicated.
++
++We have to make this decision with every new Clippy lint. It boils down to using
++either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
++
++In short, the `LateLintPass` has access to type information while the
++`EarlyLintPass` doesn't. If you don't need access to type information, use the
++`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
++hasn't really been a concern with Clippy so far.
++
++Since we don't need type information for checking the function name, we used
++`--pass=early` when running the new lint automation and all the imports were
++added accordingly.
++
++[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
++[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
++
++## Emitting a lint
++
++With UI tests and the lint declaration in place, we can start working on the
++implementation of the lint logic.
++
++Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++ // TODO: Emit lint here
++ }
++}
++```
++
++We implement the [`check_fn`][check_fn] method from the
++[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
++information about the function that is currently being checked. More on that in
++the next section. Let's worry about the details later and emit our lint for
++*every* function definition first.
++
++Depending on how complex we want our lint message to be, we can choose from a
++variety of lint emission functions. They can all be found in
++[`clippy_lints/src/utils/diagnostics.rs`][diagnostics].
++
++`span_lint_and_help` seems most appropriate in this case. It allows us to
++provide an extra help message and we can't really suggest a better name
++automatically. This is how it looks:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++ span_lint_and_help(
++ cx,
++ FOO_FUNCTIONS,
++ span,
++ "function named `foo`",
++ None,
++ "consider using a more meaningful name"
++ );
++ }
++}
++```
++
++Running our UI test should now produce output that contains the lint message.
++
++[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
++[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
++
++## Adding the lint logic
++
++Writing the logic for your lint will most likely be different from our example,
++so this section is kept rather short.
++
++Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
++that has the [`FnKind::Fn`] variant. It provides access to the name of the
++function/method via an [`Ident`][ident].
++
++With that we can expand our `check_fn` method to:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++ if is_foo_fn(fn_kind) {
++ span_lint_and_help(
++ cx,
++ FOO_FUNCTIONS,
++ span,
++ "function named `foo`",
++ None,
++ "consider using a more meaningful name"
++ );
++ }
++ }
++}
++```
++
++We separate the lint conditional from the lint emissions because it makes the
++code a bit easier to read. In some cases this separation would also allow to
++write some unit tests (as opposed to only UI tests) for the separate function.
++
++In our example, `is_foo_fn` looks like:
++
++```rust
++// use statements, impl EarlyLintPass, check_fn, ..
++
++fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
++ match fn_kind {
++ FnKind::Fn(_, ident, ..) => {
++ // check if `fn` name is `foo`
++ ident.name.as_str() == "foo"
++ }
++ // ignore closures
++ FnKind::Closure(..) => false
++ }
++}
++```
++
++Now we should also run the full test suite with `cargo test`. At this point
++running `cargo test` should produce the expected output. Remember to run
++`tests/ui/update-all-references.sh` to update the `.stderr` file.
++
++`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
++implementation is not violating any Clippy lints itself.
++
++That should be it for the lint implementation. Running `cargo test` should now
++pass.
++
++[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
++[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
++[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
++
++## Author lint
++
++If you have trouble implementing your lint, there is also the internal `author`
++lint to generate Clippy code that detects the offending pattern. It does not
++work for all of the Rust syntax, but can give a good starting point.
++
++The quickest way to use it, is the
++[Rust playground: play.rust-lang.org][author_example].
++Put the code you want to lint into the editor and add the `#[clippy::author]`
++attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
++see the generated code in the output below.
++
++[Here][author_example] is an example on the playground.
++
++If the command was executed successfully, you can copy the code over to where
++you are implementing your lint.
++
++[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
++
++## Documentation
++
++The final thing before submitting our PR is to add some documentation to our
++lint declaration.
++
++Please document your lint with a doc comment akin to the following:
++
++```rust
++declare_clippy_lint! {
++ /// **What it does:** Checks for ... (describe what the lint matches).
++ ///
++ /// **Why is this bad?** Supply the reason for linting the code.
++ ///
++ /// **Known problems:** None. (Or describe where it could go wrong.)
++ ///
++ /// **Example:**
++ ///
++ /// ```rust,ignore
++ /// // Bad
++ /// Insert a short example of code that triggers the lint
++ ///
++ /// // Good
++ /// Insert a short example of improved code that doesn't trigger the lint
++ /// ```
++ pub FOO_FUNCTIONS,
++ pedantic,
++ "function named `foo`, which is not a descriptive name"
++}
++```
++
++Once your lint is merged, this documentation will show up in the [lint
++list][lint_list].
++
++[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
++
++## Running rustfmt
++
++[Rustfmt] is a tool for formatting Rust code according to style guidelines.
++Your code has to be formatted by `rustfmt` before a PR can be merged.
++Clippy uses nightly `rustfmt` in the CI.
++
++It can be installed via `rustup`:
++
++```bash
++rustup component add rustfmt --toolchain=nightly
++```
++
++Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
++installed for the nightly toolchain.
++
++[Rustfmt]: https://github.com/rust-lang/rustfmt
++
++## Debugging
++
++If you want to debug parts of your lint implementation, you can use the [`dbg!`]
++macro anywhere in your code. Running the tests should then include the debug
++output in the `stdout` part.
++
++[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
++
++## PR Checklist
++
++Before submitting your PR make sure you followed all of the basic requirements:
++
++<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
++
++- [ ] Followed [lint naming conventions][lint_naming]
++- [ ] Added passing UI tests (including committed `.stderr` file)
++- [ ] `cargo test` passes locally
++- [ ] Executed `cargo dev update_lints`
++- [ ] Added lint documentation
++- [ ] Run `cargo dev fmt`
++
++## Cheatsheet
++
++Here are some pointers to things you are likely going to need for every lint:
++
++* [Clippy utils][utils] - Various helper functions. Maybe the function you need
++ is already in here (`implements_trait`, `match_path`, `snippet`, etc)
++* [Clippy diagnostics][diagnostics]
++* [The `if_chain` macro][if_chain]
++* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
++* [`Span`][span]
++* [`Applicability`][applicability]
++* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
++* [The nightly rustc docs][nightly_docs] which has been linked to throughout
++ this guide
++
++For `EarlyLintPass` lints:
++
++* [`EarlyLintPass`][early_lint_pass]
++* [`rustc_ast::ast`][ast]
++
++For `LateLintPass` lints:
++
++* [`LateLintPass`][late_lint_pass]
++* [`Ty::TyKind`][ty]
++
++While most of Clippy's lint utils are documented, most of rustc's internals lack
++documentation currently. This is unfortunate, but in most cases you can probably
++get away with copying things from existing similar lints. If you are stuck,
++don't hesitate to ask on [Discord] or in the issue/PR.
++
++[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
++[if_chain]: https://docs.rs/if_chain/*/if_chain/
++[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
++[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
++[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
++[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
++[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
++[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
++[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
++[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
++[Discord]: https://discord.gg/rust-lang
--- /dev/null
--- /dev/null
++# Backport Changes
++
++Sometimes it is necessary to backport changes to the beta release of Clippy.
++Backports in Clippy are rare and should be approved by the Clippy team. For
++example, a backport is done, if a crucial ICE was fixed or a lint is broken to a
++point, that it has to be disabled, before landing on stable.
++
++Backports are done to the `beta` release of Clippy. Backports to stable Clippy
++releases basically don't exist, since this would require a Rust point release,
++which is almost never justifiable for a Clippy fix.
++
++
++## Backport the changes
++
++Backports are done on the beta branch of the Clippy repository.
++
++```bash
++# Assuming the current directory corresponds to the Clippy repository
++$ git checkout beta
++$ git checkout -b backport
++$ git cherry-pick <SHA> # `<SHA>` is the commit hash of the commit, that should be backported
++$ git push origin backport
++```
++
++After this, you can open a PR to the `beta` branch of the Clippy repository.
++
++
++## Update Clippy in the Rust Repository
++
++This step must be done, **after** the PR of the previous step was merged.
++
++After the backport landed in the Clippy repository, also the Clippy version on
++the Rust `beta` branch has to be updated.
++
++```bash
++# Assuming the current directory corresponds to the Rust repository
++$ git checkout beta
++$ git checkout -b clippy_backport
++$ pushd src/tools/clippy
++$ git fetch
++$ git checkout beta
++$ popd
++$ git add src/tools/clippy
++§ git commit -m "Update Clippy"
++$ git push origin clippy_backport
++```
++
++After this you can open a PR to the `beta` branch of the Rust repository. In
++this PR you should tag the Clippy team member, that agreed to the backport or
++the `@rust-lang/clippy` team. Make sure to add `[beta]` to the title of the PR.
--- /dev/null
--- /dev/null
++# Changelog Update
++
++If you want to help with updating the [changelog][changelog], you're in the right place.
++
++## When to update
++
++Typos and other small fixes/additions are _always_ welcome.
++
++Special care needs to be taken when it comes to updating the changelog for a new
++Rust release. For that purpose, the changelog is ideally updated during the week
++before an upcoming stable release. You can find the release dates on the [Rust
++Forge][forge].
++
++Most of the time we only need to update the changelog for minor Rust releases. It's
++been very rare that Clippy changes were included in a patch release.
++
++## Changelog update walkthrough
++
++### 1. Finding the relevant Clippy commits
++
++Each Rust release ships with its own version of Clippy. The Clippy submodule can
++be found in the `tools` directory of the Rust repository.
++
++Depending on the current time and what exactly you want to update, the following
++bullet points might be helpful:
++
++* When writing the release notes for the **upcoming stable release** you need to check
++ out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools]
++* When writing the release notes for the **upcoming beta release**, you need to check
++ out the Clippy commit of the current Rust `master`. [Link][rust_master_tools]
++* When writing the (forgotten) release notes for a **past stable release**, you
++ need to select the Rust release tag from the dropdown and then check the
++ commit of the Clippy directory:
++
++ ![Explanation of how to find the commit hash](https://user-images.githubusercontent.com/2042399/62846160-1f8b0480-bcce-11e9-9da8-7964ca034e7a.png)
++
++
++### 2. Fetching the PRs between those commits
++
++Once you've got the correct commit range, run
++
++ util/fetch_prs_between.sh commit1 commit2 > changes.txt
++
++and open that file in your editor of choice.
++
++When updating the changelog it's also a good idea to make sure that `commit1` is
++already correct in the current changelog.
++
++### 3. Authoring the final changelog
++
++The above script should have dumped all the relevant PRs to the file you
++specified. It should have filtered out most of the irrelevant PRs
++already, but it's a good idea to do a manual cleanup pass where you look for
++more irrelevant PRs. If you're not sure about some PRs, just leave them in for
++the review and ask for feedback.
++
++With the PRs filtered, you can start to take each PR and move the
++`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but
++try to keep it somewhat coherent.
++
++The order should roughly be:
++
++1. New lints
++2. Moves or deprecations of lints
++3. Changes that expand what code existing lints cover
++4. False positive fixes
++5. Suggestion fixes/improvements
++6. ICE fixes
++7. Documentation improvements
++8. Others
++
++Please also be sure to update the Beta/Unreleased sections at the top with the
++relevant commit ranges.
++
++[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
++[forge]: https://forge.rust-lang.org/
++[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools
++[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools
--- /dev/null
--- /dev/null
++# 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. [Find the Clippy commit](#find-the-clippy-commit)
++2. [Tag the stable commit](#tag-the-stable-commit)
++3. [Update `CHANGELOG.md`](#update-changelogmd)
++4. [Remerge the `beta` branch](#remerge-the-beta-branch)
++5. [Update the `beta` branch](#update-the-beta-branch)
++
++_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/
++
++
++## 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
++$ git submodule update
++$ SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
++```
++
++
++## 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 master --tags # `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 `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
++
++
++## 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 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
++$ git submodule update
++$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
++```
++
++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 rebase $BETA_SHA
++$ git push upstream beta
++```
--- /dev/null
--- /dev/null
++This repository was previously licensed under MPL-2.0, however in #3093
++([archive](http://web.archive.org/web/20181005185227/https://github.com/rust-lang-nursery/rust-clippy/issues/3093),
++[screenshot](https://user-images.githubusercontent.com/1617736/46573505-5b856880-c94b-11e8-9a14-981c889b4981.png)) we
++relicensed it to the Rust license (dual licensed as Apache v2 / MIT)
++
++At the time, the contributors were those listed in contributors.txt.
++
++We opened a bunch of issues asking for an explicit relicensing approval. Screenshots of all these issues at the time of
++relicensing are archived on GitHub. We also have saved Wayback Machine copies of these:
++
++- #3094
++ ([archive](http://web.archive.org/web/20181005191247/https://github.com/rust-lang-nursery/rust-clippy/issues/3094),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573506-5b856880-c94b-11e8-8a44-51cb40bc16ee.png))
++- #3095
++ ([archive](http://web.archive.org/web/20181005184416/https://github.com/rust-lang-nursery/rust-clippy/issues/3095),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573507-5c1dff00-c94b-11e8-912a-4bd6b5f838f5.png))
++- #3096
++ ([archive](http://web.archive.org/web/20181005184802/https://github.com/rust-lang-nursery/rust-clippy/issues/3096),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573508-5c1dff00-c94b-11e8-9425-2464f7260ff0.png))
++- #3097
++ ([archive](http://web.archive.org/web/20181005184821/https://github.com/rust-lang-nursery/rust-clippy/issues/3097),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573509-5c1dff00-c94b-11e8-8ba2-53f687984fe7.png))
++- #3098
++ ([archive](http://web.archive.org/web/20181005184900/https://github.com/rust-lang-nursery/rust-clippy/issues/3098),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573510-5c1dff00-c94b-11e8-8f64-371698401c60.png))
++- #3099
++ ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3099),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573511-5c1dff00-c94b-11e8-8e20-7d0eeb392b95.png))
++- #3100
++ ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3100),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573512-5c1dff00-c94b-11e8-8a13-7d758ed3563d.png))
++- #3230
++ ([archive](http://web.archive.org/web/20181005184903/https://github.com/rust-lang-nursery/rust-clippy/issues/3230),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573513-5cb69580-c94b-11e8-86b1-14ce82741e5c.png))
++
++The usernames of commenters on these issues can be found in relicense_comments.txt
++
++There are a couple people in relicense_comments.txt who are not found in contributors.txt:
++
++- @EpocSquadron has [made minor text contributions to the
++ README](https://github.com/rust-lang/rust-clippy/commits?author=EpocSquadron) which have since been overwritten, and
++ doesn't count
++- @JayKickliter [agreed to the relicense on their pull
++ request](https://github.com/rust-lang/rust-clippy/pull/3195#issuecomment-423781016)
++ ([archive](https://web.archive.org/web/20181005190730/https://github.com/rust-lang/rust-clippy/pull/3195),
++ [screenshot](https://user-images.githubusercontent.com/1617736/46573514-5cb69580-c94b-11e8-8ffb-05a5bd02e2cc.png)
++
++- @sanmai-NL's [contribution](https://github.com/rust-lang/rust-clippy/commits?author=sanmai-NL) is a minor one-word
++ addition which doesn't count for copyright assignment
++- @zmt00's [contributions](https://github.com/rust-lang/rust-clippy/commits?author=zmt00) are minor typo fixes and don't
++ count
++- @VKlayd has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=VKlayd) which we rewrote
++ (see below)
++- @wartman4404 has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=wartman4404) which
++ we rewrote (see below)
++
++
++Two of these contributors had nonminor contributions (#2184, #427) requiring a rewrite, carried out in #3251
++([archive](http://web.archive.org/web/20181005192411/https://github.com/rust-lang-nursery/rust-clippy/pull/3251),
++[screenshot](https://user-images.githubusercontent.com/1617736/46573515-5cb69580-c94b-11e8-86e5-b456452121b2.png))
++
++First, I (Manishearth) removed the lints they had added. I then documented at a high level what the lints did in #3251,
++asking for co-maintainers who had not seen the code for the lints to rewrite them. #2814 was rewritten by @phansch, and
++#427 was rewritten by @oli-obk, who did not recall having previously seen the code they were rewriting.
++
++------
++
++Since this document was written, @JayKickliter and @sanmai-ML added their consent in #3230
++([archive](http://web.archive.org/web/20181006171926/https://github.com/rust-lang-nursery/rust-clippy/issues/3230))
--- /dev/null
--- /dev/null
++0ndorio
++0xbsec
++17cupsofcoffee
++Aaron1011
++Aaronepower
++aaudiber
++afck
++alexcrichton
++AlexEne
++alexeyzab
++alexheretic
++alexreg
++alusch
++andersk
++aochagavia
++apasel422
++Arnavion
++AtheMathmo
++auscompgeek
++AVerm
++badboy
++Baelyk
++BenoitZugmeyer
++bestouff
++birkenfeld
++bjgill
++bkchr
++Bobo1239
++bood
++bootandy
++b-r-u
++budziq
++CAD97
++Caemor
++camsteffen
++carols10cents
++CBenoit
++cesarb
++cgm616
++chrisduerr
++chrisvittal
++chyvonomys
++clarcharr
++clippered
++commandline
++cramertj
++csmoe
++ctjhoa
++cuviper
++CYBAI
++darArch
++DarkEld3r
++dashed
++daubaris
++d-dorazio
++debris
++dereckson
++detrumi
++devonhollowood
++dtolnay
++durka
++dwijnand
++eddyb
++elliottneilclark
++elpiel
++ensch
++EpicatSupercell
++EpocSquadron
++erickt
++estk
++etaoins
++F001
++fanzier
++FauxFaux
++fhartwig
++flip1995
++Fraser999
++Frederick888
++frewsxcv
++gbip
++gendx
++gibfahn
++gnieto
++gnzlbg
++goodmanjonathan
++guido4000
++GuillaumeGomez
++Hanaasagi
++hdhoang
++HMPerson1
++hobofan
++iKevinY
++illicitonion
++imp
++inrustwetrust
++ishitatsuyuki
++Jascha-N
++jayhardee9
++JayKickliter
++JDemler
++jedisct1
++jmquigs
++joelgallant
++joeratt
++josephDunne
++JoshMcguigan
++joshtriplett
++jugglerchris
++karyon
++Keats
++kennytm
++Kha
++killercup
++kimsnj
++KitFreddura
++koivunej
++kraai
++kvikas
++LaurentMazare
++letheed
++llogiq
++lo48576
++lpesk
++lucab
++luisbg
++lukasstevens
++Machtan
++MaloJaffre
++Manishearth
++marcusklaas
++mark-i-m
++martiansideofthemoon
++martinlindhe
++mathstuf
++mati865
++matthiaskrgr
++mattyhall
++mbrubeck
++mcarton
++memoryleak47
++messense
++michaelrutherford
++mikerite
++mipli
++mockersf
++montrivo
++mrecachinas
++Mrmaxmeier
++mrmonday
++ms2300
++Ms2ger
++musoke
++nathan
++Nemo157
++NiekGr
++niklasf
++nrc
++nweston
++o01eg
++ogham
++oli-obk
++ordovicia
++pengowen123
++pgerber
++phansch
++philipturnbull
++pickfire
++pietro
++PixelPirate
++pizzaiter
++PSeitz
++Pyriphlegethon
++pythonesque
++quininer
++Rantanen
++rcoh
++reiner-dolp
++reujab
++Robzz
++samueltardieu
++sanmai-NL
++sanxiyn
++scott-linder
++scottmcm
++scurest
++senden9
++shahn
++shepmaster
++shnewto
++shssoichiro
++siiptuo
++sinkuu
++skade
++sourcefrog
++sourcejedi
++steveklabnik
++sunfishcode
++sunjay
++swgillespie
++Techcable
++terry90
++theemathas
++thekidxp
++theotherphil
++TimNN
++TomasKralCZ
++tomprince
++topecongiro
++tspiteri
++Twisol
++U007D
++uHOOCCOOHu
++untitaker
++upsuper
++utaal
++utam0k
++vi
++VKlayd
++Vlad-Shcherbina
++vorner
++wafflespeanut
++wartman4404
++waywardmonkeys
++yaahallo
++yangby-cryptape
++yati-sagade
++ykrivopalov
++ysimonson
++zayenz
++zmanian
++zmbush
++zmt00
--- /dev/null
--- /dev/null
++0ndorio
++0xbsec
++17cupsofcoffee
++Aaron1011
++Aaronepower
++aaudiber
++afck
++alexcrichton
++AlexEne
++alexeyzab
++alexheretic
++alexreg
++alusch
++andersk
++aochagavia
++apasel422
++Arnavion
++AtheMathmo
++auscompgeek
++AVerm
++badboy
++Baelyk
++BenoitZugmeyer
++bestouff
++birkenfeld
++bjgill
++bkchr
++Bobo1239
++bood
++bootandy
++b-r-u
++budziq
++CAD97
++Caemor
++camsteffen
++carols10cents
++CBenoit
++cesarb
++cgm616
++chrisduerr
++chrisvittal
++chyvonomys
++clarcharr
++clippered
++commandline
++cramertj
++csmoe
++ctjhoa
++cuviper
++CYBAI
++darArch
++DarkEld3r
++dashed
++daubaris
++d-dorazio
++debris
++dereckson
++detrumi
++devonhollowood
++dtolnay
++durka
++dwijnand
++eddyb
++elliottneilclark
++elpiel
++ensch
++EpicatSupercell
++erickt
++estk
++etaoins
++F001
++fanzier
++FauxFaux
++fhartwig
++flip1995
++Fraser999
++Frederick888
++frewsxcv
++gbip
++gendx
++gibfahn
++gnieto
++gnzlbg
++goodmanjonathan
++guido4000
++GuillaumeGomez
++Hanaasagi
++hdhoang
++HMPerson1
++hobofan
++iKevinY
++illicitonion
++imp
++inrustwetrust
++ishitatsuyuki
++Jascha-N
++jayhardee9
++JDemler
++jedisct1
++jmquigs
++joelgallant
++joeratt
++josephDunne
++JoshMcguigan
++joshtriplett
++jugglerchris
++karyon
++Keats
++kennytm
++Kha
++killercup
++kimsnj
++KitFreddura
++koivunej
++kraai
++kvikas
++LaurentMazare
++letheed
++llogiq
++lo48576
++lpesk
++lucab
++luisbg
++lukasstevens
++Machtan
++MaloJaffre
++Manishearth
++marcusklaas
++mark-i-m
++martiansideofthemoon
++martinlindhe
++mathstuf
++mati865
++matthiaskrgr
++mattyhall
++mbrubeck
++mcarton
++memoryleak47
++messense
++michaelrutherford
++mikerite
++mipli
++mockersf
++montrivo
++mrecachinas
++Mrmaxmeier
++mrmonday
++ms2300
++Ms2ger
++musoke
++nathan
++Nemo157
++NiekGr
++niklasf
++nrc
++nweston
++o01eg
++ogham
++oli-obk
++ordovicia
++pengowen123
++pgerber
++phansch
++philipturnbull
++pickfire
++pietro
++PixelPirate
++pizzaiter
++PSeitz
++Pyriphlegethon
++pythonesque
++quininer
++Rantanen
++rcoh
++reiner-dolp
++reujab
++Robzz
++samueltardieu
++sanxiyn
++scott-linder
++scottmcm
++scurest
++senden9
++shahn
++shepmaster
++shnewto
++shssoichiro
++siiptuo
++sinkuu
++skade
++sourcefrog
++sourcejedi
++steveklabnik
++sunfishcode
++sunjay
++swgillespie
++Techcable
++terry90
++theemathas
++thekidxp
++theotherphil
++TimNN
++TomasKralCZ
++tommilligan
++tomprince
++topecongiro
++tspiteri
++Twisol
++U007D
++uHOOCCOOHu
++untitaker
++upsuper
++utaal
++utam0k
++vi
++Vlad-Shcherbina
++vorner
++wafflespeanut
++waywardmonkeys
++yaahallo
++yangby-cryptape
++yati-sagade
++ykrivopalov
++ysimonson
++zayenz
++zmanian
++zmbush
--- /dev/null
--- /dev/null
++[package]
++name = "clippy-mini-macro-test"
++version = "0.2.0"
++authors = [
++ "Manish Goregaokar <manishsmail@gmail.com>",
++ "Andre Bogus <bogusandre@gmail.com>",
++ "Georg Brandl <georg@python.org>",
++ "Martin Carton <cartonmartin@gmail.com>",
++ "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>"
++]
++license = "MIT OR Apache-2.0"
++description = "A macro to test clippy's procedural macro checks"
++repository = "https://github.com/rust-lang/rust-clippy"
++edition = "2018"
++
++[lib]
++name = "clippy_mini_macro_test"
++proc-macro = true
++
++[dependencies]
--- /dev/null
--- /dev/null
++#![feature(proc_macro_quote, proc_macro_hygiene)]
++#![deny(rust_2018_idioms)]
++// FIXME: Remove this attribute once the weird failure is gone.
++#![allow(unused_extern_crates)]
++extern crate proc_macro;
++
++use proc_macro::{quote, TokenStream};
++
++#[proc_macro_derive(ClippyMiniMacroTest)]
++pub fn mini_macro(_: TokenStream) -> TokenStream {
++ quote!(
++ #[allow(unused)]
++ fn needless_take_by_value(s: String) {
++ println!("{}", s.len());
++ }
++ #[allow(unused)]
++ fn needless_loop(items: &[u8]) {
++ for i in 0..items.len() {
++ println!("{}", items[i]);
++ }
++ }
++ fn line_wrapper() {
++ println!("{}", line!());
++ }
++ )
++}
--- /dev/null
--- /dev/null
++nightly
--- /dev/null
--- /dev/null
++[package]
++name = "rustc_tools_util"
++version = "0.2.0"
++authors = ["Matthias Krüger <matthias.krueger@famsik.de>"]
++description = "small helper to generate version information for git packages"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["rustc", "tool", "git", "version", "hash"]
++categories = ["development-tools"]
++edition = "2018"
++
++[dependencies]
++
++[features]
++deny-warnings = []
--- /dev/null
--- /dev/null
++# rustc_tools_util
++
++A small tool to help you generate version information
++for packages installed from a git repo
++
++## Usage
++
++Add a `build.rs` file to your repo and list it in `Cargo.toml`
++````
++build = "build.rs"
++````
++
++List rustc_tools_util as regular AND build dependency.
++````
++[dependencies]
++rustc_tools_util = "0.1"
++
++[build-dependencies]
++rustc_tools_util = "0.1"
++````
++
++In `build.rs`, generate the data in your `main()`
++````rust
++fn main() {
++ println!(
++ "cargo:rustc-env=GIT_HASH={}",
++ rustc_tools_util::get_commit_hash().unwrap_or_default()
++ );
++ println!(
++ "cargo:rustc-env=COMMIT_DATE={}",
++ rustc_tools_util::get_commit_date().unwrap_or_default()
++ );
++ println!(
++ "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
++ rustc_tools_util::get_channel().unwrap_or_default()
++ );
++}
++
++````
++
++Use the version information in your main.rs
++````rust
++use rustc_tools_util::*;
++
++fn show_version() {
++ let version_info = rustc_tools_util::get_version_info!();
++ println!("{}", version_info);
++}
++````
++This gives the following output in clippy:
++`clippy 0.0.212 (a416c5e 2018-12-14)`
++
++
++## License
++
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++option. All files in the project carrying such notice may not be
++copied, modified, or distributed except according to those terms.
--- /dev/null
--- /dev/null
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use std::env;
++
++#[macro_export]
++macro_rules! get_version_info {
++ () => {{
++ let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
++ let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
++ let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
++ let crate_name = String::from(env!("CARGO_PKG_NAME"));
++
++ let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
++ let commit_hash = option_env!("GIT_HASH").map(str::to_string);
++ let commit_date = option_env!("COMMIT_DATE").map(str::to_string);
++
++ VersionInfo {
++ major,
++ minor,
++ patch,
++ host_compiler,
++ commit_hash,
++ commit_date,
++ crate_name,
++ }
++ }};
++}
++
++// some code taken and adapted from RLS and cargo
++pub struct VersionInfo {
++ pub major: u8,
++ pub minor: u8,
++ pub patch: u16,
++ pub host_compiler: Option<String>,
++ pub commit_hash: Option<String>,
++ pub commit_date: Option<String>,
++ pub crate_name: String,
++}
++
++impl std::fmt::Display for VersionInfo {
++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++ let hash = self.commit_hash.clone().unwrap_or_default();
++ let hash_trimmed = hash.trim();
++
++ let date = self.commit_date.clone().unwrap_or_default();
++ let date_trimmed = date.trim();
++
++ if (hash_trimmed.len() + date_trimmed.len()) > 0 {
++ write!(
++ f,
++ "{} {}.{}.{} ({} {})",
++ self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed,
++ )?;
++ } else {
++ write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
++ }
++
++ Ok(())
++ }
++}
++
++impl std::fmt::Debug for VersionInfo {
++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++ write!(
++ f,
++ "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
++ self.crate_name, self.major, self.minor, self.patch,
++ )?;
++ if self.commit_hash.is_some() {
++ write!(
++ f,
++ ", commit_hash: \"{}\", commit_date: \"{}\" }}",
++ self.commit_hash.clone().unwrap_or_default().trim(),
++ self.commit_date.clone().unwrap_or_default().trim()
++ )?;
++ } else {
++ write!(f, " }}")?;
++ }
++
++ Ok(())
++ }
++}
++
++#[must_use]
++pub fn get_commit_hash() -> Option<String> {
++ std::process::Command::new("git")
++ .args(&["rev-parse", "--short", "HEAD"])
++ .output()
++ .ok()
++ .and_then(|r| String::from_utf8(r.stdout).ok())
++}
++
++#[must_use]
++pub fn get_commit_date() -> Option<String> {
++ std::process::Command::new("git")
++ .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
++ .output()
++ .ok()
++ .and_then(|r| String::from_utf8(r.stdout).ok())
++}
++
++#[must_use]
++pub fn get_channel() -> Option<String> {
++ match env::var("CFG_RELEASE_CHANNEL") {
++ Ok(channel) => Some(channel),
++ Err(_) => {
++ // if that failed, try to ask rustc -V, do some parsing and find out
++ match std::process::Command::new("rustc")
++ .arg("-V")
++ .output()
++ .ok()
++ .and_then(|r| String::from_utf8(r.stdout).ok())
++ {
++ Some(rustc_output) => {
++ if rustc_output.contains("beta") {
++ Some(String::from("beta"))
++ } else if rustc_output.contains("stable") {
++ Some(String::from("stable"))
++ } else {
++ // default to nightly if we fail to parse
++ Some(String::from("nightly"))
++ }
++ },
++ // default to nightly
++ None => Some(String::from("nightly")),
++ }
++ },
++ }
++}
++
++#[cfg(test)]
++mod test {
++ use super::*;
++
++ #[test]
++ fn test_struct_local() {
++ let vi = get_version_info!();
++ assert_eq!(vi.major, 0);
++ assert_eq!(vi.minor, 2);
++ assert_eq!(vi.patch, 0);
++ assert_eq!(vi.crate_name, "rustc_tools_util");
++ // hard to make positive tests for these since they will always change
++ assert!(vi.commit_hash.is_none());
++ assert!(vi.commit_date.is_none());
++ }
++
++ #[test]
++ fn test_display_local() {
++ let vi = get_version_info!();
++ assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0");
++ }
++
++ #[test]
++ fn test_debug_local() {
++ let vi = get_version_info!();
++ let s = format!("{:?}", vi);
++ assert_eq!(
++ s,
++ "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }"
++ );
++ }
++}
--- /dev/null
--- /dev/null
++max_width = 120
++comment_width = 100
++match_block_trailing_comma = true
++wrap_comments = true
++edition = "2018"
++error_on_line_overflow = true
--- /dev/null
--- /dev/null
++#!/usr/bin/env bash
++# Set up the appropriate rustc toolchain
++
++set -e
++
++cd "$(dirname "$0")"
++
++RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false
++CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}
++
++# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin
++if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then
++ cargo +nightly install rustup-toolchain-install-master
++else
++ VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*")
++ REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*")
++ echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH"
++ echo " current version : $VERSION"
++ echo " remote version : $REMOTE"
++fi
++
++RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}')
++
++if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then
++ echo "info: master toolchain is up-to-date"
++ exit 0
++fi
++
++if [[ -n "$HOST_TOOLCHAIN" ]]; then
++ TOOLCHAIN=('--host' "$HOST_TOOLCHAIN")
++else
++ TOOLCHAIN=()
++fi
++
++rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -- "$RUST_COMMIT"
++rustup override set master
--- /dev/null
--- /dev/null
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![feature(rustc_private)]
++#![feature(str_strip)]
++
++// FIXME: switch to something more ergonomic here, once available.
++// (Currently there is no way to opt into sysroot crates without `extern crate`.)
++#[allow(unused_extern_crates)]
++extern crate rustc_driver;
++#[allow(unused_extern_crates)]
++extern crate rustc_errors;
++#[allow(unused_extern_crates)]
++extern crate rustc_interface;
++#[allow(unused_extern_crates)]
++extern crate rustc_middle;
++
++use rustc_interface::interface;
++use rustc_middle::ty::TyCtxt;
++use rustc_tools_util::VersionInfo;
++
++use lazy_static::lazy_static;
++use std::borrow::Cow;
++use std::env;
++use std::ops::Deref;
++use std::panic;
++use std::path::{Path, PathBuf};
++use std::process::{exit, Command};
++
++mod lintlist;
++
++/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
++/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
++fn arg_value<'a, T: Deref<Target = str>>(
++ args: &'a [T],
++ find_arg: &str,
++ pred: impl Fn(&str) -> bool,
++) -> Option<&'a str> {
++ let mut args = args.iter().map(Deref::deref);
++ while let Some(arg) = args.next() {
++ let mut arg = arg.splitn(2, '=');
++ if arg.next() != Some(find_arg) {
++ continue;
++ }
++
++ match arg.next().or_else(|| args.next()) {
++ Some(v) if pred(v) => return Some(v),
++ _ => {},
++ }
++ }
++ None
++}
++
++#[test]
++fn test_arg_value() {
++ let args = &["--bar=bar", "--foobar", "123", "--foo"];
++
++ assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
++ assert_eq!(arg_value(args, "--bar", |_| false), None);
++ assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
++ assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
++ assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
++ assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
++ assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
++ assert_eq!(arg_value(args, "--foo", |_| true), None);
++}
++
++struct DefaultCallbacks;
++impl rustc_driver::Callbacks for DefaultCallbacks {}
++
++struct ClippyCallbacks;
++impl rustc_driver::Callbacks for ClippyCallbacks {
++ fn config(&mut self, config: &mut interface::Config) {
++ let previous = config.register_lints.take();
++ config.register_lints = Some(Box::new(move |sess, mut lint_store| {
++ // technically we're ~guaranteed that this is none but might as well call anything that
++ // is there already. Certainly it can't hurt.
++ if let Some(previous) = &previous {
++ (previous)(sess, lint_store);
++ }
++
++ let conf = clippy_lints::read_conf(&[], &sess);
++ clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
++ clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf);
++ clippy_lints::register_renamed(&mut lint_store);
++ }));
++
++ // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
++ // run on the unoptimized MIR. On the other hand this results in some false negatives. If
++ // MIR passes can be enabled / disabled separately, we should figure out, what passes to
++ // use for Clippy.
++ config.opts.debugging_opts.mir_opt_level = 0;
++ }
++}
++
++#[allow(clippy::find_map, clippy::filter_map)]
++fn describe_lints() {
++ use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS};
++ use std::collections::HashSet;
++
++ println!(
++ "
++Available lint options:
++ -W <foo> Warn about <foo>
++ -A <foo> Allow <foo>
++ -D <foo> Deny <foo>
++ -F <foo> Forbid <foo> (deny <foo> and all attempts to override)
++
++"
++ );
++
++ let lint_level = |lint: &Lint| {
++ LINT_LEVELS
++ .iter()
++ .find(|level_mapping| level_mapping.0 == lint.group)
++ .map(|(_, level)| match level {
++ Level::Allow => "allow",
++ Level::Warn => "warn",
++ Level::Deny => "deny",
++ })
++ .unwrap()
++ };
++
++ let mut lints: Vec<_> = ALL_LINTS.iter().collect();
++ // The sort doesn't case-fold but it's doubtful we care.
++ lints.sort_by_cached_key(|x: &&Lint| (lint_level(x), x.name));
++
++ let max_lint_name_len = lints
++ .iter()
++ .map(|lint| lint.name.len())
++ .map(|len| len + "clippy::".len())
++ .max()
++ .unwrap_or(0);
++
++ let padded = |x: &str| {
++ let mut s = " ".repeat(max_lint_name_len - x.chars().count());
++ s.push_str(x);
++ s
++ };
++
++ let scoped = |x: &str| format!("clippy::{}", x);
++
++ let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect();
++
++ println!("Lint checks provided by clippy:\n");
++ println!(" {} {:7.7} meaning", padded("name"), "default");
++ println!(" {} {:7.7} -------", padded("----"), "-------");
++
++ let print_lints = |lints: &[&Lint]| {
++ for lint in lints {
++ let name = lint.name.replace("_", "-");
++ println!(
++ " {} {:7.7} {}",
++ padded(&scoped(&name)),
++ lint_level(lint),
++ lint.desc
++ );
++ }
++ println!("\n");
++ };
++
++ print_lints(&lints);
++
++ let max_group_name_len = std::cmp::max(
++ "clippy::all".len(),
++ lint_groups
++ .iter()
++ .map(|group| group.len())
++ .map(|len| len + "clippy::".len())
++ .max()
++ .unwrap_or(0),
++ );
++
++ let padded_group = |x: &str| {
++ let mut s = " ".repeat(max_group_name_len - x.chars().count());
++ s.push_str(x);
++ s
++ };
++
++ println!("Lint groups provided by clippy:\n");
++ println!(" {} sub-lints", padded_group("name"));
++ println!(" {} ---------", padded_group("----"));
++ println!(" {} the set of all clippy lints", padded_group("clippy::all"));
++
++ let print_lint_groups = || {
++ for group in lint_groups {
++ let name = group.to_lowercase().replace("_", "-");
++ let desc = lints
++ .iter()
++ .filter(|&lint| lint.group == group)
++ .map(|lint| lint.name)
++ .map(|name| name.replace("_", "-"))
++ .collect::<Vec<String>>()
++ .join(", ");
++ println!(" {} {}", padded_group(&scoped(&name)), desc);
++ }
++ println!("\n");
++ };
++
++ print_lint_groups();
++}
++
++fn display_help() {
++ println!(
++ "\
++Checks a package to catch common mistakes and improve your Rust code.
++
++Usage:
++ cargo clippy [options] [--] [<opts>...]
++
++Common options:
++ -h, --help Print this message
++ -V, --version Print version info and exit
++
++Other options are the same as `cargo check`.
++
++To allow or deny a lint from the command line you can use `cargo clippy --`
++with:
++
++ -W --warn OPT Set lint warnings
++ -A --allow OPT Set lint allowed
++ -D --deny OPT Set lint denied
++ -F --forbid OPT Set lint forbidden
++
++You can use tool lints to allow or deny lints from your code, eg.:
++
++ #[allow(clippy::needless_lifetimes)]
++"
++ );
++}
++
++const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
++
++lazy_static! {
++ static ref ICE_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
++ let hook = panic::take_hook();
++ panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
++ hook
++ };
++}
++
++fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
++ // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
++ (*ICE_HOOK)(info);
++
++ // Separate the output with an empty line
++ eprintln!();
++
++ let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
++ rustc_errors::ColorConfig::Auto,
++ None,
++ false,
++ false,
++ None,
++ false,
++ ));
++ let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
++
++ // a .span_bug or .bug call has already printed what
++ // it wants to print.
++ if !info.payload().is::<rustc_errors::ExplicitBug>() {
++ let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
++ handler.emit_diagnostic(&d);
++ }
++
++ let version_info = rustc_tools_util::get_version_info!();
++
++ let xs: Vec<Cow<'static, str>> = vec![
++ "the compiler unexpectedly panicked. this is a bug.".into(),
++ format!("we would appreciate a bug report: {}", bug_report_url).into(),
++ format!("Clippy version: {}", version_info).into(),
++ ];
++
++ for note in &xs {
++ handler.note_without_error(¬e);
++ }
++
++ // If backtraces are enabled, also print the query stack
++ let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
++
++ if backtrace {
++ TyCtxt::try_print_query_stack(&handler);
++ }
++}
++
++fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
++ home.and_then(|home| {
++ toolchain.map(|toolchain| {
++ let mut path = PathBuf::from(home);
++ path.push("toolchains");
++ path.push(toolchain);
++ path
++ })
++ })
++}
++
++pub fn main() {
++ rustc_driver::init_rustc_env_logger();
++ lazy_static::initialize(&ICE_HOOK);
++ exit(
++ rustc_driver::catch_fatal_errors(move || {
++ let mut orig_args: Vec<String> = env::args().collect();
++
++ if orig_args.iter().any(|a| a == "--version" || a == "-V") {
++ let version_info = rustc_tools_util::get_version_info!();
++ println!("{}", version_info);
++ exit(0);
++ }
++
++ // Get the sysroot, looking from most specific to this invocation to the least:
++ // - command line
++ // - runtime environment
++ // - SYSROOT
++ // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
++ // - sysroot from rustc in the path
++ // - compile-time environment
++ // - SYSROOT
++ // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
++ let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
++ let have_sys_root_arg = sys_root_arg.is_some();
++ let sys_root = sys_root_arg
++ .map(PathBuf::from)
++ .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
++ .or_else(|| {
++ let home = std::env::var("RUSTUP_HOME")
++ .or_else(|_| std::env::var("MULTIRUST_HOME"))
++ .ok();
++ let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
++ .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
++ .ok();
++ toolchain_path(home, toolchain)
++ })
++ .or_else(|| {
++ Command::new("rustc")
++ .arg("--print")
++ .arg("sysroot")
++ .output()
++ .ok()
++ .and_then(|out| String::from_utf8(out.stdout).ok())
++ .map(|s| PathBuf::from(s.trim()))
++ })
++ .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
++ .or_else(|| {
++ let home = option_env!("RUSTUP_HOME")
++ .or(option_env!("MULTIRUST_HOME"))
++ .map(ToString::to_string);
++ let toolchain = option_env!("RUSTUP_TOOLCHAIN")
++ .or(option_env!("MULTIRUST_TOOLCHAIN"))
++ .map(ToString::to_string);
++ toolchain_path(home, toolchain)
++ })
++ .map(|pb| pb.to_string_lossy().to_string())
++ .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
++
++ // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
++ // We're invoking the compiler programmatically, so we ignore this/
++ let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
++
++ if wrapper_mode {
++ // we still want to be able to invoke it normally though
++ orig_args.remove(1);
++ }
++
++ if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
++ display_help();
++ exit(0);
++ }
++
++ let should_describe_lints = || {
++ let args: Vec<_> = env::args().collect();
++ args.windows(2).any(|args| {
++ args[1] == "help"
++ && match args[0].as_str() {
++ "-W" | "-A" | "-D" | "-F" => true,
++ _ => false,
++ }
++ })
++ };
++
++ if !wrapper_mode && should_describe_lints() {
++ describe_lints();
++ exit(0);
++ }
++
++ // this conditional check for the --sysroot flag is there so users can call
++ // `clippy_driver` directly
++ // without having to pass --sysroot or anything
++ let mut args: Vec<String> = orig_args.clone();
++ if !have_sys_root_arg {
++ args.extend(vec!["--sysroot".into(), sys_root]);
++ };
++
++ // this check ensures that dependencies are built but not linted and the final
++ // crate is linted but not built
++ let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
++ || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();
++
++ if clippy_enabled {
++ args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
++ if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
++ args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| {
++ if s.is_empty() {
++ None
++ } else {
++ Some(s.to_string())
++ }
++ }));
++ }
++ }
++ let mut clippy = ClippyCallbacks;
++ let mut default = DefaultCallbacks;
++ let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
++ if clippy_enabled { &mut clippy } else { &mut default };
++ rustc_driver::run_compiler(&args, callbacks, None, None)
++ })
++ .and_then(|result| result)
++ .is_err() as i32,
++ )
++}
--- /dev/null
--- /dev/null
++/// Lint data parsed from the Clippy source code.
++#[derive(Clone, PartialEq, Debug)]
++pub struct Lint {
++ pub name: &'static str,
++ pub group: &'static str,
++ pub desc: &'static str,
++ pub deprecation: Option<&'static str>,
++ pub module: &'static str,
++}
++
++#[derive(PartialOrd, PartialEq, Ord, Eq)]
++pub enum Level {
++ Allow,
++ Warn,
++ Deny,
++}
++
++pub const LINT_LEVELS: [(&str, Level); 8] = [
++ ("correctness", Level::Deny),
++ ("style", Level::Warn),
++ ("complexity", Level::Warn),
++ ("perf", Level::Warn),
++ ("restriction", Level::Allow),
++ ("pedantic", Level::Allow),
++ ("nursery", Level::Allow),
++ ("cargo", Level::Allow),
++];
--- /dev/null
--- /dev/null
++//! This file is managed by `cargo dev update_lints`. Do not edit.
++
++use lazy_static::lazy_static;
++
++pub mod lint;
++pub use lint::Level;
++pub use lint::Lint;
++pub use lint::LINT_LEVELS;
++
++lazy_static! {
++// begin lint list, do not remove this comment, it’s used in `update_lints`
++pub static ref ALL_LINTS: Vec<Lint> = vec![
++ Lint {
++ name: "absurd_extreme_comparisons",
++ group: "correctness",
++ desc: "a comparison with a maximum or minimum value that is always true or false",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "almost_swapped",
++ group: "correctness",
++ desc: "`foo = bar; bar = foo` sequence",
++ deprecation: None,
++ module: "swap",
++ },
++ Lint {
++ name: "approx_constant",
++ group: "correctness",
++ desc: "the approximate of a known float constant (in `std::fXX::consts`)",
++ deprecation: None,
++ module: "approx_const",
++ },
++ Lint {
++ name: "as_conversions",
++ group: "restriction",
++ desc: "using a potentially dangerous silent `as` conversion",
++ deprecation: None,
++ module: "as_conversions",
++ },
++ Lint {
++ name: "assertions_on_constants",
++ group: "style",
++ desc: "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`",
++ deprecation: None,
++ module: "assertions_on_constants",
++ },
++ Lint {
++ name: "assign_op_pattern",
++ group: "style",
++ desc: "assigning the result of an operation on a variable to that same variable",
++ deprecation: None,
++ module: "assign_ops",
++ },
++ Lint {
++ name: "await_holding_lock",
++ group: "pedantic",
++ desc: "Inside an async function, holding a MutexGuard while calling await",
++ deprecation: None,
++ module: "await_holding_lock",
++ },
++ Lint {
++ name: "bad_bit_mask",
++ group: "correctness",
++ desc: "expressions of the form `_ & mask == select` that will only ever return `true` or `false`",
++ deprecation: None,
++ module: "bit_mask",
++ },
++ Lint {
++ name: "blacklisted_name",
++ group: "style",
++ desc: "usage of a blacklisted/placeholder name",
++ deprecation: None,
++ module: "blacklisted_name",
++ },
++ Lint {
++ name: "block_in_if_condition_expr",
++ group: "style",
++ desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`",
++ deprecation: None,
++ module: "block_in_if_condition",
++ },
++ Lint {
++ name: "block_in_if_condition_stmt",
++ group: "style",
++ desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`",
++ deprecation: None,
++ module: "block_in_if_condition",
++ },
++ Lint {
++ name: "bool_comparison",
++ group: "complexity",
++ desc: "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`",
++ deprecation: None,
++ module: "needless_bool",
++ },
++ Lint {
++ name: "borrow_interior_mutable_const",
++ group: "correctness",
++ desc: "referencing `const` with interior mutability",
++ deprecation: None,
++ module: "non_copy_const",
++ },
++ Lint {
++ name: "borrowed_box",
++ group: "complexity",
++ desc: "a borrow of a boxed type",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "box_vec",
++ group: "perf",
++ desc: "usage of `Box<Vec<T>>`, vector elements are already on the heap",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "boxed_local",
++ group: "perf",
++ desc: "using `Box<T>` where unnecessary",
++ deprecation: None,
++ module: "escape",
++ },
++ Lint {
++ name: "builtin_type_shadow",
++ group: "style",
++ desc: "shadowing a builtin type",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "cargo_common_metadata",
++ group: "cargo",
++ desc: "common metadata is defined in `Cargo.toml`",
++ deprecation: None,
++ module: "cargo_common_metadata",
++ },
++ Lint {
++ name: "cast_lossless",
++ group: "pedantic",
++ desc: "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_possible_truncation",
++ group: "pedantic",
++ desc: "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_possible_wrap",
++ group: "pedantic",
++ desc: "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_precision_loss",
++ group: "pedantic",
++ desc: "casts that cause loss of precision, e.g., `x as f32` where `x: u64`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_ptr_alignment",
++ group: "correctness",
++ desc: "cast from a pointer to a more-strictly-aligned pointer",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_ref_to_mut",
++ group: "correctness",
++ desc: "a cast of reference to a mutable pointer",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "cast_sign_loss",
++ group: "pedantic",
++ desc: "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "char_lit_as_u8",
++ group: "complexity",
++ desc: "casting a character literal to `u8` truncates",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "chars_last_cmp",
++ group: "style",
++ desc: "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "chars_next_cmp",
++ group: "style",
++ desc: "using `.chars().next()` to check if a string starts with a char",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "checked_conversions",
++ group: "pedantic",
++ desc: "`try_from` could replace manual bounds checking when casting",
++ deprecation: None,
++ module: "checked_conversions",
++ },
++ Lint {
++ name: "clone_double_ref",
++ group: "correctness",
++ desc: "using `clone` on `&&T`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "clone_on_copy",
++ group: "complexity",
++ desc: "using `clone` on a `Copy` type",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "clone_on_ref_ptr",
++ group: "restriction",
++ desc: "using \'clone\' on a ref-counted pointer",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "cmp_nan",
++ group: "correctness",
++ desc: "comparisons to `NAN`, which will always return false, probably not intended",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "cmp_null",
++ group: "style",
++ desc: "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead.",
++ deprecation: None,
++ module: "ptr",
++ },
++ Lint {
++ name: "cmp_owned",
++ group: "perf",
++ desc: "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "cognitive_complexity",
++ group: "nursery",
++ desc: "functions that should be split up into multiple functions",
++ deprecation: None,
++ module: "cognitive_complexity",
++ },
++ Lint {
++ name: "collapsible_if",
++ group: "style",
++ desc: "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)",
++ deprecation: None,
++ module: "collapsible_if",
++ },
++ Lint {
++ name: "comparison_chain",
++ group: "style",
++ desc: "`if`s that can be rewritten with `match` and `cmp`",
++ deprecation: None,
++ module: "comparison_chain",
++ },
++ Lint {
++ name: "copy_iterator",
++ group: "pedantic",
++ desc: "implementing `Iterator` on a `Copy` type",
++ deprecation: None,
++ module: "copy_iterator",
++ },
++ Lint {
++ name: "crosspointer_transmute",
++ group: "complexity",
++ desc: "transmutes that have to or from types that are a pointer to the other",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "dbg_macro",
++ group: "restriction",
++ desc: "`dbg!` macro is intended as a debugging tool",
++ deprecation: None,
++ module: "dbg_macro",
++ },
++ Lint {
++ name: "debug_assert_with_mut_call",
++ group: "nursery",
++ desc: "mutable arguments in `debug_assert{,_ne,_eq}!`",
++ deprecation: None,
++ module: "mutable_debug_assertion",
++ },
++ Lint {
++ name: "decimal_literal_representation",
++ group: "restriction",
++ desc: "using decimal representation when hexadecimal would be better",
++ deprecation: None,
++ module: "literal_representation",
++ },
++ Lint {
++ name: "declare_interior_mutable_const",
++ group: "correctness",
++ desc: "declaring `const` with interior mutability",
++ deprecation: None,
++ module: "non_copy_const",
++ },
++ Lint {
++ name: "default_trait_access",
++ group: "pedantic",
++ desc: "checks for literal calls to `Default::default()`",
++ deprecation: None,
++ module: "default_trait_access",
++ },
++ Lint {
++ name: "deprecated_cfg_attr",
++ group: "complexity",
++ desc: "usage of `cfg_attr(rustfmt)` instead of tool attributes",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "deprecated_semver",
++ group: "correctness",
++ desc: "use of `#[deprecated(since = \"x\")]` where x is not semver",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "deref_addrof",
++ group: "complexity",
++ desc: "use of `*&` or `*&mut` in an expression",
++ deprecation: None,
++ module: "reference",
++ },
++ Lint {
++ name: "derive_hash_xor_eq",
++ group: "correctness",
++ desc: "deriving `Hash` but implementing `PartialEq` explicitly",
++ deprecation: None,
++ module: "derive",
++ },
++ Lint {
++ name: "diverging_sub_expression",
++ group: "complexity",
++ desc: "whether an expression contains a diverging sub expression",
++ deprecation: None,
++ module: "eval_order_dependence",
++ },
++ Lint {
++ name: "doc_markdown",
++ group: "pedantic",
++ desc: "presence of `_`, `::` or camel-case outside backticks in documentation",
++ deprecation: None,
++ module: "doc",
++ },
++ Lint {
++ name: "double_comparisons",
++ group: "complexity",
++ desc: "unnecessary double comparisons that can be simplified",
++ deprecation: None,
++ module: "double_comparison",
++ },
++ Lint {
++ name: "double_must_use",
++ group: "style",
++ desc: "`#[must_use]` attribute on a `#[must_use]`-returning function / method",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "double_neg",
++ group: "style",
++ desc: "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "double_parens",
++ group: "complexity",
++ desc: "Warn on unnecessary double parentheses",
++ deprecation: None,
++ module: "double_parens",
++ },
++ Lint {
++ name: "drop_bounds",
++ group: "correctness",
++ desc: "Bounds of the form `T: Drop` are useless",
++ deprecation: None,
++ module: "drop_bounds",
++ },
++ Lint {
++ name: "drop_copy",
++ group: "correctness",
++ desc: "calls to `std::mem::drop` with a value that implements Copy",
++ deprecation: None,
++ module: "drop_forget_ref",
++ },
++ Lint {
++ name: "drop_ref",
++ group: "correctness",
++ desc: "calls to `std::mem::drop` with a reference instead of an owned value",
++ deprecation: None,
++ module: "drop_forget_ref",
++ },
++ Lint {
++ name: "duplicate_underscore_argument",
++ group: "style",
++ desc: "function arguments having names which only differ by an underscore",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "duration_subsec",
++ group: "complexity",
++ desc: "checks for calculation of subsecond microseconds or milliseconds",
++ deprecation: None,
++ module: "duration_subsec",
++ },
++ Lint {
++ name: "else_if_without_else",
++ group: "restriction",
++ desc: "`if` expression with an `else if`, but without a final `else` branch",
++ deprecation: None,
++ module: "else_if_without_else",
++ },
++ Lint {
++ name: "empty_enum",
++ group: "pedantic",
++ desc: "enum with no variants",
++ deprecation: None,
++ module: "empty_enum",
++ },
++ Lint {
++ name: "empty_line_after_outer_attr",
++ group: "nursery",
++ desc: "empty line after outer attribute",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "empty_loop",
++ group: "style",
++ desc: "empty `loop {}`, which should block or sleep",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "enum_clike_unportable_variant",
++ group: "correctness",
++ desc: "C-like enums that are `repr(isize/usize)` and have values that don\'t fit into an `i32`",
++ deprecation: None,
++ module: "enum_clike",
++ },
++ Lint {
++ name: "enum_glob_use",
++ group: "pedantic",
++ desc: "use items that import all variants of an enum",
++ deprecation: None,
++ module: "wildcard_imports",
++ },
++ Lint {
++ name: "enum_variant_names",
++ group: "style",
++ desc: "enums where all variants share a prefix/postfix",
++ deprecation: None,
++ module: "enum_variants",
++ },
++ Lint {
++ name: "eq_op",
++ group: "correctness",
++ desc: "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)",
++ deprecation: None,
++ module: "eq_op",
++ },
++ Lint {
++ name: "erasing_op",
++ group: "correctness",
++ desc: "using erasing operations, e.g., `x * 0` or `y & 0`",
++ deprecation: None,
++ module: "erasing_op",
++ },
++ Lint {
++ name: "eval_order_dependence",
++ group: "complexity",
++ desc: "whether a variable read occurs before a write depends on sub-expression evaluation order",
++ deprecation: None,
++ module: "eval_order_dependence",
++ },
++ Lint {
++ name: "excessive_precision",
++ group: "style",
++ desc: "excessive precision for float literal",
++ deprecation: None,
++ module: "float_literal",
++ },
++ Lint {
++ name: "exit",
++ group: "restriction",
++ desc: "`std::process::exit` is called, terminating the program",
++ deprecation: None,
++ module: "exit",
++ },
++ Lint {
++ name: "expect_fun_call",
++ group: "perf",
++ desc: "using any `expect` method with a function call",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "expl_impl_clone_on_copy",
++ group: "pedantic",
++ desc: "implementing `Clone` explicitly on `Copy` types",
++ deprecation: None,
++ module: "derive",
++ },
++ Lint {
++ name: "explicit_counter_loop",
++ group: "complexity",
++ desc: "for-looping with an explicit counter when `_.enumerate()` would do",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "explicit_deref_methods",
++ group: "pedantic",
++ desc: "Explicit use of deref or deref_mut method while not in a method chain.",
++ deprecation: None,
++ module: "dereference",
++ },
++ Lint {
++ name: "explicit_into_iter_loop",
++ group: "pedantic",
++ desc: "for-looping over `_.into_iter()` when `_` would do",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "explicit_iter_loop",
++ group: "pedantic",
++ desc: "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "explicit_write",
++ group: "complexity",
++ desc: "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work",
++ deprecation: None,
++ module: "explicit_write",
++ },
++ Lint {
++ name: "extra_unused_lifetimes",
++ group: "complexity",
++ desc: "unused lifetimes in function definitions",
++ deprecation: None,
++ module: "lifetimes",
++ },
++ Lint {
++ name: "fallible_impl_from",
++ group: "nursery",
++ desc: "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`",
++ deprecation: None,
++ module: "fallible_impl_from",
++ },
++ Lint {
++ name: "filetype_is_file",
++ group: "restriction",
++ desc: "`FileType::is_file` is not recommended to test for readable file type",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "filter_map",
++ group: "pedantic",
++ desc: "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "filter_map_next",
++ group: "pedantic",
++ desc: "using combination of `filter_map` and `next` which can usually be written as a single method call",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "filter_next",
++ group: "complexity",
++ desc: "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "find_map",
++ group: "pedantic",
++ desc: "using a combination of `find` and `map` can usually be written as a single method call",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "flat_map_identity",
++ group: "complexity",
++ desc: "call to `flat_map` where `flatten` is sufficient",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "float_arithmetic",
++ group: "restriction",
++ desc: "any floating-point arithmetic statement",
++ deprecation: None,
++ module: "arithmetic",
++ },
++ Lint {
++ name: "float_cmp",
++ group: "correctness",
++ desc: "using `==` or `!=` on float values instead of comparing difference with an epsilon",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "float_cmp_const",
++ group: "restriction",
++ desc: "using `==` or `!=` on float constants instead of comparing difference with an epsilon",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "fn_address_comparisons",
++ group: "correctness",
++ desc: "comparison with an address of a function item",
++ deprecation: None,
++ module: "unnamed_address",
++ },
++ Lint {
++ name: "fn_params_excessive_bools",
++ group: "pedantic",
++ desc: "using too many bools in function parameters",
++ deprecation: None,
++ module: "excessive_bools",
++ },
++ Lint {
++ name: "fn_to_numeric_cast",
++ group: "style",
++ desc: "casting a function pointer to a numeric type other than usize",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "fn_to_numeric_cast_with_truncation",
++ group: "style",
++ desc: "casting a function pointer to a numeric type not wide enough to store the address",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "for_kv_map",
++ group: "style",
++ desc: "looping on a map using `iter` when `keys` or `values` would do",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "for_loop_over_option",
++ group: "correctness",
++ desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "for_loop_over_result",
++ group: "correctness",
++ desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "forget_copy",
++ group: "correctness",
++ desc: "calls to `std::mem::forget` with a value that implements Copy",
++ deprecation: None,
++ module: "drop_forget_ref",
++ },
++ Lint {
++ name: "forget_ref",
++ group: "correctness",
++ desc: "calls to `std::mem::forget` with a reference instead of an owned value",
++ deprecation: None,
++ module: "drop_forget_ref",
++ },
++ Lint {
++ name: "future_not_send",
++ group: "nursery",
++ desc: "public Futures must be Send",
++ deprecation: None,
++ module: "future_not_send",
++ },
++ Lint {
++ name: "get_last_with_len",
++ group: "complexity",
++ desc: "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler",
++ deprecation: None,
++ module: "get_last_with_len",
++ },
++ Lint {
++ name: "get_unwrap",
++ group: "restriction",
++ desc: "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "identity_conversion",
++ group: "complexity",
++ desc: "using always-identical `Into`/`From`/`IntoIter` conversions",
++ deprecation: None,
++ module: "identity_conversion",
++ },
++ Lint {
++ name: "identity_op",
++ group: "complexity",
++ desc: "using identity operations, e.g., `x + 0` or `y / 1`",
++ deprecation: None,
++ module: "identity_op",
++ },
++ Lint {
++ name: "if_let_mutex",
++ group: "correctness",
++ desc: "locking a `Mutex` in an `if let` block can cause deadlocks",
++ deprecation: None,
++ module: "if_let_mutex",
++ },
++ Lint {
++ name: "if_let_some_result",
++ group: "style",
++ desc: "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead",
++ deprecation: None,
++ module: "if_let_some_result",
++ },
++ Lint {
++ name: "if_not_else",
++ group: "pedantic",
++ desc: "`if` branches that could be swapped so no negation operation is necessary on the condition",
++ deprecation: None,
++ module: "if_not_else",
++ },
++ Lint {
++ name: "if_same_then_else",
++ group: "correctness",
++ desc: "`if` with the same `then` and `else` blocks",
++ deprecation: None,
++ module: "copies",
++ },
++ Lint {
++ name: "ifs_same_cond",
++ group: "correctness",
++ desc: "consecutive `if`s with the same condition",
++ deprecation: None,
++ module: "copies",
++ },
++ Lint {
++ name: "implicit_hasher",
++ group: "pedantic",
++ desc: "missing generalization over different hashers",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "implicit_return",
++ group: "restriction",
++ desc: "use a return statement like `return expr` instead of an expression",
++ deprecation: None,
++ module: "implicit_return",
++ },
++ Lint {
++ name: "implicit_saturating_sub",
++ group: "pedantic",
++ desc: "Perform saturating subtraction instead of implicitly checking lower bound of data type",
++ deprecation: None,
++ module: "implicit_saturating_sub",
++ },
++ Lint {
++ name: "imprecise_flops",
++ group: "nursery",
++ desc: "usage of imprecise floating point operations",
++ deprecation: None,
++ module: "floating_point_arithmetic",
++ },
++ Lint {
++ name: "inconsistent_digit_grouping",
++ group: "style",
++ desc: "integer literals with digits grouped inconsistently",
++ deprecation: None,
++ module: "literal_representation",
++ },
++ Lint {
++ name: "indexing_slicing",
++ group: "restriction",
++ desc: "indexing/slicing usage",
++ deprecation: None,
++ module: "indexing_slicing",
++ },
++ Lint {
++ name: "ineffective_bit_mask",
++ group: "correctness",
++ desc: "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`",
++ deprecation: None,
++ module: "bit_mask",
++ },
++ Lint {
++ name: "inefficient_to_string",
++ group: "pedantic",
++ desc: "using `to_string` on `&&T` where `T: ToString`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "infallible_destructuring_match",
++ group: "style",
++ desc: "a `match` statement with a single infallible arm instead of a `let`",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "infinite_iter",
++ group: "correctness",
++ desc: "infinite iteration",
++ deprecation: None,
++ module: "infinite_iter",
++ },
++ Lint {
++ name: "inherent_to_string",
++ group: "style",
++ desc: "type implements inherent method `to_string()`, but should instead implement the `Display` trait",
++ deprecation: None,
++ module: "inherent_to_string",
++ },
++ Lint {
++ name: "inherent_to_string_shadow_display",
++ group: "correctness",
++ desc: "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait",
++ deprecation: None,
++ module: "inherent_to_string",
++ },
++ Lint {
++ name: "inline_always",
++ group: "pedantic",
++ desc: "use of `#[inline(always)]`",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "inline_fn_without_body",
++ group: "correctness",
++ desc: "use of `#[inline]` on trait methods without bodies",
++ deprecation: None,
++ module: "inline_fn_without_body",
++ },
++ Lint {
++ name: "int_plus_one",
++ group: "complexity",
++ desc: "instead of using `x >= y + 1`, use `x > y`",
++ deprecation: None,
++ module: "int_plus_one",
++ },
++ Lint {
++ name: "integer_arithmetic",
++ group: "restriction",
++ desc: "any integer arithmetic expression which could overflow or panic",
++ deprecation: None,
++ module: "arithmetic",
++ },
++ Lint {
++ name: "integer_division",
++ group: "restriction",
++ desc: "integer division may cause loss of precision",
++ deprecation: None,
++ module: "integer_division",
++ },
++ Lint {
++ name: "into_iter_on_ref",
++ group: "style",
++ desc: "using `.into_iter()` on a reference",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "invalid_atomic_ordering",
++ group: "correctness",
++ desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
++ deprecation: None,
++ module: "atomic_ordering",
++ },
++ Lint {
++ name: "invalid_regex",
++ group: "correctness",
++ desc: "invalid regular expressions",
++ deprecation: None,
++ module: "regex",
++ },
++ Lint {
++ name: "invalid_upcast_comparisons",
++ group: "pedantic",
++ desc: "a comparison involving an upcast which is always true or false",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "items_after_statements",
++ group: "pedantic",
++ desc: "blocks where an item comes after a statement",
++ deprecation: None,
++ module: "items_after_statements",
++ },
++ Lint {
++ name: "iter_cloned_collect",
++ group: "style",
++ desc: "using `.cloned().collect()` on slice to create a `Vec`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "iter_next_loop",
++ group: "correctness",
++ desc: "for-looping over `_.next()` which is probably not intended",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "iter_nth",
++ group: "perf",
++ desc: "using `.iter().nth()` on a standard library type with O(1) element access",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "iter_nth_zero",
++ group: "style",
++ desc: "replace `iter.nth(0)` with `iter.next()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "iter_skip_next",
++ group: "style",
++ desc: "using `.skip(x).next()` on an iterator",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "iterator_step_by_zero",
++ group: "correctness",
++ desc: "using `Iterator::step_by(0)`, which will panic at runtime",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "just_underscores_and_digits",
++ group: "style",
++ desc: "unclear name",
++ deprecation: None,
++ module: "non_expressive_names",
++ },
++ Lint {
++ name: "large_const_arrays",
++ group: "perf",
++ desc: "large non-scalar const array may cause performance overhead",
++ deprecation: None,
++ module: "large_const_arrays",
++ },
++ Lint {
++ name: "large_digit_groups",
++ group: "pedantic",
++ desc: "grouping digits into groups that are too large",
++ deprecation: None,
++ module: "literal_representation",
++ },
++ Lint {
++ name: "large_enum_variant",
++ group: "perf",
++ desc: "large size difference between variants on an enum",
++ deprecation: None,
++ module: "large_enum_variant",
++ },
++ Lint {
++ name: "large_stack_arrays",
++ group: "pedantic",
++ desc: "allocating large arrays on stack may cause stack overflow",
++ deprecation: None,
++ module: "large_stack_arrays",
++ },
++ Lint {
++ name: "len_without_is_empty",
++ group: "style",
++ desc: "traits or impls with a public `len` method but no corresponding `is_empty` method",
++ deprecation: None,
++ module: "len_zero",
++ },
++ Lint {
++ name: "len_zero",
++ group: "style",
++ desc: "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead",
++ deprecation: None,
++ module: "len_zero",
++ },
++ Lint {
++ name: "let_and_return",
++ group: "style",
++ desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
++ deprecation: None,
++ module: "returns",
++ },
++ Lint {
++ name: "let_underscore_lock",
++ group: "correctness",
++ desc: "non-binding let on a synchronization lock",
++ deprecation: None,
++ module: "let_underscore",
++ },
++ Lint {
++ name: "let_underscore_must_use",
++ group: "restriction",
++ desc: "non-binding let on a `#[must_use]` expression",
++ deprecation: None,
++ module: "let_underscore",
++ },
++ Lint {
++ name: "let_unit_value",
++ group: "pedantic",
++ desc: "creating a `let` binding to a value of unit type, which usually can\'t be used afterwards",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "linkedlist",
++ group: "pedantic",
++ desc: "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "logic_bug",
++ group: "correctness",
++ desc: "boolean expressions that contain terminals which can be eliminated",
++ deprecation: None,
++ module: "booleans",
++ },
++ Lint {
++ name: "lossy_float_literal",
++ group: "restriction",
++ desc: "lossy whole number float literals",
++ deprecation: None,
++ module: "float_literal",
++ },
++ Lint {
++ name: "macro_use_imports",
++ group: "pedantic",
++ desc: "#[macro_use] is no longer needed",
++ deprecation: None,
++ module: "macro_use",
++ },
++ Lint {
++ name: "main_recursion",
++ group: "style",
++ desc: "recursion using the entrypoint",
++ deprecation: None,
++ module: "main_recursion",
++ },
++ Lint {
++ name: "manual_memcpy",
++ group: "perf",
++ desc: "manually copying items between slices",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "manual_saturating_arithmetic",
++ group: "style",
++ desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "manual_swap",
++ group: "complexity",
++ desc: "manual swap of two variables",
++ deprecation: None,
++ module: "swap",
++ },
++ Lint {
++ name: "many_single_char_names",
++ group: "style",
++ desc: "too many single character bindings",
++ deprecation: None,
++ module: "non_expressive_names",
++ },
++ Lint {
++ name: "map_clone",
++ group: "style",
++ desc: "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types",
++ deprecation: None,
++ module: "map_clone",
++ },
++ Lint {
++ name: "map_entry",
++ group: "perf",
++ desc: "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`",
++ deprecation: None,
++ module: "entry",
++ },
++ Lint {
++ name: "map_flatten",
++ group: "pedantic",
++ desc: "using combinations of `flatten` and `map` which can usually be written as a single method call",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "match_as_ref",
++ group: "complexity",
++ desc: "a `match` on an Option value instead of using `as_ref()` or `as_mut`",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "match_bool",
++ group: "pedantic",
++ desc: "a `match` on a boolean expression instead of an `if..else` block",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "match_on_vec_items",
++ group: "correctness",
++ desc: "matching on vector elements can panic",
++ deprecation: None,
++ module: "match_on_vec_items",
++ },
++ Lint {
++ name: "match_overlapping_arm",
++ group: "style",
++ desc: "a `match` with overlapping arms",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "match_ref_pats",
++ group: "style",
++ desc: "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "match_same_arms",
++ group: "pedantic",
++ desc: "`match` with identical arm bodies",
++ deprecation: None,
++ module: "copies",
++ },
++ Lint {
++ name: "match_single_binding",
++ group: "complexity",
++ desc: "a match with a single binding instead of using `let` statement",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "match_wild_err_arm",
++ group: "style",
++ desc: "a `match` with `Err(_)` arm and take drastic actions",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "maybe_infinite_iter",
++ group: "pedantic",
++ desc: "possible infinite iteration",
++ deprecation: None,
++ module: "infinite_iter",
++ },
++ Lint {
++ name: "mem_discriminant_non_enum",
++ group: "correctness",
++ desc: "calling `mem::descriminant` on non-enum type",
++ deprecation: None,
++ module: "mem_discriminant",
++ },
++ Lint {
++ name: "mem_forget",
++ group: "restriction",
++ desc: "`mem::forget` usage on `Drop` types, likely to cause memory leaks",
++ deprecation: None,
++ module: "mem_forget",
++ },
++ Lint {
++ name: "mem_replace_option_with_none",
++ group: "style",
++ desc: "replacing an `Option` with `None` instead of `take()`",
++ deprecation: None,
++ module: "mem_replace",
++ },
++ Lint {
++ name: "mem_replace_with_default",
++ group: "style",
++ desc: "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`",
++ deprecation: None,
++ module: "mem_replace",
++ },
++ Lint {
++ name: "mem_replace_with_uninit",
++ group: "correctness",
++ desc: "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`",
++ deprecation: None,
++ module: "mem_replace",
++ },
++ Lint {
++ name: "min_max",
++ group: "correctness",
++ desc: "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant",
++ deprecation: None,
++ module: "minmax",
++ },
++ Lint {
++ name: "mismatched_target_os",
++ group: "correctness",
++ desc: "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "misrefactored_assign_op",
++ group: "complexity",
++ desc: "having a variable on both sides of an assign op",
++ deprecation: None,
++ module: "assign_ops",
++ },
++ Lint {
++ name: "missing_const_for_fn",
++ group: "nursery",
++ desc: "Lint functions definitions that could be made `const fn`",
++ deprecation: None,
++ module: "missing_const_for_fn",
++ },
++ Lint {
++ name: "missing_docs_in_private_items",
++ group: "restriction",
++ desc: "detects missing documentation for public and private members",
++ deprecation: None,
++ module: "missing_doc",
++ },
++ Lint {
++ name: "missing_errors_doc",
++ group: "pedantic",
++ desc: "`pub fn` returns `Result` without `# Errors` in doc comment",
++ deprecation: None,
++ module: "doc",
++ },
++ Lint {
++ name: "missing_inline_in_public_items",
++ group: "restriction",
++ desc: "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)",
++ deprecation: None,
++ module: "missing_inline",
++ },
++ Lint {
++ name: "missing_safety_doc",
++ group: "style",
++ desc: "`pub unsafe fn` without `# Safety` docs",
++ deprecation: None,
++ module: "doc",
++ },
++ Lint {
++ name: "mistyped_literal_suffixes",
++ group: "correctness",
++ desc: "mistyped literal suffix",
++ deprecation: None,
++ module: "literal_representation",
++ },
++ Lint {
++ name: "mixed_case_hex_literals",
++ group: "style",
++ desc: "hex literals whose letter digits are not consistently upper- or lowercased",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "module_inception",
++ group: "style",
++ desc: "modules that have the same name as their parent module",
++ deprecation: None,
++ module: "enum_variants",
++ },
++ Lint {
++ name: "module_name_repetitions",
++ group: "pedantic",
++ desc: "type names prefixed/postfixed with their containing module\'s name",
++ deprecation: None,
++ module: "enum_variants",
++ },
++ Lint {
++ name: "modulo_arithmetic",
++ group: "restriction",
++ desc: "any modulo arithmetic statement",
++ deprecation: None,
++ module: "modulo_arithmetic",
++ },
++ Lint {
++ name: "modulo_one",
++ group: "correctness",
++ desc: "taking a number modulo 1, which always returns 0",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "multiple_crate_versions",
++ group: "cargo",
++ desc: "multiple versions of the same crate being used",
++ deprecation: None,
++ module: "multiple_crate_versions",
++ },
++ Lint {
++ name: "multiple_inherent_impl",
++ group: "restriction",
++ desc: "Multiple inherent impl that could be grouped",
++ deprecation: None,
++ module: "inherent_impl",
++ },
++ Lint {
++ name: "must_use_candidate",
++ group: "pedantic",
++ desc: "function or method that could take a `#[must_use]` attribute",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "must_use_unit",
++ group: "style",
++ desc: "`#[must_use]` attribute on a unit-returning function / method",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "mut_from_ref",
++ group: "correctness",
++ desc: "fns that create mutable refs from immutable ref args",
++ deprecation: None,
++ module: "ptr",
++ },
++ Lint {
++ name: "mut_mut",
++ group: "pedantic",
++ desc: "usage of double-mut refs, e.g., `&mut &mut ...`",
++ deprecation: None,
++ module: "mut_mut",
++ },
++ Lint {
++ name: "mut_range_bound",
++ group: "complexity",
++ desc: "for loop over a range where one of the bounds is a mutable variable",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "mutable_key_type",
++ group: "correctness",
++ desc: "Check for mutable `Map`/`Set` key type",
++ deprecation: None,
++ module: "mut_key",
++ },
++ Lint {
++ name: "mutex_atomic",
++ group: "perf",
++ desc: "using a mutex where an atomic value could be used instead",
++ deprecation: None,
++ module: "mutex_atomic",
++ },
++ Lint {
++ name: "mutex_integer",
++ group: "nursery",
++ desc: "using a mutex for an integer type",
++ deprecation: None,
++ module: "mutex_atomic",
++ },
++ Lint {
++ name: "naive_bytecount",
++ group: "perf",
++ desc: "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values",
++ deprecation: None,
++ module: "bytecount",
++ },
++ Lint {
++ name: "needless_bool",
++ group: "complexity",
++ desc: "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`",
++ deprecation: None,
++ module: "needless_bool",
++ },
++ Lint {
++ name: "needless_borrow",
++ group: "nursery",
++ desc: "taking a reference that is going to be automatically dereferenced",
++ deprecation: None,
++ module: "needless_borrow",
++ },
++ Lint {
++ name: "needless_borrowed_reference",
++ group: "complexity",
++ desc: "taking a needless borrowed reference",
++ deprecation: None,
++ module: "needless_borrowed_ref",
++ },
++ Lint {
++ name: "needless_collect",
++ group: "perf",
++ desc: "collecting an iterator when collect is not needed",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "needless_continue",
++ group: "pedantic",
++ desc: "`continue` statements that can be replaced by a rearrangement of code",
++ deprecation: None,
++ module: "needless_continue",
++ },
++ Lint {
++ name: "needless_doctest_main",
++ group: "style",
++ desc: "presence of `fn main() {` in code examples",
++ deprecation: None,
++ module: "doc",
++ },
++ Lint {
++ name: "needless_lifetimes",
++ group: "complexity",
++ desc: "using explicit lifetimes for references in function arguments when elision rules would allow omitting them",
++ deprecation: None,
++ module: "lifetimes",
++ },
++ Lint {
++ name: "needless_pass_by_value",
++ group: "pedantic",
++ desc: "functions taking arguments by value, but not consuming them in its body",
++ deprecation: None,
++ module: "needless_pass_by_value",
++ },
++ Lint {
++ name: "needless_range_loop",
++ group: "style",
++ desc: "for-looping over a range of indices where an iterator over items would do",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "needless_return",
++ group: "style",
++ desc: "using a return statement like `return expr;` where an expression would suffice",
++ deprecation: None,
++ module: "returns",
++ },
++ Lint {
++ name: "needless_update",
++ group: "complexity",
++ desc: "using `Foo { ..base }` when there are no missing fields",
++ deprecation: None,
++ module: "needless_update",
++ },
++ Lint {
++ name: "neg_cmp_op_on_partial_ord",
++ group: "complexity",
++ desc: "The use of negated comparison operators on partially ordered types may produce confusing code.",
++ deprecation: None,
++ module: "neg_cmp_op_on_partial_ord",
++ },
++ Lint {
++ name: "neg_multiply",
++ group: "style",
++ desc: "multiplying integers with `-1`",
++ deprecation: None,
++ module: "neg_multiply",
++ },
++ Lint {
++ name: "never_loop",
++ group: "correctness",
++ desc: "any loop that will always `break` or `return`",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "new_ret_no_self",
++ group: "style",
++ desc: "not returning type containing `Self` in a `new` method",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "new_without_default",
++ group: "style",
++ desc: "`fn new() -> Self` method without `Default` implementation",
++ deprecation: None,
++ module: "new_without_default",
++ },
++ Lint {
++ name: "no_effect",
++ group: "complexity",
++ desc: "statements with no effect",
++ deprecation: None,
++ module: "no_effect",
++ },
++ Lint {
++ name: "non_ascii_literal",
++ group: "pedantic",
++ desc: "using any literal non-ASCII chars in a string literal instead of using the `\\\\u` escape",
++ deprecation: None,
++ module: "unicode",
++ },
++ Lint {
++ name: "nonminimal_bool",
++ group: "complexity",
++ desc: "boolean expressions that can be written more concisely",
++ deprecation: None,
++ module: "booleans",
++ },
++ Lint {
++ name: "nonsensical_open_options",
++ group: "correctness",
++ desc: "nonsensical combination of options for opening a file",
++ deprecation: None,
++ module: "open_options",
++ },
++ Lint {
++ name: "not_unsafe_ptr_arg_deref",
++ group: "correctness",
++ desc: "public functions dereferencing raw pointer arguments but not marked `unsafe`",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "ok_expect",
++ group: "style",
++ desc: "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "op_ref",
++ group: "style",
++ desc: "taking a reference to satisfy the type constraints on `==`",
++ deprecation: None,
++ module: "eq_op",
++ },
++ Lint {
++ name: "option_and_then_some",
++ group: "complexity",
++ desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_as_ref_deref",
++ group: "complexity",
++ desc: "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_env_unwrap",
++ group: "correctness",
++ desc: "using `option_env!(...).unwrap()` to get environment variable",
++ deprecation: None,
++ module: "option_env_unwrap",
++ },
++ Lint {
++ name: "option_expect_used",
++ group: "restriction",
++ desc: "using `Option.expect()`, which might be better handled",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_map_or_none",
++ group: "style",
++ desc: "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_map_unit_fn",
++ group: "complexity",
++ desc: "using `option.map(f)`, where `f` is a function or closure that returns `()`",
++ deprecation: None,
++ module: "map_unit_fn",
++ },
++ Lint {
++ name: "option_map_unwrap_or",
++ group: "pedantic",
++ desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_map_unwrap_or_else",
++ group: "pedantic",
++ desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "option_option",
++ group: "pedantic",
++ desc: "usage of `Option<Option<T>>`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "option_unwrap_used",
++ group: "restriction",
++ desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "or_fun_call",
++ group: "perf",
++ desc: "using any `*or` method with a function call, which suggests `*or_else`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "out_of_bounds_indexing",
++ group: "correctness",
++ desc: "out of bounds constant indexing",
++ deprecation: None,
++ module: "indexing_slicing",
++ },
++ Lint {
++ name: "overflow_check_conditional",
++ group: "complexity",
++ desc: "overflow checks inspired by C which are likely to panic",
++ deprecation: None,
++ module: "overflow_check_conditional",
++ },
++ Lint {
++ name: "panic",
++ group: "restriction",
++ desc: "usage of the `panic!` macro",
++ deprecation: None,
++ module: "panic_unimplemented",
++ },
++ Lint {
++ name: "panic_params",
++ group: "style",
++ desc: "missing parameters in `panic!` calls",
++ deprecation: None,
++ module: "panic_unimplemented",
++ },
++ Lint {
++ name: "panicking_unwrap",
++ group: "correctness",
++ desc: "checks for calls of `unwrap[_err]()` that will always fail",
++ deprecation: None,
++ module: "unwrap",
++ },
++ Lint {
++ name: "partialeq_ne_impl",
++ group: "complexity",
++ desc: "re-implementing `PartialEq::ne`",
++ deprecation: None,
++ module: "partialeq_ne_impl",
++ },
++ Lint {
++ name: "path_buf_push_overwrite",
++ group: "nursery",
++ desc: "calling `push` with file system root on `PathBuf` can overwrite it",
++ deprecation: None,
++ module: "path_buf_push_overwrite",
++ },
++ Lint {
++ name: "possible_missing_comma",
++ group: "correctness",
++ desc: "possible missing comma in array",
++ deprecation: None,
++ module: "formatting",
++ },
++ Lint {
++ name: "precedence",
++ group: "complexity",
++ desc: "operations where precedence may be unclear",
++ deprecation: None,
++ module: "precedence",
++ },
++ Lint {
++ name: "print_literal",
++ group: "style",
++ desc: "printing a literal with a format string",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "print_stdout",
++ group: "restriction",
++ desc: "printing on stdout",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "print_with_newline",
++ group: "style",
++ desc: "using `print!()` with a format string that ends in a single newline",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "println_empty_string",
++ group: "style",
++ desc: "using `println!(\"\")` with an empty string",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "ptr_arg",
++ group: "style",
++ desc: "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively",
++ deprecation: None,
++ module: "ptr",
++ },
++ Lint {
++ name: "ptr_offset_with_cast",
++ group: "complexity",
++ desc: "unneeded pointer offset cast",
++ deprecation: None,
++ module: "ptr_offset_with_cast",
++ },
++ Lint {
++ name: "pub_enum_variant_names",
++ group: "pedantic",
++ desc: "enums where all variants share a prefix/postfix",
++ deprecation: None,
++ module: "enum_variants",
++ },
++ Lint {
++ name: "question_mark",
++ group: "style",
++ desc: "checks for expressions that could be replaced by the question mark operator",
++ deprecation: None,
++ module: "question_mark",
++ },
++ Lint {
++ name: "range_minus_one",
++ group: "complexity",
++ desc: "`x..=(y-1)` reads better as `x..y`",
++ deprecation: None,
++ module: "ranges",
++ },
++ Lint {
++ name: "range_plus_one",
++ group: "pedantic",
++ desc: "`x..(y+1)` reads better as `x..=y`",
++ deprecation: None,
++ module: "ranges",
++ },
++ Lint {
++ name: "range_zip_with_len",
++ group: "complexity",
++ desc: "zipping iterator with a range when `enumerate()` would do",
++ deprecation: None,
++ module: "ranges",
++ },
++ Lint {
++ name: "redundant_allocation",
++ group: "perf",
++ desc: "redundant allocation",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "redundant_clone",
++ group: "perf",
++ desc: "`clone()` of an owned value that is going to be dropped immediately",
++ deprecation: None,
++ module: "redundant_clone",
++ },
++ Lint {
++ name: "redundant_closure",
++ group: "style",
++ desc: "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)",
++ deprecation: None,
++ module: "eta_reduction",
++ },
++ Lint {
++ name: "redundant_closure_call",
++ group: "complexity",
++ desc: "throwaway closures called in the expression they are defined",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "redundant_closure_for_method_calls",
++ group: "pedantic",
++ desc: "redundant closures for method calls",
++ deprecation: None,
++ module: "eta_reduction",
++ },
++ Lint {
++ name: "redundant_field_names",
++ group: "style",
++ desc: "checks for fields in struct literals where shorthands could be used",
++ deprecation: None,
++ module: "redundant_field_names",
++ },
++ Lint {
++ name: "redundant_pattern",
++ group: "style",
++ desc: "using `name @ _` in a pattern",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "redundant_pattern_matching",
++ group: "style",
++ desc: "use the proper utility function avoiding an `if let`",
++ deprecation: None,
++ module: "redundant_pattern_matching",
++ },
++ Lint {
++ name: "redundant_pub_crate",
++ group: "nursery",
++ desc: "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them.",
++ deprecation: None,
++ module: "redundant_pub_crate",
++ },
++ Lint {
++ name: "redundant_static_lifetimes",
++ group: "style",
++ desc: "Using explicit `\'static` lifetime for constants or statics when elision rules would allow omitting them.",
++ deprecation: None,
++ module: "redundant_static_lifetimes",
++ },
++ Lint {
++ name: "ref_in_deref",
++ group: "complexity",
++ desc: "Use of reference in auto dereference expression.",
++ deprecation: None,
++ module: "reference",
++ },
++ Lint {
++ name: "regex_macro",
++ group: "style",
++ desc: "use of `regex!(_)` instead of `Regex::new(_)`",
++ deprecation: None,
++ module: "regex",
++ },
++ Lint {
++ name: "rest_pat_in_fully_bound_structs",
++ group: "restriction",
++ desc: "a match on a struct that binds all fields but still uses the wildcard pattern",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "result_expect_used",
++ group: "restriction",
++ desc: "using `Result.expect()`, which might be better handled",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "result_map_or_into_option",
++ group: "style",
++ desc: "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "result_map_unit_fn",
++ group: "complexity",
++ desc: "using `result.map(f)`, where `f` is a function or closure that returns `()`",
++ deprecation: None,
++ module: "map_unit_fn",
++ },
++ Lint {
++ name: "result_map_unwrap_or_else",
++ group: "pedantic",
++ desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "result_unwrap_used",
++ group: "restriction",
++ desc: "using `Result.unwrap()`, which might be better handled",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "reverse_range_loop",
++ group: "correctness",
++ desc: "iteration over an empty range, such as `10..0` or `5..5`",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "same_functions_in_if_condition",
++ group: "pedantic",
++ desc: "consecutive `if`s with the same function call",
++ deprecation: None,
++ module: "copies",
++ },
++ Lint {
++ name: "search_is_some",
++ group: "complexity",
++ desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "serde_api_misuse",
++ group: "correctness",
++ desc: "various things that will negatively affect your serde experience",
++ deprecation: None,
++ module: "serde_api",
++ },
++ Lint {
++ name: "shadow_reuse",
++ group: "restriction",
++ desc: "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`",
++ deprecation: None,
++ module: "shadow",
++ },
++ Lint {
++ name: "shadow_same",
++ group: "restriction",
++ desc: "rebinding a name to itself, e.g., `let mut x = &mut x`",
++ deprecation: None,
++ module: "shadow",
++ },
++ Lint {
++ name: "shadow_unrelated",
++ group: "pedantic",
++ desc: "rebinding a name without even using the original value",
++ deprecation: None,
++ module: "shadow",
++ },
++ Lint {
++ name: "short_circuit_statement",
++ group: "complexity",
++ desc: "using a short circuit boolean condition as a statement",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "should_implement_trait",
++ group: "style",
++ desc: "defining a method that should be implementing a std trait",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "similar_names",
++ group: "pedantic",
++ desc: "similarly named items and bindings",
++ deprecation: None,
++ module: "non_expressive_names",
++ },
++ Lint {
++ name: "single_char_pattern",
++ group: "perf",
++ desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "single_component_path_imports",
++ group: "style",
++ desc: "imports with single component path are redundant",
++ deprecation: None,
++ module: "single_component_path_imports",
++ },
++ Lint {
++ name: "single_match",
++ group: "style",
++ desc: "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "single_match_else",
++ group: "pedantic",
++ desc: "a `match` statement with two arms where the second arm\'s pattern is a placeholder instead of a specific match pattern",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "skip_while_next",
++ group: "complexity",
++ desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "slow_vector_initialization",
++ group: "perf",
++ desc: "slow vector initialization",
++ deprecation: None,
++ module: "slow_vector_initialization",
++ },
++ Lint {
++ name: "string_add",
++ group: "restriction",
++ desc: "using `x + ..` where x is a `String` instead of `push_str()`",
++ deprecation: None,
++ module: "strings",
++ },
++ Lint {
++ name: "string_add_assign",
++ group: "pedantic",
++ desc: "using `x = x + ..` where x is a `String` instead of `push_str()`",
++ deprecation: None,
++ module: "strings",
++ },
++ Lint {
++ name: "string_extend_chars",
++ group: "style",
++ desc: "using `x.extend(s.chars())` where s is a `&str` or `String`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "string_lit_as_bytes",
++ group: "style",
++ desc: "calling `as_bytes` on a string literal instead of using a byte string literal",
++ deprecation: None,
++ module: "strings",
++ },
++ Lint {
++ name: "struct_excessive_bools",
++ group: "pedantic",
++ desc: "using too many bools in a struct",
++ deprecation: None,
++ module: "excessive_bools",
++ },
++ Lint {
++ name: "suboptimal_flops",
++ group: "nursery",
++ desc: "usage of sub-optimal floating point operations",
++ deprecation: None,
++ module: "floating_point_arithmetic",
++ },
++ Lint {
++ name: "suspicious_arithmetic_impl",
++ group: "correctness",
++ desc: "suspicious use of operators in impl of arithmetic trait",
++ deprecation: None,
++ module: "suspicious_trait_impl",
++ },
++ Lint {
++ name: "suspicious_assignment_formatting",
++ group: "style",
++ desc: "suspicious formatting of `*=`, `-=` or `!=`",
++ deprecation: None,
++ module: "formatting",
++ },
++ Lint {
++ name: "suspicious_else_formatting",
++ group: "style",
++ desc: "suspicious formatting of `else`",
++ deprecation: None,
++ module: "formatting",
++ },
++ Lint {
++ name: "suspicious_map",
++ group: "complexity",
++ desc: "suspicious usage of map",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "suspicious_op_assign_impl",
++ group: "correctness",
++ desc: "suspicious use of operators in impl of OpAssign trait",
++ deprecation: None,
++ module: "suspicious_trait_impl",
++ },
++ Lint {
++ name: "suspicious_unary_op_formatting",
++ group: "style",
++ desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp",
++ deprecation: None,
++ module: "formatting",
++ },
++ Lint {
++ name: "tabs_in_doc_comments",
++ group: "style",
++ desc: "using tabs in doc comments is not recommended",
++ deprecation: None,
++ module: "tabs_in_doc_comments",
++ },
++ Lint {
++ name: "temporary_assignment",
++ group: "complexity",
++ desc: "assignments to temporaries",
++ deprecation: None,
++ module: "temporary_assignment",
++ },
++ Lint {
++ name: "temporary_cstring_as_ptr",
++ group: "correctness",
++ desc: "getting the inner pointer of a temporary `CString`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "to_digit_is_some",
++ group: "style",
++ desc: "`char.is_digit()` is clearer",
++ deprecation: None,
++ module: "to_digit_is_some",
++ },
++ Lint {
++ name: "todo",
++ group: "restriction",
++ desc: "`todo!` should not be present in production code",
++ deprecation: None,
++ module: "panic_unimplemented",
++ },
++ Lint {
++ name: "too_many_arguments",
++ group: "complexity",
++ desc: "functions with too many arguments",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "too_many_lines",
++ group: "pedantic",
++ desc: "functions with too many lines",
++ deprecation: None,
++ module: "functions",
++ },
++ Lint {
++ name: "toplevel_ref_arg",
++ group: "style",
++ desc: "an entire binding declared as `ref`, in a function argument or a `let` statement",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "transmute_bytes_to_str",
++ group: "complexity",
++ desc: "transmutes from a `&[u8]` to a `&str`",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_float_to_int",
++ group: "complexity",
++ desc: "transmutes from a float to an integer",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_int_to_bool",
++ group: "complexity",
++ desc: "transmutes from an integer to a `bool`",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_int_to_char",
++ group: "complexity",
++ desc: "transmutes from an integer to a `char`",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_int_to_float",
++ group: "complexity",
++ desc: "transmutes from an integer to a float",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_ptr_to_ptr",
++ group: "complexity",
++ desc: "transmutes from a pointer to a pointer / a reference to a reference",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmute_ptr_to_ref",
++ group: "complexity",
++ desc: "transmutes from a pointer to a reference type",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "transmuting_null",
++ group: "correctness",
++ desc: "transmutes from a null pointer to a reference, which is undefined behavior",
++ deprecation: None,
++ module: "transmuting_null",
++ },
++ Lint {
++ name: "trivial_regex",
++ group: "style",
++ desc: "trivial regular expressions",
++ deprecation: None,
++ module: "regex",
++ },
++ Lint {
++ name: "trivially_copy_pass_by_ref",
++ group: "pedantic",
++ desc: "functions taking small copyable arguments by reference",
++ deprecation: None,
++ module: "trivially_copy_pass_by_ref",
++ },
++ Lint {
++ name: "try_err",
++ group: "style",
++ desc: "return errors explicitly rather than hiding them behind a `?`",
++ deprecation: None,
++ module: "try_err",
++ },
++ Lint {
++ name: "type_complexity",
++ group: "complexity",
++ desc: "usage of very complex types that might be better factored into `type` definitions",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "type_repetition_in_bounds",
++ group: "pedantic",
++ desc: "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`",
++ deprecation: None,
++ module: "trait_bounds",
++ },
++ Lint {
++ name: "unicode_not_nfc",
++ group: "pedantic",
++ desc: "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)",
++ deprecation: None,
++ module: "unicode",
++ },
++ Lint {
++ name: "unimplemented",
++ group: "restriction",
++ desc: "`unimplemented!` should not be present in production code",
++ deprecation: None,
++ module: "panic_unimplemented",
++ },
++ Lint {
++ name: "uninit_assumed_init",
++ group: "correctness",
++ desc: "`MaybeUninit::uninit().assume_init()`",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "unit_arg",
++ group: "complexity",
++ desc: "passing unit to a function",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "unit_cmp",
++ group: "correctness",
++ desc: "comparing unit values",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "unknown_clippy_lints",
++ group: "style",
++ desc: "unknown_lints for scoped Clippy lints",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "unnecessary_cast",
++ group: "complexity",
++ desc: "cast to the same type, e.g., `x as i32` where `x: i32`",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "unnecessary_filter_map",
++ group: "complexity",
++ desc: "using `filter_map` when a more succinct alternative exists",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "unnecessary_fold",
++ group: "style",
++ desc: "using `fold` when a more succinct alternative exists",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "unnecessary_mut_passed",
++ group: "style",
++ desc: "an argument passed as a mutable reference although the callee only demands an immutable reference",
++ deprecation: None,
++ module: "mut_reference",
++ },
++ Lint {
++ name: "unnecessary_operation",
++ group: "complexity",
++ desc: "outer expressions with no effect",
++ deprecation: None,
++ module: "no_effect",
++ },
++ Lint {
++ name: "unnecessary_unwrap",
++ group: "complexity",
++ desc: "checks for calls of `unwrap[_err]()` that cannot fail",
++ deprecation: None,
++ module: "unwrap",
++ },
++ Lint {
++ name: "unneeded_field_pattern",
++ group: "restriction",
++ desc: "struct fields bound to a wildcard instead of using `..`",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "unneeded_wildcard_pattern",
++ group: "complexity",
++ desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "unreachable",
++ group: "restriction",
++ desc: "`unreachable!` should not be present in production code",
++ deprecation: None,
++ module: "panic_unimplemented",
++ },
++ Lint {
++ name: "unreadable_literal",
++ group: "pedantic",
++ desc: "long integer literal without underscores",
++ deprecation: None,
++ module: "literal_representation",
++ },
++ Lint {
++ name: "unsafe_derive_deserialize",
++ group: "pedantic",
++ desc: "deriving `serde::Deserialize` on a type that has methods using `unsafe`",
++ deprecation: None,
++ module: "derive",
++ },
++ Lint {
++ name: "unsafe_removed_from_name",
++ group: "style",
++ desc: "`unsafe` removed from API names on import",
++ deprecation: None,
++ module: "unsafe_removed_from_name",
++ },
++ Lint {
++ name: "unseparated_literal_suffix",
++ group: "pedantic",
++ desc: "literals whose suffix is not separated by an underscore",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "unsound_collection_transmute",
++ group: "correctness",
++ desc: "transmute between collections of layout-incompatible types",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "unused_io_amount",
++ group: "correctness",
++ desc: "unused written/read amount",
++ deprecation: None,
++ module: "unused_io_amount",
++ },
++ Lint {
++ name: "unused_self",
++ group: "pedantic",
++ desc: "methods that contain a `self` argument but don\'t use it",
++ deprecation: None,
++ module: "unused_self",
++ },
++ Lint {
++ name: "unused_unit",
++ group: "style",
++ desc: "needless unit expression",
++ deprecation: None,
++ module: "returns",
++ },
++ Lint {
++ name: "use_debug",
++ group: "restriction",
++ desc: "use of `Debug`-based formatting",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "use_self",
++ group: "nursery",
++ desc: "Unnecessary structure name repetition whereas `Self` is applicable",
++ deprecation: None,
++ module: "use_self",
++ },
++ Lint {
++ name: "used_underscore_binding",
++ group: "pedantic",
++ desc: "using a binding which is prefixed with an underscore",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "useless_asref",
++ group: "complexity",
++ desc: "using `as_ref` where the types before and after the call are the same",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "useless_attribute",
++ group: "correctness",
++ desc: "use of lint attributes on `extern crate` items",
++ deprecation: None,
++ module: "attrs",
++ },
++ Lint {
++ name: "useless_format",
++ group: "complexity",
++ desc: "useless use of `format!`",
++ deprecation: None,
++ module: "format",
++ },
++ Lint {
++ name: "useless_let_if_seq",
++ group: "style",
++ desc: "unidiomatic `let mut` declaration followed by initialization in `if`",
++ deprecation: None,
++ module: "let_if_seq",
++ },
++ Lint {
++ name: "useless_transmute",
++ group: "nursery",
++ desc: "transmutes that have the same to and from types or could be a cast/coercion",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "useless_vec",
++ group: "perf",
++ desc: "useless `vec!`",
++ deprecation: None,
++ module: "vec",
++ },
++ Lint {
++ name: "vec_box",
++ group: "complexity",
++ desc: "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap",
++ deprecation: None,
++ module: "types",
++ },
++ Lint {
++ name: "verbose_bit_mask",
++ group: "style",
++ desc: "expressions where a bit mask is less readable than the corresponding method call",
++ deprecation: None,
++ module: "bit_mask",
++ },
++ Lint {
++ name: "verbose_file_reads",
++ group: "restriction",
++ desc: "use of `File::read_to_end` or `File::read_to_string`",
++ deprecation: None,
++ module: "verbose_file_reads",
++ },
++ Lint {
++ name: "vtable_address_comparisons",
++ group: "correctness",
++ desc: "comparison with an address of a trait vtable",
++ deprecation: None,
++ module: "unnamed_address",
++ },
++ Lint {
++ name: "while_immutable_condition",
++ group: "correctness",
++ desc: "variables used within while expression are not mutated in the body",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "while_let_loop",
++ group: "complexity",
++ desc: "`loop { if let { ... } else break }`, which can be written as a `while let` loop",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "while_let_on_iterator",
++ group: "style",
++ desc: "using a while-let loop instead of a for loop on an iterator",
++ deprecation: None,
++ module: "loops",
++ },
++ Lint {
++ name: "wildcard_dependencies",
++ group: "cargo",
++ desc: "wildcard dependencies being used",
++ deprecation: None,
++ module: "wildcard_dependencies",
++ },
++ Lint {
++ name: "wildcard_enum_match_arm",
++ group: "restriction",
++ desc: "a wildcard enum match arm using `_`",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "wildcard_imports",
++ group: "pedantic",
++ desc: "lint `use _::*` statements",
++ deprecation: None,
++ module: "wildcard_imports",
++ },
++ Lint {
++ name: "wildcard_in_or_patterns",
++ group: "complexity",
++ desc: "a wildcard pattern used with others patterns in same match arm",
++ deprecation: None,
++ module: "matches",
++ },
++ Lint {
++ name: "write_literal",
++ group: "style",
++ desc: "writing a literal with a format string",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "write_with_newline",
++ group: "style",
++ desc: "using `write!()` with a format string that ends in a single newline",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "writeln_empty_string",
++ group: "style",
++ desc: "using `writeln!(buf, \"\")` with an empty string",
++ deprecation: None,
++ module: "write",
++ },
++ Lint {
++ name: "wrong_pub_self_convention",
++ group: "restriction",
++ desc: "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "wrong_self_convention",
++ group: "style",
++ desc: "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
++ deprecation: None,
++ module: "methods",
++ },
++ Lint {
++ name: "wrong_transmute",
++ group: "correctness",
++ desc: "transmutes that are confusing at best, undefined behaviour at worst and always useless",
++ deprecation: None,
++ module: "transmute",
++ },
++ Lint {
++ name: "zero_divided_by_zero",
++ group: "complexity",
++ desc: "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`",
++ deprecation: None,
++ module: "zero_div_zero",
++ },
++ Lint {
++ name: "zero_prefixed_literal",
++ group: "complexity",
++ desc: "integer literals starting with `0`",
++ deprecation: None,
++ module: "misc_early",
++ },
++ Lint {
++ name: "zero_ptr",
++ group: "style",
++ desc: "using `0 as *{const, mut} T`",
++ deprecation: None,
++ module: "misc",
++ },
++ Lint {
++ name: "zero_width_space",
++ group: "correctness",
++ desc: "using a zero-width space in a string literal, which is confusing",
++ deprecation: None,
++ module: "unicode",
++ },
++ Lint {
++ name: "zst_offset",
++ group: "correctness",
++ desc: "Check for offset calculations on raw pointers to zero-sized types",
++ deprecation: None,
++ module: "methods",
++ },
++];
++// end lint list, do not remove this comment, it’s used in `update_lints`
++}
--- /dev/null
--- /dev/null
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++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 {
++ unstable_options: bool,
++ cargo_subcommand: &'static str,
++ args: Vec<String>,
++ clippy_args: String,
++}
++
++impl ClippyCmd {
++ fn new<I>(mut old_args: I) -> Self
++ where
++ I: Iterator<Item = String>,
++ {
++ let mut cargo_subcommand = "check";
++ let mut unstable_options = false;
++ let mut args = vec![];
++
++ for arg in old_args.by_ref() {
++ match arg.as_str() {
++ "--fix" => {
++ cargo_subcommand = "fix";
++ continue;
++ },
++ "--" => break,
++ // Cover -Zunstable-options and -Z unstable-options
++ s if s.ends_with("unstable-options") => unstable_options = true,
++ _ => {},
++ }
++
++ args.push(arg);
++ }
++
++ if cargo_subcommand == "fix" && !unstable_options {
++ panic!("Usage of `--fix` requires `-Z unstable-options`");
++ }
++
++ // Run the dogfood tests directly on nightly cargo. This is required due
++ // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118.
++ if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
++ args.insert(0, "+nightly".to_string());
++ }
++
++ let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();
++
++ ClippyCmd {
++ unstable_options,
++ cargo_subcommand,
++ args,
++ clippy_args,
++ }
++ }
++
++ fn path_env(&self) -> &'static str {
++ if self.unstable_options {
++ "RUSTC_WORKSPACE_WRAPPER"
++ } else {
++ "RUSTC_WRAPPER"
++ }
++ }
++
++ 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");
++
++ cmd.env(self.path_env(), Self::path())
++ .envs(ClippyCmd::target_dir())
++ .env("CLIPPY_ARGS", self.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]
++ #[should_panic]
++ fn fix_without_unstable() {
++ let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
++ let _ = ClippyCmd::new(args);
++ }
++
++ #[test]
++ fn fix_unstable() {
++ let args = "cargo clippy --fix -Zunstable-options"
++ .split_whitespace()
++ .map(ToString::to_string);
++ let cmd = ClippyCmd::new(args);
++ assert_eq!("fix", cmd.cargo_subcommand);
++ assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
++ assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
++ }
++
++ #[test]
++ fn check() {
++ let args = "cargo clippy".split_whitespace().map(ToString::to_string);
++ let cmd = ClippyCmd::new(args);
++ assert_eq!("check", cmd.cargo_subcommand);
++ assert_eq!("RUSTC_WRAPPER", cmd.path_env());
++ }
++
++ #[test]
++ fn check_unstable() {
++ let args = "cargo clippy -Zunstable-options"
++ .split_whitespace()
++ .map(ToString::to_string);
++ let cmd = ClippyCmd::new(args);
++ assert_eq!("check", cmd.cargo_subcommand);
++ assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
++ }
++}
--- /dev/null
--- /dev/null
++pub trait A {}
++
++macro_rules! __implicit_hasher_test_macro {
++ (impl< $($impl_arg:tt),* > for $kind:ty where $($bounds:tt)*) => {
++ __implicit_hasher_test_macro!( ($($impl_arg),*) ($kind) ($($bounds)*) );
++ };
++
++ (($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => {
++ impl< $($impl_arg)* > test_macro::A for $($kind_arg)* where $($bounds)* { }
++ };
++}
--- /dev/null
--- /dev/null
++use lazy_static::lazy_static;
++use std::env;
++use std::path::PathBuf;
++
++lazy_static! {
++ pub static ref CARGO_TARGET_DIR: PathBuf = {
++ match env::var_os("CARGO_TARGET_DIR") {
++ Some(v) => v.into(),
++ None => env::current_dir().unwrap().join("target"),
++ }
++ };
++ pub static ref TARGET_LIB: PathBuf = {
++ if let Some(path) = option_env!("TARGET_LIBS") {
++ path.into()
++ } else {
++ let mut dir = CARGO_TARGET_DIR.clone();
++ if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
++ dir.push(target);
++ }
++ dir.push(env!("PROFILE"));
++ dir
++ }
++ };
++}
++
++#[must_use]
++pub fn is_rustc_test_suite() -> bool {
++ option_env!("RUSTC_TEST_SUITE").is_some()
++}
--- /dev/null
--- /dev/null
++#![feature(test)] // compiletest_rs requires this attribute
++
++use compiletest_rs as compiletest;
++use compiletest_rs::common::Mode as TestMode;
++
++use std::env::{self, set_var};
++use std::ffi::OsStr;
++use std::fs;
++use std::io;
++use std::path::{Path, PathBuf};
++
++mod cargo;
++
++fn host_lib() -> PathBuf {
++ if let Some(path) = option_env!("HOST_LIBS") {
++ PathBuf::from(path)
++ } else {
++ cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
++ }
++}
++
++fn clippy_driver_path() -> PathBuf {
++ if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
++ PathBuf::from(path)
++ } else {
++ cargo::TARGET_LIB.join("clippy-driver")
++ }
++}
++
++// 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"];
++ 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(),
++ _ => 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.ends_with(".rlib") {
++ crates.entry(dep).or_insert(path);
++ 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(name) = env::var("TESTNAME") {
++ config.filter = Some(name);
++ }
++
++ 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!(
++ "-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 = if cargo::is_rustc_test_suite() {
++ // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
++ let mut path = PathBuf::from(env!("OUT_DIR"));
++ path.push("test_build_base");
++ path
++ } else {
++ host_lib().join("test_build_base")
++ };
++ config.rustc_path = clippy_driver_path();
++ config
++}
++
++fn run_mode(cfg: &mut compiletest::Config) {
++ cfg.mode = TestMode::Ui;
++ cfg.src_base = Path::new("tests").join("ui");
++ compiletest::run_tests(&cfg);
++}
++
++#[allow(clippy::identity_conversion)]
++fn run_ui_toml_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();
++ set_var("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)
++}
++
++fn run_ui_toml(config: &mut compiletest::Config) {
++ config.mode = TestMode::Ui;
++ config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
++
++ let tests = compiletest::make_tests(&config);
++
++ let res = run_ui_toml_tests(&config, tests);
++ match res {
++ Ok(true) => {},
++ Ok(false) => panic!("Some tests failed"),
++ Err(e) => {
++ println!("I/O failure during tests: {:?}", e);
++ },
++ }
++}
++
++fn prepare_env() {
++ set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
++ set_var("CLIPPY_TESTS", "true");
++ //set_var("RUST_BACKTRACE", "0");
++}
++
++#[test]
++fn compile_test() {
++ prepare_env();
++ let mut config = default_config();
++ run_mode(&mut config);
++ run_ui_toml(&mut config);
++}
--- /dev/null
--- /dev/null
++// Dogfood cannot run on Windows
++#![cfg(not(windows))]
++
++use lazy_static::lazy_static;
++use std::path::PathBuf;
++use std::process::Command;
++
++mod cargo;
++
++lazy_static! {
++ static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy");
++}
++
++#[test]
++fn dogfood_clippy() {
++ // run clippy on itself and fail the test if lint warnings are reported
++ if cargo::is_rustc_test_suite() {
++ return;
++ }
++ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++
++ let output = Command::new(&*CLIPPY_PATH)
++ .current_dir(root_dir)
++ .env("CLIPPY_DOGFOOD", "1")
++ .env("CARGO_INCREMENTAL", "0")
++ .arg("clippy-preview")
++ .arg("--all-targets")
++ .arg("--all-features")
++ .arg("--")
++ .args(&["-D", "clippy::all"])
++ .args(&["-D", "clippy::internal"])
++ .args(&["-D", "clippy::pedantic"])
++ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
++ .output()
++ .unwrap();
++ println!("status: {}", output.status);
++ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++ assert!(output.status.success());
++}
++
++#[test]
++fn dogfood_subprojects() {
++ // run clippy on remaining subprojects and fail the test if lint warnings are reported
++ if cargo::is_rustc_test_suite() {
++ return;
++ }
++ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++
++ for d in &[
++ "clippy_workspace_tests",
++ "clippy_workspace_tests/src",
++ "clippy_workspace_tests/subcrate",
++ "clippy_workspace_tests/subcrate/src",
++ "clippy_dev",
++ "rustc_tools_util",
++ ] {
++ let output = Command::new(&*CLIPPY_PATH)
++ .current_dir(root_dir.join(d))
++ .env("CLIPPY_DOGFOOD", "1")
++ .env("CARGO_INCREMENTAL", "0")
++ .arg("clippy")
++ .arg("--")
++ .args(&["-D", "clippy::all"])
++ .args(&["-D", "clippy::pedantic"])
++ .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
++ .output()
++ .unwrap();
++ println!("status: {}", output.status);
++ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++ assert!(output.status.success());
++ }
++}
--- /dev/null
--- /dev/null
++use std::path::PathBuf;
++use std::process::Command;
++
++#[test]
++fn fmt() {
++ if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
++ return;
++ }
++
++ // Skip this test if rustup nightly is unavailable
++ let rustup_output = Command::new("rustup")
++ .args(&["component", "list", "--toolchain", "nightly"])
++ .output()
++ .unwrap();
++ assert!(rustup_output.status.success());
++ let component_output = String::from_utf8_lossy(&rustup_output.stdout);
++ if !component_output.contains("rustfmt") {
++ return;
++ }
++
++ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++ let dev_dir = root_dir.join("clippy_dev");
++ let target_dir = root_dir.join("target");
++ let target_dir = target_dir.to_str().unwrap();
++ let output = Command::new("cargo")
++ .current_dir(dev_dir)
++ .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
++ .output()
++ .unwrap();
++
++ println!("status: {}", output.status);
++ println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++ println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++ assert!(
++ output.status.success(),
++ "Formatting check failed. Run `cargo dev fmt` to update formatting."
++ );
++}
--- /dev/null
--- /dev/null
++#![cfg(feature = "integration")]
++
++use std::env;
++use std::ffi::OsStr;
++use std::process::Command;
++
++#[cfg_attr(feature = "integration", test)]
++fn integration_test() {
++ let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
++ let repo_url = format!("https://github.com/{}", repo_name);
++ let crate_name = repo_name
++ .split('/')
++ .nth(1)
++ .expect("repo name should have format `<org>/<name>`");
++
++ let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
++ repo_dir.push(crate_name);
++
++ let st = Command::new("git")
++ .args(&[
++ OsStr::new("clone"),
++ OsStr::new("--depth=1"),
++ OsStr::new(&repo_url),
++ OsStr::new(&repo_dir),
++ ])
++ .status()
++ .expect("unable to run git");
++ assert!(st.success());
++
++ let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++ let target_dir = std::path::Path::new(&root_dir).join("target");
++ let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");
++
++ let output = Command::new(clippy_binary)
++ .current_dir(repo_dir)
++ .env("RUST_BACKTRACE", "full")
++ .env("CARGO_TARGET_DIR", target_dir)
++ .args(&[
++ "clippy",
++ "--all-targets",
++ "--all-features",
++ "--",
++ "--cap-lints",
++ "warn",
++ "-Wclippy::pedantic",
++ "-Wclippy::nursery",
++ ])
++ .output()
++ .expect("unable to run clippy");
++
++ let stderr = String::from_utf8_lossy(&output.stderr);
++ if stderr.contains("internal compiler error") {
++ let backtrace_start = stderr
++ .find("thread 'rustc' panicked at")
++ .expect("start of backtrace not found");
++ let backtrace_end = stderr
++ .rfind("error: internal compiler error")
++ .expect("end of backtrace not found");
++
++ panic!(
++ "internal compiler error\nBacktrace:\n\n{}",
++ &stderr[backtrace_start..backtrace_end]
++ );
++ } else if stderr.contains("query stack during panic") {
++ panic!("query stack during panic in the output");
++ } else if stderr.contains("E0463") {
++ // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the
++ // past. Even though it should have. That's why we explicitly panic here.
++ // See PR #3552 and issue #3523 for more background.
++ panic!("error: E0463");
++ } else if stderr.contains("E0514") {
++ panic!("incompatible crate versions");
++ } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
++ panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
++ }
++
++ match output.status.code() {
++ Some(0) => println!("Compilation successful"),
++ Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
++ None => panic!("Process terminated by signal"),
++ }
++}
--- /dev/null
--- /dev/null
++#![allow(clippy::assertions_on_constants)]
++
++use std::fs::{self, DirEntry};
++use std::path::Path;
++
++#[test]
++fn test_missing_tests() {
++ let missing_files = explore_directory(Path::new("./tests"));
++ if !missing_files.is_empty() {
++ assert!(
++ false,
++ format!(
++ "Didn't see a test file for the following files:\n\n{}\n",
++ missing_files
++ .iter()
++ .map(|s| format!("\t{}", s))
++ .collect::<Vec<_>>()
++ .join("\n")
++ )
++ );
++ }
++}
++
++/*
++Test for missing files.
++
++Since rs files are alphabetically before stderr/stdout, we can sort by the full name
++and iter in that order. If we've seen the file stem for the first time and it's not
++a rust file, it means the rust file has to be missing.
++*/
++fn explore_directory(dir: &Path) -> Vec<String> {
++ let mut missing_files: Vec<String> = Vec::new();
++ let mut current_file = String::new();
++ let mut files: Vec<DirEntry> = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect();
++ files.sort_by_key(std::fs::DirEntry::path);
++ for entry in &files {
++ let path = entry.path();
++ if path.is_dir() {
++ missing_files.extend(explore_directory(&path));
++ } else {
++ let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
++ if let Some(ext) = path.extension() {
++ match ext.to_str().unwrap() {
++ "rs" => current_file = file_stem.clone(),
++ "stderr" | "stdout" => {
++ if file_stem != current_file {
++ missing_files.push(path.to_str().unwrap().to_string());
++ }
++ },
++ _ => continue,
++ };
++ }
++ }
++ }
++ missing_files
++}
--- /dev/null
--- /dev/null
++fn this_is_obviously(not: a, toml: file) {
++}
--- /dev/null
--- /dev/null
++// error-pattern: error reading Clippy's configuration file
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++blacklisted-names = 42
--- /dev/null
--- /dev/null
++// error-pattern: error reading Clippy's configuration file: `blacklisted-names` is expected to be a
++// `Vec < String >` but is a `integer`
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++# that one is an error
++cyclomatic-complexity-threshold = 42
++
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
--- /dev/null
--- /dev/null
++// error-pattern: error reading Clippy's configuration file: found deprecated field
++// `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++max-fn-params-bools = 1
--- /dev/null
--- /dev/null
++#![warn(clippy::fn_params_excessive_bools)]
++
++fn f(_: bool) {}
++fn g(_: bool, _: bool) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: more than 1 bools in function parameters
++ --> $DIR/test.rs:4:1
++ |
++LL | fn g(_: bool, _: bool) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
++ = help: consider refactoring bools into two-variant enums
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++too-many-lines-threshold = 1
--- /dev/null
--- /dev/null
++#![warn(clippy::too_many_lines)]
++
++// This function should be considered one line.
++fn many_comments_but_one_line_of_code() {
++ /* println!("This is good."); */
++ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* println!("This is good.");
++ println!("This is good.");
++ println!("This is good."); */
++ println!("This is good.");
++}
++
++// This should be considered two and a fail.
++fn too_many_lines() {
++ println!("This is bad.");
++ println!("This is bad.");
++}
++
++// This should be considered one line.
++#[rustfmt::skip]
++fn comment_starts_after_code() {
++ let _ = 5; /* closing comment. */ /*
++ this line shouldn't be counted theoretically.
++ */
++}
++
++// This should be considered one line.
++fn comment_after_code() {
++ let _ = 5; /* this line should get counted once. */
++}
++
++// This should fail since it is technically two lines.
++#[rustfmt::skip]
++fn comment_before_code() {
++ let _ = "test";
++ /* This comment extends to the front of
++ the code but this line should still count. */ let _ = 5;
++}
++
++// This should be considered one line.
++fn main() {}
--- /dev/null
--- /dev/null
++error: This function has a large number of lines.
++ --> $DIR/test.rs:18:1
++ |
++LL | / fn too_many_lines() {
++LL | | println!("This is bad.");
++LL | | println!("This is bad.");
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::too-many-lines` implied by `-D warnings`
++
++error: This function has a large number of lines.
++ --> $DIR/test.rs:38:1
++ |
++LL | / fn comment_before_code() {
++LL | | let _ = "test";
++LL | | /* This comment extends to the front of
++LL | | the code but this line should still count. */ let _ = 5;
++LL | | }
++ | |_^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
--- /dev/null
--- /dev/null
++// error-pattern: should give absolutely no error
++
++fn main() {}
--- /dev/null
--- /dev/null
++max-struct-bools = 0
--- /dev/null
--- /dev/null
++#![warn(clippy::struct_excessive_bools)]
++
++struct S {
++ a: bool,
++}
++
++struct Foo {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: more than 0 bools in a struct
++ --> $DIR/test.rs:3:1
++ |
++LL | / struct S {
++LL | | a: bool,
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
++ = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++blacklisted-names = ["toto", "tata", "titi"]
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![allow(clippy::single_match)]
++#![allow(unused_variables)]
++#![warn(clippy::blacklisted_name)]
++
++fn test(toto: ()) {}
++
++fn main() {
++ let toto = 42;
++ let tata = 42;
++ let titi = 42;
++
++ let tatab = 42;
++ let tatatataic = 42;
++
++ match (42, Some(1337), Some(0)) {
++ (toto, Some(tata), titi @ Some(_)) => (),
++ _ => (),
++ }
++}
--- /dev/null
--- /dev/null
++error: use of a blacklisted/placeholder name `toto`
++ --> $DIR/conf_french_blacklisted_name.rs:6:9
++ |
++LL | fn test(toto: ()) {}
++ | ^^^^
++ |
++ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
++
++error: use of a blacklisted/placeholder name `toto`
++ --> $DIR/conf_french_blacklisted_name.rs:9:9
++ |
++LL | let toto = 42;
++ | ^^^^
++
++error: use of a blacklisted/placeholder name `tata`
++ --> $DIR/conf_french_blacklisted_name.rs:10:9
++ |
++LL | let tata = 42;
++ | ^^^^
++
++error: use of a blacklisted/placeholder name `titi`
++ --> $DIR/conf_french_blacklisted_name.rs:11:9
++ |
++LL | let titi = 42;
++ | ^^^^
++
++error: use of a blacklisted/placeholder name `toto`
++ --> $DIR/conf_french_blacklisted_name.rs:17:10
++ |
++LL | (toto, Some(tata), titi @ Some(_)) => (),
++ | ^^^^
++
++error: use of a blacklisted/placeholder name `tata`
++ --> $DIR/conf_french_blacklisted_name.rs:17:21
++ |
++LL | (toto, Some(tata), titi @ Some(_)) => (),
++ | ^^^^
++
++error: use of a blacklisted/placeholder name `titi`
++ --> $DIR/conf_french_blacklisted_name.rs:17:28
++ |
++LL | (toto, Some(tata), titi @ Some(_)) => (),
++ | ^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++trivial-copy-size-limit = 2
--- /dev/null
--- /dev/null
++// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
++// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
++
++#![deny(clippy::trivially_copy_pass_by_ref)]
++#![allow(clippy::many_single_char_names)]
++
++#[derive(Copy, Clone)]
++struct Foo(u8);
++
++#[derive(Copy, Clone)]
++struct Bar(u32);
++
++fn good(a: &mut u32, b: u32, c: &Bar, d: &u32) {}
++
++fn bad(x: &u16, y: &Foo) {}
++
++fn main() {
++ let (mut a, b, c, d, x, y) = (0, 0, Bar(0), 0, 0, Foo(0));
++ good(&mut a, b, &c, &d);
++ bad(&x, &y);
++}
--- /dev/null
--- /dev/null
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/test.rs:15:11
++ |
++LL | fn bad(x: &u16, y: &Foo) {}
++ | ^^^^ help: consider passing by value instead: `u16`
++ |
++note: the lint level is defined here
++ --> $DIR/test.rs:4:9
++ |
++LL | #![deny(clippy::trivially_copy_pass_by_ref)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/test.rs:15:20
++ |
++LL | fn bad(x: &u16, y: &Foo) {}
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++# that one is an error
++foobar = 42
++
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
--- /dev/null
--- /dev/null
++// error-pattern: error reading Clippy's configuration file: unknown key `foobar`
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#!/bin/bash
++#
++# A script to update the references for all tests. The idea is that
++# you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. You then
++# run this script, which will copy those files over. If you find
++# yourself manually editing a foo.stderr file, you're doing it wrong.
++#
++# See all `update-references.sh`, if you just want to update a single test.
++
++if [[ "$1" == "--help" || "$1" == "-h" ]]; then
++ echo "usage: $0"
++fi
++
++BUILD_DIR=$PWD/target/debug/test_build_base
++MY_DIR=$(dirname "$0")
++cd "$MY_DIR" || exit
++find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++# A script to update the references for particular tests. The idea is
++# that you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. This
++# script will then copy that output and replace the "expected output"
++# files. You can then commit the changes.
++#
++# If you find yourself manually editing a foo.stderr file, you're
++# doing it wrong.
++
++if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
++ echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
++ echo ""
++ echo "For example:"
++ echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
++fi
++
++MYDIR=$(dirname "$0")
++
++BUILD_DIR="$1"
++shift
++
++while [[ "$1" != "" ]]; do
++ STDERR_NAME="${1/%.rs/.stderr}"
++ STDOUT_NAME="${1/%.rs/.stdout}"
++ shift
++ if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
++ ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
++ echo updating "$MYDIR"/"$STDOUT_NAME"
++ cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
++ fi
++ if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
++ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
++ echo updating "$MYDIR"/"$STDERR_NAME"
++ cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
++ fi
++done
--- /dev/null
--- /dev/null
++vec-box-size-threshold = 4
--- /dev/null
--- /dev/null
++struct S {
++ x: u64,
++}
++
++struct C {
++ y: u16,
++}
++
++struct Foo(Vec<Box<u8>>);
++struct Bar(Vec<Box<u32>>);
++struct Baz(Vec<Box<(u32, u32)>>);
++struct BarBaz(Vec<Box<S>>);
++struct FooBarBaz(Vec<Box<C>>);
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/test.rs:9:12
++ |
++LL | struct Foo(Vec<Box<u8>>);
++ | ^^^^^^^^^^^^ help: try: `Vec<u8>`
++ |
++ = note: `-D clippy::vec-box` implied by `-D warnings`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/test.rs:10:12
++ |
++LL | struct Bar(Vec<Box<u32>>);
++ | ^^^^^^^^^^^^^ help: try: `Vec<u32>`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/test.rs:13:18
++ |
++LL | struct FooBarBaz(Vec<Box<C>>);
++ | ^^^^^^^^^^^ help: try: `Vec<C>`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++single-char-binding-names-threshold = 0
--- /dev/null
--- /dev/null
++#![warn(clippy::many_single_char_names)]
++
++fn main() {}
--- /dev/null
--- /dev/null
++#![warn(clippy::absurd_extreme_comparisons)]
++#![allow(
++ unused,
++ clippy::eq_op,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::needless_pass_by_value
++)]
++
++#[rustfmt::skip]
++fn main() {
++ const Z: u32 = 0;
++ let u: u32 = 42;
++ u <= 0;
++ u <= Z;
++ u < Z;
++ Z >= u;
++ Z > u;
++ u > u32::MAX;
++ u >= u32::MAX;
++ u32::MAX < u;
++ u32::MAX <= u;
++ 1-1 > u;
++ u >= !0;
++ u <= 12 - 2*6;
++ let i: i8 = 0;
++ i < -127 - 1;
++ i8::MAX >= i;
++ 3-7 < i32::MIN;
++ let b = false;
++ b >= true;
++ false > b;
++ u > 0; // ok
++ // this is handled by clippy::unit_cmp
++ () < {};
++}
++
++use std::cmp::{Ordering, PartialEq, PartialOrd};
++
++#[derive(PartialEq, PartialOrd)]
++pub struct U(u64);
++
++impl PartialEq<u32> for U {
++ fn eq(&self, other: &u32) -> bool {
++ self.eq(&U(u64::from(*other)))
++ }
++}
++impl PartialOrd<u32> for U {
++ fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
++ self.partial_cmp(&U(u64::from(*other)))
++ }
++}
++
++pub fn foo(val: U) -> bool {
++ val > u32::MAX
++}
++
++pub fn bar(len: u64) -> bool {
++ // This is OK as we are casting from target sized to fixed size
++ len >= usize::MAX as u64
++}
--- /dev/null
--- /dev/null
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:14:5
++ |
++LL | u <= 0;
++ | ^^^^^^
++ |
++ = note: `-D clippy::absurd-extreme-comparisons` implied by `-D warnings`
++ = help: because `0` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 0` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:15:5
++ |
++LL | u <= Z;
++ | ^^^^^^
++ |
++ = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:16:5
++ |
++LL | u < Z;
++ | ^^^^^
++ |
++ = help: because `Z` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:17:5
++ |
++LL | Z >= u;
++ | ^^^^^^
++ |
++ = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:18:5
++ |
++LL | Z > u;
++ | ^^^^^
++ |
++ = help: because `Z` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:19:5
++ |
++LL | u > u32::MAX;
++ | ^^^^^^^^^^^^
++ |
++ = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:20:5
++ |
++LL | u >= u32::MAX;
++ | ^^^^^^^^^^^^^
++ |
++ = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:21:5
++ |
++LL | u32::MAX < u;
++ | ^^^^^^^^^^^^
++ |
++ = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:22:5
++ |
++LL | u32::MAX <= u;
++ | ^^^^^^^^^^^^^
++ |
++ = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:23:5
++ |
++LL | 1-1 > u;
++ | ^^^^^^^
++ |
++ = help: because `1-1` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:24:5
++ |
++LL | u >= !0;
++ | ^^^^^^^
++ |
++ = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:25:5
++ |
++LL | u <= 12 - 2*6;
++ | ^^^^^^^^^^^^^
++ |
++ = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:27:5
++ |
++LL | i < -127 - 1;
++ | ^^^^^^^^^^^^
++ |
++ = help: because `-127 - 1` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:28:5
++ |
++LL | i8::MAX >= i;
++ | ^^^^^^^^^^^^
++ |
++ = help: because `i8::MAX` is the maximum value for this type, this comparison is always true
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:29:5
++ |
++LL | 3-7 < i32::MIN;
++ | ^^^^^^^^^^^^^^
++ |
++ = help: because `i32::MIN` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:31:5
++ |
++LL | b >= true;
++ | ^^^^^^^^^
++ |
++ = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++ --> $DIR/absurd-extreme-comparisons.rs:32:5
++ |
++LL | false > b;
++ | ^^^^^^^^^
++ |
++ = help: because `false` is the minimum value for this type, this comparison is always false
++
++error: <-comparison of unit values detected. This will always be false
++ --> $DIR/absurd-extreme-comparisons.rs:35:5
++ |
++LL | () < {};
++ | ^^^^^^^
++ |
++ = note: `#[deny(clippy::unit_cmp)]` on by default
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::approx_constant)]
++#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)]
++fn main() {
++ let my_e = 2.7182;
++ let almost_e = 2.718;
++ let no_e = 2.71;
++
++ let my_1_frac_pi = 0.3183;
++ let no_1_frac_pi = 0.31;
++
++ let my_frac_1_sqrt_2 = 0.70710678;
++ let almost_frac_1_sqrt_2 = 0.70711;
++ let my_frac_1_sqrt_2 = 0.707;
++
++ let my_frac_2_pi = 0.63661977;
++ let no_frac_2_pi = 0.636;
++
++ let my_frac_2_sq_pi = 1.128379;
++ let no_frac_2_sq_pi = 1.128;
++
++ let my_frac_pi_2 = 1.57079632679;
++ let no_frac_pi_2 = 1.5705;
++
++ let my_frac_pi_3 = 1.04719755119;
++ let no_frac_pi_3 = 1.047;
++
++ let my_frac_pi_4 = 0.785398163397;
++ let no_frac_pi_4 = 0.785;
++
++ let my_frac_pi_6 = 0.523598775598;
++ let no_frac_pi_6 = 0.523;
++
++ let my_frac_pi_8 = 0.3926990816987;
++ let no_frac_pi_8 = 0.392;
++
++ let my_ln_10 = 2.302585092994046;
++ let no_ln_10 = 2.303;
++
++ let my_ln_2 = 0.6931471805599453;
++ let no_ln_2 = 0.693;
++
++ let my_log10_e = 0.4342944819032518;
++ let no_log10_e = 0.434;
++
++ let my_log2_e = 1.4426950408889634;
++ let no_log2_e = 1.442;
++
++ let log2_10 = 3.321928094887362;
++ let no_log2_10 = 3.321;
++
++ let log10_2 = 0.301029995663981;
++ let no_log10_2 = 0.301;
++
++ let my_pi = 3.1415;
++ let almost_pi = 3.14;
++ let no_pi = 3.15;
++
++ let my_sq2 = 1.4142;
++ let no_sq2 = 1.414;
++}
--- /dev/null
--- /dev/null
++error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++ --> $DIR/approx_const.rs:4:16
++ |
++LL | let my_e = 2.7182;
++ | ^^^^^^
++ |
++ = note: `-D clippy::approx-constant` implied by `-D warnings`
++
++error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++ --> $DIR/approx_const.rs:5:20
++ |
++LL | let almost_e = 2.718;
++ | ^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly
++ --> $DIR/approx_const.rs:8:24
++ |
++LL | let my_1_frac_pi = 0.3183;
++ | ^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:11:28
++ |
++LL | let my_frac_1_sqrt_2 = 0.70710678;
++ | ^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:12:32
++ |
++LL | let almost_frac_1_sqrt_2 = 0.70711;
++ | ^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly
++ --> $DIR/approx_const.rs:15:24
++ |
++LL | let my_frac_2_pi = 0.63661977;
++ | ^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly
++ --> $DIR/approx_const.rs:18:27
++ |
++LL | let my_frac_2_sq_pi = 1.128379;
++ | ^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:21:24
++ |
++LL | let my_frac_pi_2 = 1.57079632679;
++ | ^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly
++ --> $DIR/approx_const.rs:24:24
++ |
++LL | let my_frac_pi_3 = 1.04719755119;
++ | ^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly
++ --> $DIR/approx_const.rs:27:24
++ |
++LL | let my_frac_pi_4 = 0.785398163397;
++ | ^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly
++ --> $DIR/approx_const.rs:30:24
++ |
++LL | let my_frac_pi_6 = 0.523598775598;
++ | ^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly
++ --> $DIR/approx_const.rs:33:24
++ |
++LL | let my_frac_pi_8 = 0.3926990816987;
++ | ^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly
++ --> $DIR/approx_const.rs:36:20
++ |
++LL | let my_ln_10 = 2.302585092994046;
++ | ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:39:19
++ |
++LL | let my_ln_2 = 0.6931471805599453;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
++ --> $DIR/approx_const.rs:42:22
++ |
++LL | let my_log10_e = 0.4342944819032518;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
++ --> $DIR/approx_const.rs:45:21
++ |
++LL | let my_log2_e = 1.4426950408889634;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly
++ --> $DIR/approx_const.rs:48:19
++ |
++LL | let log2_10 = 3.321928094887362;
++ | ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:51:19
++ |
++LL | let log10_2 = 0.301029995663981;
++ | ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++ --> $DIR/approx_const.rs:54:17
++ |
++LL | let my_pi = 3.1415;
++ | ^^^^^^
++
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++ --> $DIR/approx_const.rs:55:21
++ |
++LL | let almost_pi = 3.14;
++ | ^^^^
++
++error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly
++ --> $DIR/approx_const.rs:58:18
++ |
++LL | let my_sq2 = 1.4142;
++ | ^^^^^^
++
++error: aborting due to 21 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::as_conversions)]
++
++fn main() {
++ let i = 0u32 as u64;
++
++ let j = &i as *const u64 as *mut u64;
++}
--- /dev/null
--- /dev/null
++error: using a potentially dangerous silent `as` conversion
++ --> $DIR/as_conversions.rs:4:13
++ |
++LL | let i = 0u32 as u64;
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::as-conversions` implied by `-D warnings`
++ = help: consider using a safe wrapper for this conversion
++
++error: using a potentially dangerous silent `as` conversion
++ --> $DIR/as_conversions.rs:6:13
++ |
++LL | let j = &i as *const u64 as *mut u64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using a safe wrapper for this conversion
++
++error: using a potentially dangerous silent `as` conversion
++ --> $DIR/as_conversions.rs:6:13
++ |
++LL | let j = &i as *const u64 as *mut u64;
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using a safe wrapper for this conversion
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++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);
++}
--- /dev/null
--- /dev/null
++error: `assert!(true)` will be optimized out by the compiler
++ --> $DIR/assertions_on_constants.rs:9:5
++ |
++LL | assert!(true);
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::assertions-on-constants` implied by `-D warnings`
++ = help: remove it
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false)` should probably be replaced
++ --> $DIR/assertions_on_constants.rs:10:5
++ |
++LL | assert!(false);
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: use `panic!()` or `unreachable!()`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(true)` will be optimized out by the compiler
++ --> $DIR/assertions_on_constants.rs:11:5
++ |
++LL | assert!(true, "true message");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: remove it
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, "false message")` should probably be replaced
++ --> $DIR/assertions_on_constants.rs:12:5
++ |
++LL | assert!(false, "false message");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: use `panic!("false message")` or `unreachable!("false message")`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, msg.to_uppercase())` should probably be replaced
++ --> $DIR/assertions_on_constants.rs:15:5
++ |
++LL | assert!(false, msg.to_uppercase());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(true)` will be optimized out by the compiler
++ --> $DIR/assertions_on_constants.rs:18:5
++ |
++LL | assert!(B);
++ | ^^^^^^^^^^^
++ |
++ = help: remove it
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false)` should probably be replaced
++ --> $DIR/assertions_on_constants.rs:21:5
++ |
++LL | assert!(C);
++ | ^^^^^^^^^^^
++ |
++ = help: use `panic!()` or `unreachable!()`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, "C message")` should probably be replaced
++ --> $DIR/assertions_on_constants.rs:22:5
++ |
++LL | assert!(C, "C message");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: use `panic!("C message")` or `unreachable!("C message")`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert!(true)` will be optimized out by the compiler
++ --> $DIR/assertions_on_constants.rs:24:5
++ |
++LL | debug_assert!(true);
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: remove it
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(dead_code, unused_assignments)]
++#[warn(clippy::assign_op_pattern)]
++fn main() {
++ let mut a = 5;
++ a += 1;
++ a += 1;
++ a -= 1;
++ a *= 99;
++ a *= 42;
++ a /= 2;
++ a %= 5;
++ a &= 1;
++ a = 1 - a;
++ a = 5 / a;
++ a = 42 % a;
++ a = 6 << a;
++ let mut s = String::new();
++ s += "bla";
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(dead_code, unused_assignments)]
++#[warn(clippy::assign_op_pattern)]
++fn main() {
++ let mut a = 5;
++ a = a + 1;
++ a = 1 + a;
++ a = a - 1;
++ a = a * 99;
++ a = 42 * a;
++ a = a / 2;
++ a = a % 5;
++ a = a & 1;
++ a = 1 - a;
++ a = 5 / a;
++ a = 42 % a;
++ a = 6 << a;
++ let mut s = String::new();
++ s = s + "bla";
++}
--- /dev/null
--- /dev/null
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:7:5
++ |
++LL | a = a + 1;
++ | ^^^^^^^^^ help: replace it with: `a += 1`
++ |
++ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:8:5
++ |
++LL | a = 1 + a;
++ | ^^^^^^^^^ help: replace it with: `a += 1`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:9:5
++ |
++LL | a = a - 1;
++ | ^^^^^^^^^ help: replace it with: `a -= 1`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:10:5
++ |
++LL | a = a * 99;
++ | ^^^^^^^^^^ help: replace it with: `a *= 99`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:11:5
++ |
++LL | a = 42 * a;
++ | ^^^^^^^^^^ help: replace it with: `a *= 42`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:12:5
++ |
++LL | a = a / 2;
++ | ^^^^^^^^^ help: replace it with: `a /= 2`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:13:5
++ |
++LL | a = a % 5;
++ | ^^^^^^^^^ help: replace it with: `a %= 5`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:14:5
++ |
++LL | a = a & 1;
++ | ^^^^^^^^^ help: replace it with: `a &= 1`
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops.rs:20:5
++ |
++LL | s = s + "bla";
++ | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#[allow(unused_assignments)]
++#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)]
++fn main() {
++ let mut a = 5;
++ a += a + 1;
++ a += 1 + a;
++ a -= a - 1;
++ a *= a * 99;
++ a *= 42 * a;
++ a /= a / 2;
++ a %= a % 5;
++ a &= a & 1;
++ a *= a * a;
++ a = a * a * a;
++ a = a * 42 * a;
++ a = a * 2 + a;
++ a -= 1 - a;
++ a /= 5 / a;
++ a %= 42 % a;
++ a <<= 6 << a;
++}
++
++// check that we don't lint on op assign impls, because that's just the way to impl them
++
++use std::ops::{Mul, MulAssign};
++
++#[derive(Copy, Clone, Debug, PartialEq)]
++pub struct Wrap(i64);
++
++impl Mul<i64> for Wrap {
++ type Output = Self;
++
++ fn mul(self, rhs: i64) -> Self {
++ Wrap(self.0 * rhs)
++ }
++}
++
++impl MulAssign<i64> for Wrap {
++ fn mul_assign(&mut self, rhs: i64) {
++ *self = *self * rhs
++ }
++}
++
++fn cow_add_assign() {
++ use std::borrow::Cow;
++ let mut buf = Cow::Owned(String::from("bar"));
++ let cows = Cow::Borrowed("foo");
++
++ // this can be linted
++ buf = buf + cows.clone();
++
++ // this should not as cow<str> Add is not commutative
++ buf = cows + buf;
++ println!("{}", buf);
++}
--- /dev/null
--- /dev/null
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:5:5
++ |
++LL | a += a + 1;
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings`
++help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with
++ |
++LL | a += 1;
++ | ^^^^^^
++help: or
++ |
++LL | a = a + a + 1;
++ | ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:6:5
++ |
++LL | a += 1 + a;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with
++ |
++LL | a += 1;
++ | ^^^^^^
++help: or
++ |
++LL | a = a + 1 + a;
++ | ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:7:5
++ |
++LL | a -= a - 1;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with
++ |
++LL | a -= 1;
++ | ^^^^^^
++help: or
++ |
++LL | a = a - (a - 1);
++ | ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:8:5
++ |
++LL | a *= a * 99;
++ | ^^^^^^^^^^^
++ |
++help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with
++ |
++LL | a *= 99;
++ | ^^^^^^^
++help: or
++ |
++LL | a = a * a * 99;
++ | ^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:9:5
++ |
++LL | a *= 42 * a;
++ | ^^^^^^^^^^^
++ |
++help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with
++ |
++LL | a *= 42;
++ | ^^^^^^^
++help: or
++ |
++LL | a = a * 42 * a;
++ | ^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:10:5
++ |
++LL | a /= a / 2;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with
++ |
++LL | a /= 2;
++ | ^^^^^^
++help: or
++ |
++LL | a = a / (a / 2);
++ | ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:11:5
++ |
++LL | a %= a % 5;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with
++ |
++LL | a %= 5;
++ | ^^^^^^
++help: or
++ |
++LL | a = a % (a % 5);
++ | ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:12:5
++ |
++LL | a &= a & 1;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with
++ |
++LL | a &= 1;
++ | ^^^^^^
++help: or
++ |
++LL | a = a & a & 1;
++ | ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++ --> $DIR/assign_ops2.rs:13:5
++ |
++LL | a *= a * a;
++ | ^^^^^^^^^^
++ |
++help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with
++ |
++LL | a *= a;
++ | ^^^^^^
++help: or
++ |
++LL | a = a * a * a;
++ | ^^^^^^^^^^^^^
++
++error: manual implementation of an assign operation
++ --> $DIR/assign_ops2.rs:50:5
++ |
++LL | buf = buf + cows.clone();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
++ |
++ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicBool, Ordering};
++
++fn main() {
++ let x = AtomicBool::new(true);
++
++ // Allowed load ordering modes
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++
++ // Disallowed load ordering modes
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ // Allowed store ordering modes
++ x.store(false, Ordering::Release);
++ x.store(false, Ordering::SeqCst);
++ x.store(false, Ordering::Relaxed);
++
++ // Disallowed store ordering modes
++ x.store(false, Ordering::Acquire);
++ x.store(false, Ordering::AcqRel);
++}
--- /dev/null
--- /dev/null
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_bool.rs:14:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_bool.rs:15:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_bool.rs:23:20
++ |
++LL | x.store(false, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_bool.rs:24:20
++ |
++LL | x.store(false, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{compiler_fence, fence, Ordering};
++
++fn main() {
++ // Allowed fence ordering modes
++ fence(Ordering::Acquire);
++ fence(Ordering::Release);
++ fence(Ordering::AcqRel);
++ fence(Ordering::SeqCst);
++
++ // Disallowed fence ordering modes
++ fence(Ordering::Relaxed);
++
++ compiler_fence(Ordering::Acquire);
++ compiler_fence(Ordering::Release);
++ compiler_fence(Ordering::AcqRel);
++ compiler_fence(Ordering::SeqCst);
++ compiler_fence(Ordering::Relaxed);
++}
--- /dev/null
--- /dev/null
++error: memory fences cannot have `Relaxed` ordering
++ --> $DIR/atomic_ordering_fence.rs:13:11
++ |
++LL | fence(Ordering::Relaxed);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++ = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`
++
++error: memory fences cannot have `Relaxed` ordering
++ --> $DIR/atomic_ordering_fence.rs:19:20
++ |
++LL | compiler_fence(Ordering::Relaxed);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, Ordering};
++
++fn main() {
++ // `AtomicI8` test cases
++ let x = AtomicI8::new(0);
++
++ // Allowed load ordering modes
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++
++ // Disallowed load ordering modes
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ // Allowed store ordering modes
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++
++ // Disallowed store ordering modes
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicI16` test cases
++ let x = AtomicI16::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicI32` test cases
++ let x = AtomicI32::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicI64` test cases
++ let x = AtomicI64::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicIsize` test cases
++ let x = AtomicIsize::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++}
--- /dev/null
--- /dev/null
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:15:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:16:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:24:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:25:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:33:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:34:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:39:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:40:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:48:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:49:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:54:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:55:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:63:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:64:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:69:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:70:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:78:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:79:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:84:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_int.rs:85:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicPtr, Ordering};
++
++fn main() {
++ let ptr = &mut 5;
++ let other_ptr = &mut 10;
++ let x = AtomicPtr::new(ptr);
++
++ // Allowed load ordering modes
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++
++ // Disallowed load ordering modes
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ // Allowed store ordering modes
++ x.store(other_ptr, Ordering::Release);
++ x.store(other_ptr, Ordering::SeqCst);
++ x.store(other_ptr, Ordering::Relaxed);
++
++ // Disallowed store ordering modes
++ x.store(other_ptr, Ordering::Acquire);
++ x.store(other_ptr, Ordering::AcqRel);
++}
--- /dev/null
--- /dev/null
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_ptr.rs:16:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_ptr.rs:17:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_ptr.rs:25:24
++ |
++LL | x.store(other_ptr, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_ptr.rs:26:24
++ |
++LL | x.store(other_ptr, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering};
++
++fn main() {
++ // `AtomicU8` test cases
++ let x = AtomicU8::new(0);
++
++ // Allowed load ordering modes
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++
++ // Disallowed load ordering modes
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ // Allowed store ordering modes
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++
++ // Disallowed store ordering modes
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicU16` test cases
++ let x = AtomicU16::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicU32` test cases
++ let x = AtomicU32::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicU64` test cases
++ let x = AtomicU64::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++
++ // `AtomicUsize` test cases
++ let x = AtomicUsize::new(0);
++
++ let _ = x.load(Ordering::Acquire);
++ let _ = x.load(Ordering::SeqCst);
++ let _ = x.load(Ordering::Relaxed);
++ let _ = x.load(Ordering::Release);
++ let _ = x.load(Ordering::AcqRel);
++
++ x.store(1, Ordering::Release);
++ x.store(1, Ordering::SeqCst);
++ x.store(1, Ordering::Relaxed);
++ x.store(1, Ordering::Acquire);
++ x.store(1, Ordering::AcqRel);
++}
--- /dev/null
--- /dev/null
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:15:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:16:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:24:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:25:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:33:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:34:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:39:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:40:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:48:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:49:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:54:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:55:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:63:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:64:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:69:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:70:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:78:20
++ |
++LL | let _ = x.load(Ordering::Release);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:79:20
++ |
++LL | let _ = x.load(Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:84:16
++ |
++LL | x.store(1, Ordering::Acquire);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++ --> $DIR/atomic_ordering_uint.rs:85:16
++ |
++LL | x.store(1, Ordering::AcqRel);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::inline_always, clippy::deprecated_semver)]
++#![allow(clippy::assertions_on_constants)]
++#[inline(always)]
++fn test_attr_lint() {
++ assert!(true)
++}
++
++#[inline(always)]
++fn false_positive_expr() {
++ unreachable!()
++}
++
++#[inline(always)]
++fn false_positive_stmt() {
++ unreachable!();
++}
++
++#[inline(always)]
++fn empty_and_false_positive_stmt() {
++ unreachable!();
++}
++
++#[deprecated(since = "forever")]
++pub const SOME_CONST: u8 = 42;
++
++#[deprecated(since = "1")]
++pub const ANOTHER_CONST: u8 = 23;
++
++#[deprecated(since = "0.1.1")]
++pub const YET_ANOTHER_CONST: u8 = 0;
++
++fn main() {
++ test_attr_lint();
++ if false {
++ false_positive_expr()
++ }
++ if false {
++ false_positive_stmt()
++ }
++ if false {
++ empty_and_false_positive_stmt()
++ }
++}
--- /dev/null
--- /dev/null
++error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
++ --> $DIR/attrs.rs:3:1
++ |
++LL | #[inline(always)]
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::inline-always` implied by `-D warnings`
++
++error: the since field must contain a semver-compliant version
++ --> $DIR/attrs.rs:23:14
++ |
++LL | #[deprecated(since = "forever")]
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::deprecated-semver` implied by `-D warnings`
++
++error: the since field must contain a semver-compliant version
++ --> $DIR/attrs.rs:26:14
++ |
++LL | #[deprecated(since = "1")]
++ | ^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++fn main() {
++ #[clippy::author]
++ let x: char = 0x45 as char;
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let Some(ref init) = local.init;
++ if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
++ if let TyKind::Path(ref qp) = cast_ty.kind;
++ if match_qpath(qp, &["char"]);
++ if let ExprKind::Lit(ref lit) = expr.kind;
++ if let LitKind::Int(69, _) = lit.node;
++ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
++ if name.as_str() == "x";
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#![feature(stmt_expr_attributes)]
++#![allow(redundant_semicolons, clippy::no_effect)]
++
++#[rustfmt::skip]
++fn main() {
++ #[clippy::author]
++ {
++ ;;;;
++ }
++}
++
++#[clippy::author]
++fn foo() {
++ let x = 42i32;
++ -x;
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let ExprKind::Block(ref block) = expr.kind;
++ if let Some(trailing_expr) = &block.expr;
++ if block.stmts.len() == 0;
++ then {
++ // report your lint here
++ }
++}
++if_chain! {
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++fn main() {
++ #[clippy::author]
++ let _ = ::std::cmp::min(3, 4);
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let Some(ref init) = local.init;
++ if let ExprKind::Call(ref func, ref args) = init.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
++ if args.len() == 2;
++ if let ExprKind::Lit(ref lit) = args[0].kind;
++ if let LitKind::Int(3, _) = lit.node;
++ if let ExprKind::Lit(ref lit1) = args[1].kind;
++ if let LitKind::Int(4, _) = lit1.node;
++ if let PatKind::Wild = local.pat.kind;
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#![feature(stmt_expr_attributes)]
++
++fn main() {
++ #[clippy::author]
++ for y in 0..10 {
++ let z = y;
++ }
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let ExprKind::DropTemps(ref expr) = expr.kind;
++ if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
++ if let ExprKind::Call(ref func, ref args) = expr1.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]);
++ if args.len() == 1;
++ if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
++ if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]);
++ if fields.len() == 2;
++ // unimplemented: field checks
++ if arms.len() == 1;
++ if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
++ if let Some(trailing_expr) = &body.expr;
++ if body.stmts.len() == 4;
++ if let StmtKind::Local(ref local) = body.stmts[0].kind;
++ if let PatKind::Binding(BindingAnnotation::Mutable, _, name, None) = local.pat.kind;
++ if name.as_str() == "__next";
++ if let StmtKind::Expr(ref e, _) = body.stmts[1].kind
++ if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
++ if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
++ if let ExprKind::Path(ref path2) = func1.kind;
++ if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]);
++ if args1.len() == 1;
++ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
++ if let ExprKind::Path(ref path3) = inner.kind;
++ if match_qpath(path3, &["iter"]);
++ if arms1.len() == 2;
++ if let ExprKind::Assign(ref target, ref value, ref _span) = arms1[0].body.kind;
++ if let ExprKind::Path(ref path4) = target.kind;
++ if match_qpath(path4, &["__next"]);
++ if let ExprKind::Path(ref path5) = value.kind;
++ if match_qpath(path5, &["val"]);
++ if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind;
++ if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]);
++ if fields1.len() == 1;
++ // unimplemented: field checks
++ if let ExprKind::Break(ref destination, None) = arms1[1].body.kind;
++ if let PatKind::Path(ref path7) = arms1[1].pat.kind;
++ if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]);
++ if let StmtKind::Local(ref local1) = body.stmts[2].kind;
++ if let Some(ref init) = local1.init;
++ if let ExprKind::Path(ref path8) = init.kind;
++ if match_qpath(path8, &["__next"]);
++ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
++ if name1.as_str() == "y";
++ if let StmtKind::Expr(ref e1, _) = body.stmts[3].kind
++ if let ExprKind::Block(ref block) = e1.kind;
++ if let Some(trailing_expr1) = &block.expr;
++ if block.stmts.len() == 1;
++ if let StmtKind::Local(ref local2) = block.stmts[0].kind;
++ if let Some(ref init1) = local2.init;
++ if let ExprKind::Path(ref path9) = init1.kind;
++ if match_qpath(path9, &["y"]);
++ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name2, None) = local2.pat.kind;
++ if name2.as_str() == "z";
++ if let PatKind::Binding(BindingAnnotation::Mutable, _, name3, None) = arms[0].pat.kind;
++ if name3.as_str() == "iter";
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#[allow(clippy::all)]
++
++fn main() {
++ #[clippy::author]
++ let _ = if true {
++ 1 == 1;
++ } else {
++ 2 == 2;
++ };
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let Some(ref init) = local.init;
++ if let Some((ref cond, ref then, Some(else_))) = higher::if_block(&init);
++ if let ExprKind::Block(ref block) = else_.kind;
++ if let Some(trailing_expr) = &block.expr;
++ if block.stmts.len() == 1;
++ if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
++ if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
++ if BinOpKind::Eq == op.node;
++ if let ExprKind::Lit(ref lit) = left.kind;
++ if let LitKind::Int(2, _) = lit.node;
++ if let ExprKind::Lit(ref lit1) = right.kind;
++ if let LitKind::Int(2, _) = lit1.node;
++ if let ExprKind::Lit(ref lit2) = cond.kind;
++ if let LitKind::Bool(true) = lit2.node;
++ if let ExprKind::Block(ref block1) = then.kind;
++ if let Some(trailing_expr1) = &block1.expr;
++ if block1.stmts.len() == 1;
++ if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
++ if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
++ if BinOpKind::Eq == op1.node;
++ if let ExprKind::Lit(ref lit3) = left1.kind;
++ if let LitKind::Int(1, _) = lit3.node;
++ if let ExprKind::Lit(ref lit4) = right1.kind;
++ if let LitKind::Int(1, _) = lit4.node;
++ if let PatKind::Wild = local.pat.kind;
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![allow(clippy::zero_ptr)]
++#![allow(clippy::transmute_ptr_to_ref)]
++#![allow(clippy::transmuting_null)]
++
++pub const ZPTR: *const usize = 0 as *const _;
++
++fn main() {
++ unsafe {
++ #[clippy::author]
++ let _: &i32 = std::mem::transmute(ZPTR);
++ let _: &i32 = std::mem::transmute(0 as *const i32);
++ }
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let Some(ref init) = local.init;
++ if let ExprKind::Call(ref func, ref args) = init.kind;
++ if let ExprKind::Path(ref path) = func.kind;
++ if match_qpath(path, &["std", "mem", "transmute"]);
++ if args.len() == 1;
++ if let ExprKind::Path(ref path1) = args[0].kind;
++ if match_qpath(path1, &["ZPTR"]);
++ if let PatKind::Wild = local.pat.kind;
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#![allow(clippy::let_and_return)]
++
++fn main() {
++ #[clippy::author]
++ let a = match 42 {
++ 16 => 5,
++ 17 => {
++ let x = 3;
++ x
++ },
++ _ => 1,
++ };
++}
--- /dev/null
--- /dev/null
++if_chain! {
++ if let StmtKind::Local(ref local) = stmt.kind;
++ if let Some(ref init) = local.init;
++ if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
++ if let ExprKind::Lit(ref lit) = expr.kind;
++ if let LitKind::Int(42, _) = lit.node;
++ if arms.len() == 3;
++ if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
++ if let LitKind::Int(5, _) = lit1.node;
++ if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
++ if let ExprKind::Lit(ref lit2) = lit_expr.kind;
++ if let LitKind::Int(16, _) = lit2.node;
++ if let ExprKind::Block(ref block) = arms[1].body.kind;
++ if let Some(trailing_expr) = &block.expr;
++ if block.stmts.len() == 1;
++ if let StmtKind::Local(ref local1) = block.stmts[0].kind;
++ if let Some(ref init1) = local1.init;
++ if let ExprKind::Lit(ref lit3) = init1.kind;
++ if let LitKind::Int(3, _) = lit3.node;
++ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
++ if name.as_str() == "x";
++ if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
++ if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
++ if let LitKind::Int(17, _) = lit4.node;
++ if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
++ if let LitKind::Int(1, _) = lit5.node;
++ if let PatKind::Wild = arms[2].pat.kind;
++ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
++ if name1.as_str() == "a";
++ then {
++ // report your lint here
++ }
++}
--- /dev/null
--- /dev/null
++#[macro_export]
++macro_rules! undocd_unsafe {
++ () => {
++ pub unsafe fn oy_vey() {
++ unimplemented!();
++ }
++ };
++}
--- /dev/null
--- /dev/null
++#[macro_export]
++macro_rules! implicit_hasher_fn {
++ () => {
++ pub fn f(input: &HashMap<u32, u32>) {}
++ };
++}
--- /dev/null
--- /dev/null
++#![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)
++ };
++}
--- /dev/null
--- /dev/null
++#![allow(dead_code, unused_variables)]
++
++/// Utility macro to test linting behavior in `option_methods()`
++/// The lints included in `option_methods()` should not lint if the call to map is partially
++/// within a macro
++#[macro_export]
++macro_rules! opt_map {
++ ($opt:expr, $map:expr) => {
++ ($opt).map($map)
++ };
++}
++
++/// Struct to generate false positive for Iterator-based lints
++#[derive(Copy, Clone)]
++pub struct IteratorFalsePositives {
++ pub foo: u32,
++}
++
++impl IteratorFalsePositives {
++ pub fn filter(self) -> IteratorFalsePositives {
++ self
++ }
++
++ pub fn next(self) -> IteratorFalsePositives {
++ self
++ }
++
++ pub fn find(self) -> Option<u32> {
++ Some(self.foo)
++ }
++
++ pub fn position(self) -> Option<u32> {
++ Some(self.foo)
++ }
++
++ pub fn rposition(self) -> Option<u32> {
++ Some(self.foo)
++ }
++
++ pub fn nth(self, n: usize) -> Option<u32> {
++ Some(self.foo)
++ }
++
++ pub fn skip(self, _: usize) -> IteratorFalsePositives {
++ self
++ }
++
++ pub fn skip_while(self) -> IteratorFalsePositives {
++ self
++ }
++}
--- /dev/null
--- /dev/null
++// no-prefer-dynamic
++
++#![crate_type = "proc-macro"]
++#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
++
++extern crate proc_macro;
++
++use proc_macro::{quote, TokenStream};
++
++#[proc_macro_derive(DeriveSomething)]
++pub fn derive(_: TokenStream) -> TokenStream {
++ // Shound not trigger `used_underscore_binding`
++ let _inside_derive = 1;
++ assert_eq!(_inside_derive, _inside_derive);
++
++ let output = quote! {
++ // Should not trigger `useless_attribute`
++ #[allow(dead_code)]
++ extern crate rustc_middle;
++ };
++ output
++}
--- /dev/null
--- /dev/null
++macro_rules! use_self {
++ (
++ impl $ty:ident {
++ fn func(&$this:ident) {
++ [fields($($field:ident)*)]
++ }
++ }
++ ) => (
++ impl $ty {
++ fn func(&$this) {
++ let $ty { $($field),* } = $this;
++ }
++ }
++ )
++}
--- /dev/null
--- /dev/null
++pub use crate::extern_exports::*;
++
++pub fn extern_foo() {}
++pub fn extern_bar() {}
++
++pub struct ExternA;
++
++pub mod inner {
++ pub mod inner_for_self_import {
++ pub fn inner_extern_foo() {}
++ pub fn inner_extern_bar() {}
++ }
++}
++
++mod extern_exports {
++ pub fn extern_exported() {}
++ pub struct ExternExportedStruct;
++ pub enum ExternExportedEnum {
++ A,
++ }
++}
--- /dev/null
--- /dev/null
++// edition:2018
++#![warn(clippy::await_holding_lock)]
++
++use std::sync::Mutex;
++
++async fn bad(x: &Mutex<u32>) -> u32 {
++ let guard = x.lock().unwrap();
++ baz().await
++}
++
++async fn good(x: &Mutex<u32>) -> u32 {
++ {
++ let guard = x.lock().unwrap();
++ let y = *guard + 1;
++ }
++ baz().await;
++ let guard = x.lock().unwrap();
++ 47
++}
++
++async fn baz() -> u32 {
++ 42
++}
++
++async fn also_bad(x: &Mutex<u32>) -> u32 {
++ let first = baz().await;
++
++ let guard = x.lock().unwrap();
++
++ let second = baz().await;
++
++ let third = baz().await;
++
++ first + second + third
++}
++
++async fn not_good(x: &Mutex<u32>) -> u32 {
++ let first = baz().await;
++
++ let second = {
++ let guard = x.lock().unwrap();
++ baz().await
++ };
++
++ let third = baz().await;
++
++ first + second + third
++}
++
++fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
++ async move {
++ let guard = x.lock().unwrap();
++ baz().await
++ }
++}
++
++fn main() {
++ let m = Mutex::new(100);
++ good(&m);
++ bad(&m);
++ also_bad(&m);
++ not_good(&m);
++ block_bad(&m);
++}
--- /dev/null
--- /dev/null
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++ --> $DIR/await_holding_lock.rs:7:9
++ |
++LL | let guard = x.lock().unwrap();
++ | ^^^^^
++ |
++ = note: `-D clippy::await-holding-lock` implied by `-D warnings`
++note: these are all the await points this lock is held through
++ --> $DIR/await_holding_lock.rs:7:5
++ |
++LL | / let guard = x.lock().unwrap();
++LL | | baz().await
++LL | | }
++ | |_^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++ --> $DIR/await_holding_lock.rs:28:9
++ |
++LL | let guard = x.lock().unwrap();
++ | ^^^^^
++ |
++note: these are all the await points this lock is held through
++ --> $DIR/await_holding_lock.rs:28:5
++ |
++LL | / let guard = x.lock().unwrap();
++LL | |
++LL | | let second = baz().await;
++LL | |
++... |
++LL | | first + second + third
++LL | | }
++ | |_^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++ --> $DIR/await_holding_lock.rs:41:13
++ |
++LL | let guard = x.lock().unwrap();
++ | ^^^^^
++ |
++note: these are all the await points this lock is held through
++ --> $DIR/await_holding_lock.rs:41:9
++ |
++LL | / let guard = x.lock().unwrap();
++LL | | baz().await
++LL | | };
++ | |_____^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++ --> $DIR/await_holding_lock.rs:52:13
++ |
++LL | let guard = x.lock().unwrap();
++ | ^^^^^
++ |
++note: these are all the await points this lock is held through
++ --> $DIR/await_holding_lock.rs:52:9
++ |
++LL | / let guard = x.lock().unwrap();
++LL | | baz().await
++LL | | }
++ | |_____^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++const THREE_BITS: i64 = 7;
++const EVEN_MORE_REDIRECTION: i64 = THREE_BITS;
++
++#[warn(clippy::bad_bit_mask)]
++#[allow(
++ clippy::ineffective_bit_mask,
++ clippy::identity_op,
++ clippy::no_effect,
++ clippy::unnecessary_operation
++)]
++fn main() {
++ let x = 5;
++
++ x & 0 == 0;
++ x & 1 == 1; //ok, distinguishes bit 0
++ x & 1 == 0; //ok, compared with zero
++ x & 2 == 1;
++ x | 0 == 0; //ok, equals x == 0 (maybe warn?)
++ x | 1 == 3; //ok, equals x == 2 || x == 3
++ x | 3 == 3; //ok, equals x <= 3
++ x | 3 == 2;
++
++ x & 1 > 1;
++ x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
++ x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
++ x | 1 > 1; // ok (if a bit silly), equals x > 1
++ x | 2 > 1;
++ x | 2 <= 2; // ok (if a bit silly), equals x <= 2
++
++ x & 192 == 128; // ok, tests for bit 7 and not bit 6
++ x & 0xffc0 == 0xfe80; // ok
++
++ // this also now works with constants
++ x & THREE_BITS == 8;
++ x | EVEN_MORE_REDIRECTION < 7;
++
++ 0 & x == 0;
++ 1 | x > 1;
++
++ // and should now also match uncommon usage
++ 1 < 2 | x;
++ 2 == 3 | x;
++ 1 == x & 2;
++
++ x | 1 > 2; // no error, because we allowed ineffective bit masks
++ ineffective();
++}
++
++#[warn(clippy::ineffective_bit_mask)]
++#[allow(clippy::bad_bit_mask, clippy::no_effect, clippy::unnecessary_operation)]
++fn ineffective() {
++ let x = 5;
++
++ x | 1 > 3;
++ x | 1 < 4;
++ x | 1 <= 3;
++ x | 1 >= 8;
++
++ x | 1 > 2; // not an error (yet), better written as x >= 2
++ x | 1 >= 7; // not an error (yet), better written as x >= 6
++ x | 3 > 4; // not an error (yet), better written as x >= 4
++ x | 4 <= 19;
++}
--- /dev/null
--- /dev/null
++error: &-masking with zero
++ --> $DIR/bit_masks.rs:14:5
++ |
++LL | x & 0 == 0;
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::bad-bit-mask` implied by `-D warnings`
++
++error: this operation will always return zero. This is likely not the intended outcome
++ --> $DIR/bit_masks.rs:14:5
++ |
++LL | x & 0 == 0;
++ | ^^^^^
++ |
++ = note: `#[deny(clippy::erasing_op)]` on by default
++
++error: incompatible bit mask: `_ & 2` can never be equal to `1`
++ --> $DIR/bit_masks.rs:17:5
++ |
++LL | x & 2 == 1;
++ | ^^^^^^^^^^
++
++error: incompatible bit mask: `_ | 3` can never be equal to `2`
++ --> $DIR/bit_masks.rs:21:5
++ |
++LL | x | 3 == 2;
++ | ^^^^^^^^^^
++
++error: incompatible bit mask: `_ & 1` will never be higher than `1`
++ --> $DIR/bit_masks.rs:23:5
++ |
++LL | x & 1 > 1;
++ | ^^^^^^^^^
++
++error: incompatible bit mask: `_ | 2` will always be higher than `1`
++ --> $DIR/bit_masks.rs:27:5
++ |
++LL | x | 2 > 1;
++ | ^^^^^^^^^
++
++error: incompatible bit mask: `_ & 7` can never be equal to `8`
++ --> $DIR/bit_masks.rs:34:5
++ |
++LL | x & THREE_BITS == 8;
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: incompatible bit mask: `_ | 7` will never be lower than `7`
++ --> $DIR/bit_masks.rs:35:5
++ |
++LL | x | EVEN_MORE_REDIRECTION < 7;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: &-masking with zero
++ --> $DIR/bit_masks.rs:37:5
++ |
++LL | 0 & x == 0;
++ | ^^^^^^^^^^
++
++error: this operation will always return zero. This is likely not the intended outcome
++ --> $DIR/bit_masks.rs:37:5
++ |
++LL | 0 & x == 0;
++ | ^^^^^
++
++error: incompatible bit mask: `_ | 2` will always be higher than `1`
++ --> $DIR/bit_masks.rs:41:5
++ |
++LL | 1 < 2 | x;
++ | ^^^^^^^^^
++
++error: incompatible bit mask: `_ | 3` can never be equal to `2`
++ --> $DIR/bit_masks.rs:42:5
++ |
++LL | 2 == 3 | x;
++ | ^^^^^^^^^^
++
++error: incompatible bit mask: `_ & 2` can never be equal to `1`
++ --> $DIR/bit_masks.rs:43:5
++ |
++LL | 1 == x & 2;
++ | ^^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
++ --> $DIR/bit_masks.rs:54:5
++ |
++LL | x | 1 > 3;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::ineffective-bit-mask` implied by `-D warnings`
++
++error: ineffective bit mask: `x | 1` compared to `4`, is the same as x compared directly
++ --> $DIR/bit_masks.rs:55:5
++ |
++LL | x | 1 < 4;
++ | ^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
++ --> $DIR/bit_masks.rs:56:5
++ |
++LL | x | 1 <= 3;
++ | ^^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `8`, is the same as x compared directly
++ --> $DIR/bit_masks.rs:57:5
++ |
++LL | x | 1 >= 8;
++ | ^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![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 bar = 42;
++ let baz = 42;
++
++ let barb = 42;
++ let barbaric = 42;
++
++ match (42, Some(1337), Some(0)) {
++ (foo, Some(bar), baz @ Some(_)) => (),
++ _ => (),
++ }
++}
++
++fn issue_1647(mut foo: u8) {
++ let mut bar = 0;
++ if let Some(mut baz) = Some(42) {}
++}
++
++fn issue_1647_ref() {
++ let ref bar = 0;
++ if let Some(ref baz) = Some(42) {}
++}
++
++fn issue_1647_ref_mut() {
++ let ref mut bar = 0;
++ if let Some(ref mut baz) = Some(42) {}
++}
--- /dev/null
--- /dev/null
++error: use of a blacklisted/placeholder name `foo`
++ --> $DIR/blacklisted_name.rs:11:9
++ |
++LL | fn test(foo: ()) {}
++ | ^^^
++ |
++ = note: `-D clippy::blacklisted-name` implied by `-D warnings`
++
++error: use of a blacklisted/placeholder name `foo`
++ --> $DIR/blacklisted_name.rs:14:9
++ |
++LL | let foo = 42;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++ --> $DIR/blacklisted_name.rs:15:9
++ |
++LL | let bar = 42;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++ --> $DIR/blacklisted_name.rs:16:9
++ |
++LL | let baz = 42;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `foo`
++ --> $DIR/blacklisted_name.rs:22:10
++ |
++LL | (foo, Some(bar), baz @ Some(_)) => (),
++ | ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++ --> $DIR/blacklisted_name.rs:22:20
++ |
++LL | (foo, Some(bar), baz @ Some(_)) => (),
++ | ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++ --> $DIR/blacklisted_name.rs:22:26
++ |
++LL | (foo, Some(bar), baz @ Some(_)) => (),
++ | ^^^
++
++error: use of a blacklisted/placeholder name `foo`
++ --> $DIR/blacklisted_name.rs:27:19
++ |
++LL | fn issue_1647(mut foo: u8) {
++ | ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++ --> $DIR/blacklisted_name.rs:28:13
++ |
++LL | let mut bar = 0;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++ --> $DIR/blacklisted_name.rs:29:21
++ |
++LL | if let Some(mut baz) = Some(42) {}
++ | ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++ --> $DIR/blacklisted_name.rs:33:13
++ |
++LL | let ref bar = 0;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++ --> $DIR/blacklisted_name.rs:34:21
++ |
++LL | if let Some(ref baz) = Some(42) {}
++ | ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++ --> $DIR/blacklisted_name.rs:38:17
++ |
++LL | let ref mut bar = 0;
++ | ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++ --> $DIR/blacklisted_name.rs:39:25
++ |
++LL | if let Some(ref mut baz) = Some(42) {}
++ | ^^^
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++#![warn(clippy::nonminimal_bool)]
++
++macro_rules! blocky {
++ () => {{
++ true
++ }};
++}
++
++macro_rules! blocky_too {
++ () => {{
++ let r = true;
++ r
++ }};
++}
++
++fn macro_if() {
++ if blocky!() {}
++
++ if blocky_too!() {}
++}
++
++fn condition_has_block() -> i32 {
++ let res = {
++ let x = 3;
++ x == 3
++ }; if res {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_has_block_with_single_expression() -> i32 {
++ if true {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_is_normal() -> i32 {
++ let x = 3;
++ if x == 3 {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_is_unsafe_block() {
++ let a: i32 = 1;
++
++ // this should not warn because the condition is an unsafe block
++ if unsafe { 1u32 == std::mem::transmute(a) } {
++ println!("1u32 == a");
++ }
++}
++
++fn block_in_assert() {
++ let opt = Some(42);
++ assert!(opt
++ .as_ref()
++ .and_then(|val| {
++ let mut v = val * 2;
++ v -= 1;
++ Some(v * 3)
++ })
++ .is_some());
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++#![warn(clippy::nonminimal_bool)]
++
++macro_rules! blocky {
++ () => {{
++ true
++ }};
++}
++
++macro_rules! blocky_too {
++ () => {{
++ let r = true;
++ r
++ }};
++}
++
++fn macro_if() {
++ if blocky!() {}
++
++ if blocky_too!() {}
++}
++
++fn condition_has_block() -> i32 {
++ if {
++ let x = 3;
++ x == 3
++ } {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_has_block_with_single_expression() -> i32 {
++ if { true } {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_is_normal() -> i32 {
++ let x = 3;
++ if true && x == 3 {
++ 6
++ } else {
++ 10
++ }
++}
++
++fn condition_is_unsafe_block() {
++ let a: i32 = 1;
++
++ // this should not warn because the condition is an unsafe block
++ if unsafe { 1u32 == std::mem::transmute(a) } {
++ println!("1u32 == a");
++ }
++}
++
++fn block_in_assert() {
++ let opt = Some(42);
++ assert!(opt
++ .as_ref()
++ .and_then(|val| {
++ let mut v = val * 2;
++ v -= 1;
++ Some(v * 3)
++ })
++ .is_some());
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++ --> $DIR/block_in_if_condition.rs:27:5
++ |
++LL | / if {
++LL | | let x = 3;
++LL | | x == 3
++LL | | } {
++ | |_____^
++ |
++ = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings`
++help: try
++ |
++LL | let res = {
++LL | let x = 3;
++LL | x == 3
++LL | }; if res {
++ |
++
++error: omit braces around single expression condition
++ --> $DIR/block_in_if_condition.rs:38:8
++ |
++LL | if { true } {
++ | ^^^^^^^^ help: try: `true`
++ |
++ = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++ --> $DIR/block_in_if_condition.rs:47:8
++ |
++LL | if true && x == 3 {
++ | ^^^^^^^^^^^^^^ help: try: `x == 3`
++ |
++ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++
++fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool {
++ pfn(val)
++}
++
++fn pred_test() {
++ let v = 3;
++ let sky = "blue";
++ // This is a sneaky case, where the block isn't directly in the condition,
++ // but is actually nside a closure that the condition is using.
++ // The same principle applies -- add some extra expressions to make sure
++ // linter isn't confused by them.
++ if v == 3
++ && sky == "blue"
++ && predicate(
++ |x| {
++ let target = 3;
++ x == target
++ },
++ v,
++ )
++ {}
++
++ if predicate(
++ |x| {
++ let target = 3;
++ x == target
++ },
++ v,
++ ) {}
++}
++
++fn closure_without_block() {
++ if predicate(|x| x == 3, 6) {}
++}
++
++fn macro_in_closure() {
++ let option = Some(true);
++
++ if option.unwrap_or_else(|| unimplemented!()) {
++ unimplemented!()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++ --> $DIR/block_in_if_condition_closure.rs:19:17
++ |
++LL | |x| {
++ | _________________^
++LL | | let target = 3;
++LL | | x == target
++LL | | },
++ | |_____________^
++ |
++ = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings`
++
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++ --> $DIR/block_in_if_condition_closure.rs:28:13
++ |
++LL | |x| {
++ | _____________^
++LL | | let target = 3;
++LL | | x == target
++LL | | },
++ | |_________^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::bool_comparison)]
++fn main() {
++ let x = true;
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x {
++ "yes"
++ } else {
++ "no"
++ };
++ if !x {
++ "yes"
++ } else {
++ "no"
++ };
++ let y = true;
++ if !x & y {
++ "yes"
++ } else {
++ "no"
++ };
++ if x & !y {
++ "yes"
++ } else {
++ "no"
++ };
++}
++
++#[allow(dead_code)]
++fn issue3703() {
++ struct Foo;
++ impl PartialEq<bool> for Foo {
++ fn eq(&self, _: &bool) -> bool {
++ true
++ }
++ }
++ impl PartialEq<Foo> for bool {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++ }
++ impl PartialOrd<bool> for Foo {
++ fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
++ None
++ }
++ }
++ impl PartialOrd<Foo> for bool {
++ fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
++ None
++ }
++ }
++
++ if Foo == true {}
++ if true == Foo {}
++ if Foo != true {}
++ if true != Foo {}
++ if Foo == false {}
++ if false == Foo {}
++ if Foo != false {}
++ if false != Foo {}
++ if Foo < false {}
++ if false < Foo {}
++}
++
++#[allow(dead_code)]
++fn issue4983() {
++ let a = true;
++ let b = false;
++
++ if a != b {};
++ if a != b {};
++ if a == b {};
++ if !a == !b {};
++
++ if b != a {};
++ if b != a {};
++ if b == a {};
++ if !b == !a {};
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::bool_comparison)]
++fn main() {
++ let x = true;
++ if x == true {
++ "yes"
++ } else {
++ "no"
++ };
++ if x == false {
++ "yes"
++ } else {
++ "no"
++ };
++ if true == x {
++ "yes"
++ } else {
++ "no"
++ };
++ if false == x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x != true {
++ "yes"
++ } else {
++ "no"
++ };
++ if x != false {
++ "yes"
++ } else {
++ "no"
++ };
++ if true != x {
++ "yes"
++ } else {
++ "no"
++ };
++ if false != x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x < true {
++ "yes"
++ } else {
++ "no"
++ };
++ if false < x {
++ "yes"
++ } else {
++ "no"
++ };
++ if x > false {
++ "yes"
++ } else {
++ "no"
++ };
++ if true > x {
++ "yes"
++ } else {
++ "no"
++ };
++ let y = true;
++ if x < y {
++ "yes"
++ } else {
++ "no"
++ };
++ if x > y {
++ "yes"
++ } else {
++ "no"
++ };
++}
++
++#[allow(dead_code)]
++fn issue3703() {
++ struct Foo;
++ impl PartialEq<bool> for Foo {
++ fn eq(&self, _: &bool) -> bool {
++ true
++ }
++ }
++ impl PartialEq<Foo> for bool {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++ }
++ impl PartialOrd<bool> for Foo {
++ fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
++ None
++ }
++ }
++ impl PartialOrd<Foo> for bool {
++ fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
++ None
++ }
++ }
++
++ if Foo == true {}
++ if true == Foo {}
++ if Foo != true {}
++ if true != Foo {}
++ if Foo == false {}
++ if false == Foo {}
++ if Foo != false {}
++ if false != Foo {}
++ if Foo < false {}
++ if false < Foo {}
++}
++
++#[allow(dead_code)]
++fn issue4983() {
++ let a = true;
++ let b = false;
++
++ if a == !b {};
++ if !a == b {};
++ if a == b {};
++ if !a == !b {};
++
++ if b == !a {};
++ if !b == a {};
++ if b == a {};
++ if !b == !a {};
++}
--- /dev/null
--- /dev/null
++error: equality checks against true are unnecessary
++ --> $DIR/bool_comparison.rs:6:8
++ |
++LL | if x == true {
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++ |
++ = note: `-D clippy::bool-comparison` implied by `-D warnings`
++
++error: equality checks against false can be replaced by a negation
++ --> $DIR/bool_comparison.rs:11:8
++ |
++LL | if x == false {
++ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: equality checks against true are unnecessary
++ --> $DIR/bool_comparison.rs:16:8
++ |
++LL | if true == x {
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: equality checks against false can be replaced by a negation
++ --> $DIR/bool_comparison.rs:21:8
++ |
++LL | if false == x {
++ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against true can be replaced by a negation
++ --> $DIR/bool_comparison.rs:26:8
++ |
++LL | if x != true {
++ | ^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against false are unnecessary
++ --> $DIR/bool_comparison.rs:31:8
++ |
++LL | if x != false {
++ | ^^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: inequality checks against true can be replaced by a negation
++ --> $DIR/bool_comparison.rs:36:8
++ |
++LL | if true != x {
++ | ^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against false are unnecessary
++ --> $DIR/bool_comparison.rs:41:8
++ |
++LL | if false != x {
++ | ^^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: less than comparison against true can be replaced by a negation
++ --> $DIR/bool_comparison.rs:46:8
++ |
++LL | if x < true {
++ | ^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: greater than checks against false are unnecessary
++ --> $DIR/bool_comparison.rs:51:8
++ |
++LL | if false < x {
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: greater than checks against false are unnecessary
++ --> $DIR/bool_comparison.rs:56:8
++ |
++LL | if x > false {
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: less than comparison against true can be replaced by a negation
++ --> $DIR/bool_comparison.rs:61:8
++ |
++LL | if true > x {
++ | ^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: order comparisons between booleans can be simplified
++ --> $DIR/bool_comparison.rs:67:8
++ |
++LL | if x < y {
++ | ^^^^^ help: try simplifying it as shown: `!x & y`
++
++error: order comparisons between booleans can be simplified
++ --> $DIR/bool_comparison.rs:72:8
++ |
++LL | if x > y {
++ | ^^^^^ help: try simplifying it as shown: `x & !y`
++
++error: This comparison might be written more concisely
++ --> $DIR/bool_comparison.rs:120:8
++ |
++LL | if a == !b {};
++ | ^^^^^^^ help: try simplifying it as shown: `a != b`
++
++error: This comparison might be written more concisely
++ --> $DIR/bool_comparison.rs:121:8
++ |
++LL | if !a == b {};
++ | ^^^^^^^ help: try simplifying it as shown: `a != b`
++
++error: This comparison might be written more concisely
++ --> $DIR/bool_comparison.rs:125:8
++ |
++LL | if b == !a {};
++ | ^^^^^^^ help: try simplifying it as shown: `b != a`
++
++error: This comparison might be written more concisely
++ --> $DIR/bool_comparison.rs:126:8
++ |
++LL | if !b == a {};
++ | ^^^^^^^ help: try simplifying it as shown: `b != a`
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::borrowed_box)]
++#![allow(clippy::blacklisted_name)]
++#![allow(unused_variables)]
++#![allow(dead_code)]
++
++pub fn test1(foo: &mut Box<bool>) {
++ // Although this function could be changed to "&mut bool",
++ // avoiding the Box, mutable references to boxes are not
++ // flagged by this lint.
++ //
++ // This omission is intentional: By passing a mutable Box,
++ // the memory location of the pointed-to object could be
++ // modified. By passing a mutable reference, the contents
++ // could change, but not the location.
++ println!("{:?}", foo)
++}
++
++pub fn test2() {
++ let foo: &Box<bool>;
++}
++
++struct Test3<'a> {
++ foo: &'a Box<bool>,
++}
++
++trait Test4 {
++ fn test4(a: &Box<bool>);
++}
++
++impl<'a> Test4 for Test3<'a> {
++ fn test4(a: &Box<bool>) {
++ unimplemented!();
++ }
++}
++
++use std::any::Any;
++
++pub fn test5(foo: &mut Box<dyn Any>) {
++ println!("{:?}", foo)
++}
++
++pub fn test6() {
++ let foo: &Box<dyn Any>;
++}
++
++struct Test7<'a> {
++ foo: &'a Box<dyn Any>,
++}
++
++trait Test8 {
++ fn test8(a: &Box<dyn Any>);
++}
++
++impl<'a> Test8 for Test7<'a> {
++ fn test8(a: &Box<dyn Any>) {
++ unimplemented!();
++ }
++}
++
++pub fn test9(foo: &mut Box<dyn Any + Send + Sync>) {
++ let _ = foo;
++}
++
++pub fn test10() {
++ let foo: &Box<dyn Any + Send + 'static>;
++}
++
++struct Test11<'a> {
++ foo: &'a Box<dyn Any + Send>,
++}
++
++trait Test12 {
++ fn test4(a: &Box<dyn Any + 'static>);
++}
++
++impl<'a> Test12 for Test11<'a> {
++ fn test4(a: &Box<dyn Any + 'static>) {
++ unimplemented!();
++ }
++}
++
++pub fn test13(boxed_slice: &mut Box<[i32]>) {
++ // Unconditionally replaces the box pointer.
++ //
++ // This cannot be accomplished if "&mut [i32]" is passed,
++ // and provides a test case where passing a reference to
++ // a Box is valid.
++ let mut data = vec![12];
++ *boxed_slice = data.into_boxed_slice();
++}
++
++fn main() {
++ test1(&mut Box::new(false));
++ test2();
++ test5(&mut (Box::new(false) as Box<dyn Any>));
++ test6();
++ test9(&mut (Box::new(false) as Box<dyn Any + Send + Sync>));
++ test10();
++}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++ --> $DIR/borrow_box.rs:19:14
++ |
++LL | let foo: &Box<bool>;
++ | ^^^^^^^^^^ help: try: `&bool`
++ |
++note: the lint level is defined here
++ --> $DIR/borrow_box.rs:1:9
++ |
++LL | #![deny(clippy::borrowed_box)]
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++ --> $DIR/borrow_box.rs:23:10
++ |
++LL | foo: &'a Box<bool>,
++ | ^^^^^^^^^^^^^ help: try: `&'a bool`
++
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++ --> $DIR/borrow_box.rs:27:17
++ |
++LL | fn test4(a: &Box<bool>);
++ | ^^^^^^^^^^ help: try: `&bool`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::borrow_interior_mutable_const)]
++#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
++
++use std::borrow::Cow;
++use std::cell::Cell;
++use std::fmt::Display;
++use std::sync::atomic::{AtomicUsize, Ordering};
++use std::sync::Once;
++
++const ATOMIC: AtomicUsize = AtomicUsize::new(5);
++const CELL: Cell<usize> = Cell::new(6);
++const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++const INTEGER: u8 = 8;
++const STRING: String = String::new();
++const STR: &str = "012345";
++const COW: Cow<str> = Cow::Borrowed("abcdef");
++const NO_ANN: &dyn Display = &70;
++static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
++const ONCE_INIT: Once = Once::new();
++
++trait Trait<T>: Copy {
++ type NonCopyType;
++
++ const ATOMIC: AtomicUsize;
++}
++
++impl Trait<u32> for u64 {
++ type NonCopyType = u16;
++
++ const ATOMIC: AtomicUsize = AtomicUsize::new(9);
++}
++
++fn main() {
++ ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
++ assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
++
++ let _once = ONCE_INIT;
++ let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
++ let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
++ let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
++ let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
++ let _atomic_into_inner = ATOMIC.into_inner();
++ // these should be all fine.
++ let _twice = (ONCE_INIT, ONCE_INIT);
++ let _ref_twice = &(ONCE_INIT, ONCE_INIT);
++ let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
++ let _array_twice = [ONCE_INIT, ONCE_INIT];
++ let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
++ let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
++
++ // referencing projection is still bad.
++ let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
++ let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
++ let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
++ let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++ let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
++ let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability
++ let _ = &ATOMIC_TUPLE.2;
++ let _ = (&&&&ATOMIC_TUPLE).0;
++ let _ = (&&&&ATOMIC_TUPLE).2;
++ let _ = ATOMIC_TUPLE.0;
++ let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++ let _ = ATOMIC_TUPLE.1.into_iter();
++ let _ = ATOMIC_TUPLE.2;
++ let _ = &{ ATOMIC_TUPLE };
++
++ CELL.set(2); //~ ERROR interior mutability
++ assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
++
++ assert_eq!(INTEGER, 8);
++ assert!(STRING.is_empty());
++
++ let a = ATOMIC;
++ a.store(4, Ordering::SeqCst);
++ assert_eq!(a.load(Ordering::SeqCst), 4);
++
++ STATIC_TUPLE.0.store(3, Ordering::SeqCst);
++ assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
++ assert!(STATIC_TUPLE.1.is_empty());
++
++ u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
++ assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
++
++ assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
++}
--- /dev/null
--- /dev/null
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:34:5
++ |
++LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
++ | ^^^^^^
++ |
++ = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:35:16
++ |
++LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
++ | ^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:38:22
++ |
++LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
++ | ^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:39:25
++ |
++LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
++ | ^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:40:27
++ |
++LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
++ | ^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:41:26
++ |
++LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
++ | ^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:52:14
++ |
++LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:53:14
++ |
++LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:54:19
++ |
++LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:55:14
++ |
++LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:56:13
++ |
++LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:62:13
++ |
++LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++ | ^^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:67:5
++ |
++LL | CELL.set(2); //~ ERROR interior mutability
++ | ^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:68:16
++ |
++LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
++ | ^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:81:5
++ |
++LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
++ | ^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++ --> $DIR/borrow_interior_mutable_const.rs:82:16
++ |
++LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
++ | ^^^^^^^^^^^
++ |
++ = help: assign this const to a local or static variable, and use the variable here
++
++error: aborting due to 16 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name)]
++
++macro_rules! boxit {
++ ($init:expr, $x:ty) => {
++ let _: Box<$x> = Box::new($init);
++ };
++}
++
++fn test_macro() {
++ boxit!(Vec::new(), Vec<u8>);
++}
++pub fn test(foo: Box<Vec<bool>>) {
++ println!("{:?}", foo.get(0))
++}
++
++pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
++ // pass if #31 is fixed
++ foo(vec![1, 2, 3])
++}
++
++pub fn test_local_not_linted() {
++ let _: Box<Vec<bool>>;
++}
++
++fn main() {
++ test(Box::new(Vec::new()));
++ test2(Box::new(|v| println!("{:?}", v)));
++ test_macro();
++ test_local_not_linted();
++}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
++ --> $DIR/box_vec.rs:14:18
++ |
++LL | pub fn test(foo: Box<Vec<bool>>) {
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::box-vec` implied by `-D warnings`
++ = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::builtin_type_shadow)]
++#![allow(non_camel_case_types)]
++
++fn foo<u32>(a: u32) -> u32 {
++ 42
++ // ^ rustc's type error
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: This generic shadows the built-in type `u32`
++ --> $DIR/builtin-type-shadow.rs:4:8
++ |
++LL | fn foo<u32>(a: u32) -> u32 {
++ | ^^^
++ |
++ = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
++
++error[E0308]: mismatched types
++ --> $DIR/builtin-type-shadow.rs:5:5
++ |
++LL | fn foo<u32>(a: u32) -> u32 {
++ | --- --- expected `u32` because of return type
++ | |
++ | this type parameter
++LL | 42
++ | ^^ expected type parameter `u32`, found integer
++ |
++ = note: expected type parameter `u32`
++ found type `{integer}`
++ = help: type parameters must be constrained to match other types
++ = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
++
++error: aborting due to 2 previous errors
++
++For more information about this error, try `rustc --explain E0308`.
--- /dev/null
--- /dev/null
++#[deny(clippy::naive_bytecount)]
++fn main() {
++ let x = vec![0_u8; 16];
++
++ let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
++
++ let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
++
++ let _ = x.iter().filter(|a| **a > 0).count(); // not an equality count, OK.
++
++ let _ = x.iter().map(|a| a + 1).filter(|&a| a < 15).count(); // not a slice
++
++ let b = 0;
++
++ let _ = x.iter().filter(|_| b > 0).count(); // woah there
++
++ let _ = x.iter().filter(|_a| b == b + 1).count(); // nothing to see here, move along
++
++ let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
++
++ let y = vec![0_u16; 3];
++
++ let _ = y.iter().filter(|&&a| a == 0).count(); // naive count, but not bytes
++}
--- /dev/null
--- /dev/null
++error: You appear to be counting bytes the naive way
++ --> $DIR/bytecount.rs:5:13
++ |
++LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)`
++ |
++note: the lint level is defined here
++ --> $DIR/bytecount.rs:1:8
++ |
++LL | #[deny(clippy::naive_bytecount)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: You appear to be counting bytes the naive way
++ --> $DIR/bytecount.rs:7:13
++ |
++LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
++
++error: You appear to be counting bytes the naive way
++ --> $DIR/bytecount.rs:19:13
++ |
++LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(
++ clippy::cast_precision_loss,
++ clippy::cast_possible_truncation,
++ clippy::cast_sign_loss,
++ clippy::cast_possible_wrap
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++ // Test clippy::cast_precision_loss
++ let x0 = 1i32;
++ x0 as f32;
++ let x1 = 1i64;
++ x1 as f32;
++ x1 as f64;
++ let x2 = 1u32;
++ x2 as f32;
++ let x3 = 1u64;
++ x3 as f32;
++ x3 as f64;
++ // Test clippy::cast_possible_truncation
++ 1f32 as i32;
++ 1f32 as u32;
++ 1f64 as f32;
++ 1i32 as i8;
++ 1i32 as u8;
++ 1f64 as isize;
++ 1f64 as usize;
++ // Test clippy::cast_possible_wrap
++ 1u8 as i8;
++ 1u16 as i16;
++ 1u32 as i32;
++ 1u64 as i64;
++ 1usize as isize;
++ // Test clippy::cast_sign_loss
++ 1i32 as u32;
++ -1i32 as u32;
++ 1isize as usize;
++ -1isize as usize;
++ 0i8 as u8;
++ i8::max_value() as u8;
++ i16::max_value() as u16;
++ i32::max_value() as u32;
++ i64::max_value() as u64;
++ i128::max_value() as u128;
++
++ (-1i8).abs() as u8;
++ (-1i16).abs() as u16;
++ (-1i32).abs() as u32;
++ (-1i64).abs() as u64;
++ (-1isize).abs() as usize;
++
++ (-1i8).checked_abs().unwrap() as u8;
++ (-1i16).checked_abs().unwrap() as u16;
++ (-1i32).checked_abs().unwrap() as u32;
++ (-1i64).checked_abs().unwrap() as u64;
++ (-1isize).checked_abs().unwrap() as usize;
++
++ (-1i8).rem_euclid(1i8) as u8;
++ (-1i8).rem_euclid(1i8) as u16;
++ (-1i16).rem_euclid(1i16) as u16;
++ (-1i16).rem_euclid(1i16) as u32;
++ (-1i32).rem_euclid(1i32) as u32;
++ (-1i32).rem_euclid(1i32) as u64;
++ (-1i64).rem_euclid(1i64) as u64;
++ (-1i64).rem_euclid(1i64) as u128;
++ (-1isize).rem_euclid(1isize) as usize;
++ (1i8).rem_euclid(-1i8) as u8;
++ (1i8).rem_euclid(-1i8) as u16;
++ (1i16).rem_euclid(-1i16) as u16;
++ (1i16).rem_euclid(-1i16) as u32;
++ (1i32).rem_euclid(-1i32) as u32;
++ (1i32).rem_euclid(-1i32) as u64;
++ (1i64).rem_euclid(-1i64) as u64;
++ (1i64).rem_euclid(-1i64) as u128;
++ (1isize).rem_euclid(-1isize) as usize;
++
++ (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
++ (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
++ (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
++ (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
++ (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
++ (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
++ (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
++ (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
++ (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
++ (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
++ (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
++ (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
++ (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
++ (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
++ (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
++ (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
++ (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
++ (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
++}
--- /dev/null
--- /dev/null
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast.rs:11:5
++ |
++LL | x0 as f32;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast.rs:13:5
++ |
++LL | x1 as f32;
++ | ^^^^^^^^^
++
++error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast.rs:14:5
++ |
++LL | x1 as f64;
++ | ^^^^^^^^^
++
++error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast.rs:16:5
++ |
++LL | x2 as f32;
++ | ^^^^^^^^^
++
++error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast.rs:18:5
++ |
++LL | x3 as f32;
++ | ^^^^^^^^^
++
++error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast.rs:19:5
++ |
++LL | x3 as f64;
++ | ^^^^^^^^^
++
++error: casting `f32` to `i32` may truncate the value
++ --> $DIR/cast.rs:21:5
++ |
++LL | 1f32 as i32;
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `f32` to `u32` may truncate the value
++ --> $DIR/cast.rs:22:5
++ |
++LL | 1f32 as u32;
++ | ^^^^^^^^^^^
++
++error: casting `f32` to `u32` may lose the sign of the value
++ --> $DIR/cast.rs:22:5
++ |
++LL | 1f32 as u32;
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
++
++error: casting `f64` to `f32` may truncate the value
++ --> $DIR/cast.rs:23:5
++ |
++LL | 1f64 as f32;
++ | ^^^^^^^^^^^
++
++error: casting `i32` to `i8` may truncate the value
++ --> $DIR/cast.rs:24:5
++ |
++LL | 1i32 as i8;
++ | ^^^^^^^^^^
++
++error: casting `i32` to `u8` may truncate the value
++ --> $DIR/cast.rs:25:5
++ |
++LL | 1i32 as u8;
++ | ^^^^^^^^^^
++
++error: casting `f64` to `isize` may truncate the value
++ --> $DIR/cast.rs:26:5
++ |
++LL | 1f64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `f64` to `usize` may truncate the value
++ --> $DIR/cast.rs:27:5
++ |
++LL | 1f64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `f64` to `usize` may lose the sign of the value
++ --> $DIR/cast.rs:27:5
++ |
++LL | 1f64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u8` to `i8` may wrap around the value
++ --> $DIR/cast.rs:29:5
++ |
++LL | 1u8 as i8;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `u16` to `i16` may wrap around the value
++ --> $DIR/cast.rs:30:5
++ |
++LL | 1u16 as i16;
++ | ^^^^^^^^^^^
++
++error: casting `u32` to `i32` may wrap around the value
++ --> $DIR/cast.rs:31:5
++ |
++LL | 1u32 as i32;
++ | ^^^^^^^^^^^
++
++error: casting `u64` to `i64` may wrap around the value
++ --> $DIR/cast.rs:32:5
++ |
++LL | 1u64 as i64;
++ | ^^^^^^^^^^^
++
++error: casting `usize` to `isize` may wrap around the value
++ --> $DIR/cast.rs:33:5
++ |
++LL | 1usize as isize;
++ | ^^^^^^^^^^^^^^^
++
++error: casting `i32` to `u32` may lose the sign of the value
++ --> $DIR/cast.rs:36:5
++ |
++LL | -1i32 as u32;
++ | ^^^^^^^^^^^^
++
++error: casting `isize` to `usize` may lose the sign of the value
++ --> $DIR/cast.rs:38:5
++ |
++LL | -1isize as usize;
++ | ^^^^^^^^^^^^^^^^
++
++error: aborting due to 22 previous errors
++
--- /dev/null
--- /dev/null
++//! Test casts for alignment issues
++
++#![feature(rustc_private)]
++extern crate libc;
++
++#[warn(clippy::cast_ptr_alignment)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)]
++fn main() {
++ /* These should be warned against */
++
++ // cast to more-strictly-aligned type
++ (&1u8 as *const u8) as *const u16;
++ (&mut 1u8 as *mut u8) as *mut u16;
++
++ /* These should be ok */
++
++ // not a pointer type
++ 1u8 as u16;
++ // cast to less-strictly-aligned type
++ (&1u16 as *const u16) as *const u8;
++ (&mut 1u16 as *mut u16) as *mut u8;
++ // For c_void, we should trust the user. See #2677
++ (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32;
++ (&1u32 as *const u32 as *const libc::c_void) as *const u32;
++ // For ZST, we should trust the user. See #4256
++ (&1u32 as *const u32 as *const ()) as *const u32;
++}
--- /dev/null
--- /dev/null
++error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
++ --> $DIR/cast_alignment.rs:12:5
++ |
++LL | (&1u8 as *const u8) as *const u16;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings`
++
++error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
++ --> $DIR/cast_alignment.rs:13:5
++ |
++LL | (&mut 1u8 as *mut u8) as *mut u16;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++ // Test clippy::cast_lossless with casts to floating-point types
++ let x0 = 1i8;
++ f32::from(x0);
++ f64::from(x0);
++ let x1 = 1u8;
++ f32::from(x1);
++ f64::from(x1);
++ let x2 = 1i16;
++ f32::from(x2);
++ f64::from(x2);
++ let x3 = 1u16;
++ f32::from(x3);
++ f64::from(x3);
++ let x4 = 1i32;
++ f64::from(x4);
++ let x5 = 1u32;
++ f64::from(x5);
++
++ // Test with casts from floating-point types
++ f64::from(1.0f32);
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: f32) -> f64 {
++ input as f64
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++ struct A;
++
++ impl A {
++ pub const fn convert(x: f32) -> f64 {
++ x as f64
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++ // Test clippy::cast_lossless with casts to floating-point types
++ let x0 = 1i8;
++ x0 as f32;
++ x0 as f64;
++ let x1 = 1u8;
++ x1 as f32;
++ x1 as f64;
++ let x2 = 1i16;
++ x2 as f32;
++ x2 as f64;
++ let x3 = 1u16;
++ x3 as f32;
++ x3 as f64;
++ let x4 = 1i32;
++ x4 as f64;
++ let x5 = 1u32;
++ x5 as f64;
++
++ // Test with casts from floating-point types
++ 1.0f32 as f64;
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: f32) -> f64 {
++ input as f64
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++ struct A;
++
++ impl A {
++ pub const fn convert(x: f32) -> f64 {
++ x as f64
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: casting `i8` to `f32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:9:5
++ |
++LL | x0 as f32;
++ | ^^^^^^^^^ help: try: `f32::from(x0)`
++ |
++ = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `i8` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:10:5
++ |
++LL | x0 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x0)`
++
++error: casting `u8` to `f32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:12:5
++ |
++LL | x1 as f32;
++ | ^^^^^^^^^ help: try: `f32::from(x1)`
++
++error: casting `u8` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:13:5
++ |
++LL | x1 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x1)`
++
++error: casting `i16` to `f32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:15:5
++ |
++LL | x2 as f32;
++ | ^^^^^^^^^ help: try: `f32::from(x2)`
++
++error: casting `i16` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:16:5
++ |
++LL | x2 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x2)`
++
++error: casting `u16` to `f32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:18:5
++ |
++LL | x3 as f32;
++ | ^^^^^^^^^ help: try: `f32::from(x3)`
++
++error: casting `u16` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:19:5
++ |
++LL | x3 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x3)`
++
++error: casting `i32` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:21:5
++ |
++LL | x4 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x4)`
++
++error: casting `u32` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:23:5
++ |
++LL | x5 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x5)`
++
++error: casting `f32` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_float.rs:26:5
++ |
++LL | 1.0f32 as f64;
++ | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++ // Test clippy::cast_lossless with casts to integer types
++ i16::from(1i8);
++ i32::from(1i8);
++ i64::from(1i8);
++ i16::from(1u8);
++ i32::from(1u8);
++ i64::from(1u8);
++ u16::from(1u8);
++ u32::from(1u8);
++ u64::from(1u8);
++ i32::from(1i16);
++ i64::from(1i16);
++ i32::from(1u16);
++ i64::from(1u16);
++ u32::from(1u16);
++ u64::from(1u16);
++ i64::from(1i32);
++ i64::from(1u32);
++ u64::from(1u32);
++
++ // Test with an expression wrapped in parens
++ u16::from(1u8 + 1u8);
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: u16) -> u32 {
++ input as u32
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++ struct A;
++
++ impl A {
++ pub const fn convert(x: u32) -> u64 {
++ x as u64
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++ // Test clippy::cast_lossless with casts to integer types
++ 1i8 as i16;
++ 1i8 as i32;
++ 1i8 as i64;
++ 1u8 as i16;
++ 1u8 as i32;
++ 1u8 as i64;
++ 1u8 as u16;
++ 1u8 as u32;
++ 1u8 as u64;
++ 1i16 as i32;
++ 1i16 as i64;
++ 1u16 as i32;
++ 1u16 as i64;
++ 1u16 as u32;
++ 1u16 as u64;
++ 1i32 as i64;
++ 1u32 as i64;
++ 1u32 as u64;
++
++ // Test with an expression wrapped in parens
++ (1u8 + 1u8) as u16;
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: u16) -> u32 {
++ input as u32
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++ struct A;
++
++ impl A {
++ pub const fn convert(x: u32) -> u64 {
++ x as u64
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: casting `i8` to `i16` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:8:5
++ |
++LL | 1i8 as i16;
++ | ^^^^^^^^^^ help: try: `i16::from(1i8)`
++ |
++ = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `i8` to `i32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:9:5
++ |
++LL | 1i8 as i32;
++ | ^^^^^^^^^^ help: try: `i32::from(1i8)`
++
++error: casting `i8` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:10:5
++ |
++LL | 1i8 as i64;
++ | ^^^^^^^^^^ help: try: `i64::from(1i8)`
++
++error: casting `u8` to `i16` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:11:5
++ |
++LL | 1u8 as i16;
++ | ^^^^^^^^^^ help: try: `i16::from(1u8)`
++
++error: casting `u8` to `i32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:12:5
++ |
++LL | 1u8 as i32;
++ | ^^^^^^^^^^ help: try: `i32::from(1u8)`
++
++error: casting `u8` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:13:5
++ |
++LL | 1u8 as i64;
++ | ^^^^^^^^^^ help: try: `i64::from(1u8)`
++
++error: casting `u8` to `u16` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:14:5
++ |
++LL | 1u8 as u16;
++ | ^^^^^^^^^^ help: try: `u16::from(1u8)`
++
++error: casting `u8` to `u32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:15:5
++ |
++LL | 1u8 as u32;
++ | ^^^^^^^^^^ help: try: `u32::from(1u8)`
++
++error: casting `u8` to `u64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:16:5
++ |
++LL | 1u8 as u64;
++ | ^^^^^^^^^^ help: try: `u64::from(1u8)`
++
++error: casting `i16` to `i32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:17:5
++ |
++LL | 1i16 as i32;
++ | ^^^^^^^^^^^ help: try: `i32::from(1i16)`
++
++error: casting `i16` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:18:5
++ |
++LL | 1i16 as i64;
++ | ^^^^^^^^^^^ help: try: `i64::from(1i16)`
++
++error: casting `u16` to `i32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:19:5
++ |
++LL | 1u16 as i32;
++ | ^^^^^^^^^^^ help: try: `i32::from(1u16)`
++
++error: casting `u16` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:20:5
++ |
++LL | 1u16 as i64;
++ | ^^^^^^^^^^^ help: try: `i64::from(1u16)`
++
++error: casting `u16` to `u32` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:21:5
++ |
++LL | 1u16 as u32;
++ | ^^^^^^^^^^^ help: try: `u32::from(1u16)`
++
++error: casting `u16` to `u64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:22:5
++ |
++LL | 1u16 as u64;
++ | ^^^^^^^^^^^ help: try: `u64::from(1u16)`
++
++error: casting `i32` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:23:5
++ |
++LL | 1i32 as i64;
++ | ^^^^^^^^^^^ help: try: `i64::from(1i32)`
++
++error: casting `u32` to `i64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:24:5
++ |
++LL | 1u32 as i64;
++ | ^^^^^^^^^^^ help: try: `i64::from(1u32)`
++
++error: casting `u32` to `u64` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:25:5
++ |
++LL | 1u32 as u64;
++ | ^^^^^^^^^^^ help: try: `u64::from(1u32)`
++
++error: casting `u8` to `u16` may become silently lossy if you later change the type
++ --> $DIR/cast_lossless_integer.rs:28:5
++ |
++LL | (1u8 + 1u8) as u16;
++ | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)`
++
++error: aborting due to 19 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::cast_ref_to_mut)]
++#![allow(clippy::no_effect)]
++
++extern "C" {
++ // N.B., mutability can be easily incorrect in FFI calls -- as
++ // in C, the default is mutable pointers.
++ fn ffi(c: *mut u8);
++ fn int_ffi(c: *mut i32);
++}
++
++fn main() {
++ let s = String::from("Hello");
++ let a = &s;
++ unsafe {
++ let num = &3i32;
++ let mut_num = &mut 3i32;
++ // Should be warned against
++ (*(a as *const _ as *mut String)).push_str(" world");
++ *(a as *const _ as *mut _) = String::from("Replaced");
++ *(a as *const _ as *mut String) += " world";
++ // Shouldn't be warned against
++ println!("{}", *(num as *const _ as *const i16));
++ println!("{}", *(mut_num as *mut _ as *mut i16));
++ ffi(a.as_ptr() as *mut _);
++ int_ffi(num as *const _ as *mut _);
++ int_ffi(&3 as *const _ as *mut _);
++ let mut value = 3;
++ let value: *const i32 = &mut value;
++ *(value as *const i16 as *mut i16) = 42;
++ }
++}
--- /dev/null
--- /dev/null
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++ --> $DIR/cast_ref_to_mut.rs:18:9
++ |
++LL | (*(a as *const _ as *mut String)).push_str(" world");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings`
++
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++ --> $DIR/cast_ref_to_mut.rs:19:9
++ |
++LL | *(a as *const _ as *mut _) = String::from("Replaced");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++ --> $DIR/cast_ref_to_mut.rs:20:9
++ |
++LL | *(a as *const _ as *mut String) += " world";
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-32bit
++#[warn(
++ clippy::cast_precision_loss,
++ clippy::cast_possible_truncation,
++ clippy::cast_sign_loss,
++ clippy::cast_possible_wrap,
++ clippy::cast_lossless
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++ // Casting from *size
++ 1isize as i8;
++ let x0 = 1isize;
++ let x1 = 1usize;
++ x0 as f64;
++ x1 as f64;
++ x0 as f32;
++ x1 as f32;
++ 1isize as i32;
++ 1isize as u32;
++ 1usize as u32;
++ 1usize as i32;
++ // Casting to *size
++ 1i64 as isize;
++ 1i64 as usize;
++ 1u64 as isize;
++ 1u64 as usize;
++ 1u32 as isize;
++ 1u32 as usize; // Should not trigger any lint
++ 1i32 as isize; // Neither should this
++ 1i32 as usize;
++ // Big integer literal to float
++ 999_999_999 as f32;
++ 9_999_999_999_999_999usize as f64;
++}
--- /dev/null
--- /dev/null
++error: casting `isize` to `i8` may truncate the value
++ --> $DIR/cast_size.rs:12:5
++ |
++LL | 1isize as i8;
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast_size.rs:15:5
++ |
++LL | x0 as f64;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast_size.rs:16:5
++ |
++LL | x1 as f64;
++ | ^^^^^^^^^
++
++error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size.rs:17:5
++ |
++LL | x0 as f32;
++ | ^^^^^^^^^
++
++error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size.rs:18:5
++ |
++LL | x1 as f32;
++ | ^^^^^^^^^
++
++error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size.rs:19:5
++ |
++LL | 1isize as i32;
++ | ^^^^^^^^^^^^^
++
++error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size.rs:20:5
++ |
++LL | 1isize as u32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size.rs:21:5
++ |
++LL | 1usize as u32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size.rs:22:5
++ |
++LL | 1usize as i32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:22:5
++ |
++LL | 1usize as i32;
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:24:5
++ |
++LL | 1i64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:25:5
++ |
++LL | 1i64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:26:5
++ |
++LL | 1u64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size.rs:26:5
++ |
++LL | 1u64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:27:5
++ |
++LL | 1u64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size.rs:28:5
++ |
++LL | 1u32 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size.rs:33:5
++ |
++LL | 999_999_999 as f32;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast_size.rs:34:5
++ |
++LL | 9_999_999_999_999_999usize as f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-64bit
++#[warn(
++ clippy::cast_precision_loss,
++ clippy::cast_possible_truncation,
++ clippy::cast_sign_loss,
++ clippy::cast_possible_wrap,
++ clippy::cast_lossless
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++ // Casting from *size
++ 1isize as i8;
++ let x0 = 1isize;
++ let x1 = 1usize;
++ x0 as f64;
++ x1 as f64;
++ x0 as f32;
++ x1 as f32;
++ 1isize as i32;
++ 1isize as u32;
++ 1usize as u32;
++ 1usize as i32;
++ // Casting to *size
++ 1i64 as isize;
++ 1i64 as usize;
++ 1u64 as isize;
++ 1u64 as usize;
++ 1u32 as isize;
++ 1u32 as usize; // Should not trigger any lint
++ 1i32 as isize; // Neither should this
++ 1i32 as usize;
++ // Big integer literal to float
++ 999_999_999 as f32;
++ 3_999_999_999usize as f64;
++}
--- /dev/null
--- /dev/null
++error: casting `isize` to `i8` may truncate the value
++ --> $DIR/cast_size_32bit.rs:12:5
++ |
++LL | 1isize as i8;
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast_size_32bit.rs:15:5
++ |
++LL | x0 as f64;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `isize` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_size_32bit.rs:15:5
++ |
++LL | x0 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x0)`
++ |
++ = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++ --> $DIR/cast_size_32bit.rs:16:5
++ |
++LL | x1 as f64;
++ | ^^^^^^^^^
++
++error: casting `usize` to `f64` may become silently lossy if you later change the type
++ --> $DIR/cast_size_32bit.rs:16:5
++ |
++LL | x1 as f64;
++ | ^^^^^^^^^ help: try: `f64::from(x1)`
++
++error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size_32bit.rs:17:5
++ |
++LL | x0 as f32;
++ | ^^^^^^^^^
++
++error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size_32bit.rs:18:5
++ |
++LL | x1 as f32;
++ | ^^^^^^^^^
++
++error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:19:5
++ |
++LL | 1isize as i32;
++ | ^^^^^^^^^^^^^
++
++error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:20:5
++ |
++LL | 1isize as u32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:21:5
++ |
++LL | 1usize as u32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:22:5
++ |
++LL | 1usize as i32;
++ | ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:22:5
++ |
++LL | 1usize as i32;
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:24:5
++ |
++LL | 1i64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:25:5
++ |
++LL | 1i64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:26:5
++ |
++LL | 1u64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:26:5
++ |
++LL | 1u64 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:27:5
++ |
++LL | 1u64 as usize;
++ | ^^^^^^^^^^^^^
++
++error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
++ --> $DIR/cast_size_32bit.rs:28:5
++ |
++LL | 1u32 as isize;
++ | ^^^^^^^^^^^^^
++
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++ --> $DIR/cast_size_32bit.rs:33:5
++ |
++LL | 999_999_999 as f32;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: casting integer literal to `f64` is unnecessary
++ --> $DIR/cast_size_32bit.rs:34:5
++ |
++LL | 3_999_999_999usize as f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64`
++ |
++ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++
++#![allow(unused, clippy::no_effect)]
++#![warn(clippy::deprecated_cfg_attr)]
++
++// This doesn't get linted, see known problems
++#![cfg_attr(rustfmt, rustfmt_skip)]
++
++#[rustfmt::skip]
++trait Foo
++{
++fn foo(
++);
++}
++
++fn skip_on_statements() {
++ #[rustfmt::skip]
++ 5+3;
++}
++
++#[rustfmt::skip]
++fn main() {
++ foo::f();
++}
++
++mod foo {
++ #![cfg_attr(rustfmt, rustfmt_skip)]
++
++ pub fn f() {}
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++
++#![allow(unused, clippy::no_effect)]
++#![warn(clippy::deprecated_cfg_attr)]
++
++// This doesn't get linted, see known problems
++#![cfg_attr(rustfmt, rustfmt_skip)]
++
++#[rustfmt::skip]
++trait Foo
++{
++fn foo(
++);
++}
++
++fn skip_on_statements() {
++ #[cfg_attr(rustfmt, rustfmt::skip)]
++ 5+3;
++}
++
++#[cfg_attr(rustfmt, rustfmt_skip)]
++fn main() {
++ foo::f();
++}
++
++mod foo {
++ #![cfg_attr(rustfmt, rustfmt_skip)]
++
++ pub fn f() {}
++}
--- /dev/null
--- /dev/null
++error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
++ --> $DIR/cfg_attr_rustfmt.rs:18:5
++ |
++LL | #[cfg_attr(rustfmt, rustfmt::skip)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
++ |
++ = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings`
++
++error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
++ --> $DIR/cfg_attr_rustfmt.rs:22:1
++ |
++LL | #[cfg_attr(rustfmt, rustfmt_skip)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++ let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
++}
--- /dev/null
--- /dev/null
++error: casting a character literal to `u8` truncates
++ --> $DIR/char_lit_as_u8.rs:4:13
++ |
++LL | let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
++ = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++ let _ = b'a';
++ let _ = b'\n';
++ let _ = b'\0';
++ let _ = b'\x01';
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++ let _ = 'a' as u8;
++ let _ = '\n' as u8;
++ let _ = '\0' as u8;
++ let _ = '\x01' as u8;
++}
--- /dev/null
--- /dev/null
++error: casting a character literal to `u8` truncates
++ --> $DIR/char_lit_as_u8_suggestions.rs:6:13
++ |
++LL | let _ = 'a' as u8;
++ | ^^^^^^^^^ help: use a byte literal instead: `b'a'`
++ |
++ = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
++ = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++ --> $DIR/char_lit_as_u8_suggestions.rs:7:13
++ |
++LL | let _ = '/n' as u8;
++ | ^^^^^^^^^^ help: use a byte literal instead: `b'/n'`
++ |
++ = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++ --> $DIR/char_lit_as_u8_suggestions.rs:8:13
++ |
++LL | let _ = '/0' as u8;
++ | ^^^^^^^^^^ help: use a byte literal instead: `b'/0'`
++ |
++ = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++ --> $DIR/char_lit_as_u8_suggestions.rs:9:13
++ |
++LL | let _ = '/x01' as u8;
++ | ^^^^^^^^^^^^ help: use a byte literal instead: `b'/x01'`
++ |
++ = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::checked_conversions)]
++#![allow(clippy::cast_lossless)]
++#![allow(dead_code)]
++use std::convert::TryFrom;
++
++// Positive tests
++
++// Signed to unsigned
++
++fn i64_to_u32(value: i64) -> Option<u32> {
++ if u32::try_from(value).is_ok() {
++ Some(value as u32)
++ } else {
++ None
++ }
++}
++
++fn i64_to_u16(value: i64) -> Option<u16> {
++ if u16::try_from(value).is_ok() {
++ Some(value as u16)
++ } else {
++ None
++ }
++}
++
++fn isize_to_u8(value: isize) -> Option<u8> {
++ if u8::try_from(value).is_ok() {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++// Signed to signed
++
++fn i64_to_i32(value: i64) -> Option<i32> {
++ if i32::try_from(value).is_ok() {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn i64_to_i16(value: i64) -> Option<i16> {
++ if i16::try_from(value).is_ok() {
++ Some(value as i16)
++ } else {
++ None
++ }
++}
++
++// Unsigned to X
++
++fn u32_to_i32(value: u32) -> Option<i32> {
++ if i32::try_from(value).is_ok() {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn usize_to_isize(value: usize) -> isize {
++ if isize::try_from(value).is_ok() && value as i32 == 5 {
++ 5
++ } else {
++ 1
++ }
++}
++
++fn u32_to_u16(value: u32) -> isize {
++ if u16::try_from(value).is_ok() && value as i32 == 5 {
++ 5
++ } else {
++ 1
++ }
++}
++
++// Negative tests
++
++fn no_i64_to_i32(value: i64) -> Option<i32> {
++ if value <= (i32::max_value() as i64) && value >= 0 {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn no_isize_to_u8(value: isize) -> Option<u8> {
++ if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++fn i8_to_u8(value: i8) -> Option<u8> {
++ if value >= 0 {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::checked_conversions)]
++#![allow(clippy::cast_lossless)]
++#![allow(dead_code)]
++use std::convert::TryFrom;
++
++// Positive tests
++
++// Signed to unsigned
++
++fn i64_to_u32(value: i64) -> Option<u32> {
++ if value <= (u32::max_value() as i64) && value >= 0 {
++ Some(value as u32)
++ } else {
++ None
++ }
++}
++
++fn i64_to_u16(value: i64) -> Option<u16> {
++ if value <= i64::from(u16::max_value()) && value >= 0 {
++ Some(value as u16)
++ } else {
++ None
++ }
++}
++
++fn isize_to_u8(value: isize) -> Option<u8> {
++ if value <= (u8::max_value() as isize) && value >= 0 {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++// Signed to signed
++
++fn i64_to_i32(value: i64) -> Option<i32> {
++ if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn i64_to_i16(value: i64) -> Option<i16> {
++ if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
++ Some(value as i16)
++ } else {
++ None
++ }
++}
++
++// Unsigned to X
++
++fn u32_to_i32(value: u32) -> Option<i32> {
++ if value <= i32::max_value() as u32 {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn usize_to_isize(value: usize) -> isize {
++ if value <= isize::max_value() as usize && value as i32 == 5 {
++ 5
++ } else {
++ 1
++ }
++}
++
++fn u32_to_u16(value: u32) -> isize {
++ if value <= u16::max_value() as u32 && value as i32 == 5 {
++ 5
++ } else {
++ 1
++ }
++}
++
++// Negative tests
++
++fn no_i64_to_i32(value: i64) -> Option<i32> {
++ if value <= (i32::max_value() as i64) && value >= 0 {
++ Some(value as i32)
++ } else {
++ None
++ }
++}
++
++fn no_isize_to_u8(value: isize) -> Option<u8> {
++ if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++fn i8_to_u8(value: i8) -> Option<u8> {
++ if value >= 0 {
++ Some(value as u8)
++ } else {
++ None
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:13:8
++ |
++LL | if value <= (u32::max_value() as i64) && value >= 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
++ |
++ = note: `-D clippy::checked-conversions` implied by `-D warnings`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:21:8
++ |
++LL | if value <= i64::from(u16::max_value()) && value >= 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:29:8
++ |
++LL | if value <= (u8::max_value() as isize) && value >= 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:39:8
++ |
++LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:47:8
++ |
++LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:57:8
++ |
++LL | if value <= i32::max_value() as u32 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:65:8
++ |
++LL | if value <= isize::max_value() as usize && value as i32 == 5 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++ --> $DIR/checked_conversions.rs:73:8
++ |
++LL | if value <= u16::max_value() as u32 && value as i32 == 5 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++fn test_complex_conditions() {
++ let x: Result<(), ()> = Ok(());
++ let y: Result<(), ()> = Ok(());
++ if x.is_ok() && y.is_err() {
++ x.unwrap(); // unnecessary
++ x.unwrap_err(); // will panic
++ y.unwrap(); // will panic
++ y.unwrap_err(); // unnecessary
++ } else {
++ // not statically determinable whether any of the following will always succeed or always fail:
++ x.unwrap();
++ x.unwrap_err();
++ y.unwrap();
++ y.unwrap_err();
++ }
++
++ if x.is_ok() || y.is_ok() {
++ // not statically determinable whether any of the following will always succeed or always fail:
++ x.unwrap();
++ y.unwrap();
++ } else {
++ x.unwrap(); // will panic
++ x.unwrap_err(); // unnecessary
++ y.unwrap(); // will panic
++ y.unwrap_err(); // unnecessary
++ }
++ let z: Result<(), ()> = Ok(());
++ if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ x.unwrap(); // unnecessary
++ x.unwrap_err(); // will panic
++ y.unwrap(); // will panic
++ y.unwrap_err(); // unnecessary
++ z.unwrap(); // unnecessary
++ z.unwrap_err(); // will panic
++ }
++ if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ // not statically determinable whether any of the following will always succeed or always fail:
++ x.unwrap();
++ y.unwrap();
++ z.unwrap();
++ } else {
++ x.unwrap(); // will panic
++ x.unwrap_err(); // unnecessary
++ y.unwrap(); // unnecessary
++ y.unwrap_err(); // will panic
++ z.unwrap(); // will panic
++ z.unwrap_err(); // unnecessary
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:8:9
++ |
++LL | if x.is_ok() && y.is_err() {
++ | --------- the check is happening here
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/complex_conditionals.rs:1:35
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/complex_conditionals.rs:9:9
++ |
++LL | if x.is_ok() && y.is_err() {
++ | --------- because of this check
++LL | x.unwrap(); // unnecessary
++LL | x.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/complex_conditionals.rs:1:9
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:10:9
++ |
++LL | if x.is_ok() && y.is_err() {
++ | ---------- because of this check
++...
++LL | y.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:11:9
++ |
++LL | if x.is_ok() && y.is_err() {
++ | ---------- the check is happening here
++...
++LL | y.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:25:9
++ |
++LL | if x.is_ok() || y.is_ok() {
++ | --------- because of this check
++...
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:26:9
++ |
++LL | if x.is_ok() || y.is_ok() {
++ | --------- the check is happening here
++...
++LL | x.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:27:9
++ |
++LL | if x.is_ok() || y.is_ok() {
++ | --------- because of this check
++...
++LL | y.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:28:9
++ |
++LL | if x.is_ok() || y.is_ok() {
++ | --------- the check is happening here
++...
++LL | y.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:32:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | --------- the check is happening here
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/complex_conditionals.rs:33:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | --------- because of this check
++LL | x.unwrap(); // unnecessary
++LL | x.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:34:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | --------- because of this check
++...
++LL | y.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:35:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | --------- the check is happening here
++...
++LL | y.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:36:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | ---------- the check is happening here
++...
++LL | z.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/complex_conditionals.rs:37:9
++ |
++LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
++ | ---------- because of this check
++...
++LL | z.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:45:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | --------- because of this check
++...
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:46:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | --------- the check is happening here
++...
++LL | x.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:47:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | --------- the check is happening here
++...
++LL | y.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/complex_conditionals.rs:48:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | --------- because of this check
++...
++LL | y.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals.rs:49:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | ---------- because of this check
++...
++LL | z.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals.rs:50:9
++ |
++LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
++ | ---------- the check is happening here
++...
++LL | z.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++fn test_nested() {
++ fn nested() {
++ let x = Some(());
++ if x.is_some() {
++ x.unwrap(); // unnecessary
++ } else {
++ x.unwrap(); // will panic
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/complex_conditionals_nested.rs:8:13
++ |
++LL | if x.is_some() {
++ | ----------- the check is happening here
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/complex_conditionals_nested.rs:1:35
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/complex_conditionals_nested.rs:10:13
++ |
++LL | if x.is_some() {
++ | ----------- because of this check
++...
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/complex_conditionals_nested.rs:1:9
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++macro_rules! m {
++ ($a:expr) => {
++ if $a.is_some() {
++ $a.unwrap(); // unnecessary
++ }
++ };
++}
++
++fn main() {
++ let x = Some(());
++ if x.is_some() {
++ x.unwrap(); // unnecessary
++ } else {
++ x.unwrap(); // will panic
++ }
++ if x.is_none() {
++ x.unwrap(); // will panic
++ } else {
++ x.unwrap(); // unnecessary
++ }
++ m!(x);
++ let mut x: Result<(), ()> = Ok(());
++ if x.is_ok() {
++ x.unwrap(); // unnecessary
++ x.unwrap_err(); // will panic
++ } else {
++ x.unwrap(); // will panic
++ x.unwrap_err(); // unnecessary
++ }
++ if x.is_err() {
++ x.unwrap(); // will panic
++ x.unwrap_err(); // unnecessary
++ } else {
++ x.unwrap(); // unnecessary
++ x.unwrap_err(); // will panic
++ }
++ if x.is_ok() {
++ x = Err(());
++ x.unwrap(); // not unnecessary because of mutation of x
++ // it will always panic but the lint is not smart enough to see this (it only
++ // checks if conditions).
++ } else {
++ x = Ok(());
++ x.unwrap_err(); // not unnecessary because of mutation of x
++ // it will always panic but the lint is not smart enough to see this (it
++ // only checks if conditions).
++ }
++
++ assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
++}
--- /dev/null
--- /dev/null
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:15:9
++ |
++LL | if x.is_some() {
++ | ----------- the check is happening here
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/simple_conditionals.rs:1:35
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/simple_conditionals.rs:17:9
++ |
++LL | if x.is_some() {
++ | ----------- because of this check
++...
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/simple_conditionals.rs:1:9
++ |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/simple_conditionals.rs:20:9
++ |
++LL | if x.is_none() {
++ | ----------- because of this check
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:22:9
++ |
++LL | if x.is_none() {
++ | ----------- the check is happening here
++...
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:7:13
++ |
++LL | if $a.is_some() {
++ | ------------ the check is happening here
++LL | $a.unwrap(); // unnecessary
++ | ^^^^^^^^^^^
++...
++LL | m!(x);
++ | ------ in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:27:9
++ |
++LL | if x.is_ok() {
++ | --------- the check is happening here
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/simple_conditionals.rs:28:9
++ |
++LL | if x.is_ok() {
++ | --------- because of this check
++LL | x.unwrap(); // unnecessary
++LL | x.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/simple_conditionals.rs:30:9
++ |
++LL | if x.is_ok() {
++ | --------- because of this check
++...
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:31:9
++ |
++LL | if x.is_ok() {
++ | --------- the check is happening here
++...
++LL | x.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++ --> $DIR/simple_conditionals.rs:34:9
++ |
++LL | if x.is_err() {
++ | ---------- because of this check
++LL | x.unwrap(); // will panic
++ | ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:35:9
++ |
++LL | if x.is_err() {
++ | ---------- the check is happening here
++LL | x.unwrap(); // will panic
++LL | x.unwrap_err(); // unnecessary
++ | ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++ --> $DIR/simple_conditionals.rs:37:9
++ |
++LL | if x.is_err() {
++ | ---------- the check is happening here
++...
++LL | x.unwrap(); // unnecessary
++ | ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++ --> $DIR/simple_conditionals.rs:38:9
++ |
++LL | if x.is_err() {
++ | ---------- because of this check
++...
++LL | x.unwrap_err(); // will panic
++ | ^^^^^^^^^^^^^^
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++use std::fmt;
++use std::marker::PhantomData;
++
++pub struct Key<T> {
++ #[doc(hidden)]
++ pub __name: &'static str,
++ #[doc(hidden)]
++ pub __phantom: PhantomData<T>,
++}
++
++impl<T> Copy for Key<T> {}
++
++impl<T> Clone for Key<T> {
++ fn clone(&self) -> Self {
++ Key {
++ __name: self.__name,
++ __phantom: self.__phantom,
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++pub fn dec_read_dec(i: &mut i32) -> i32 {
++ *i -= 1;
++ let ret = *i;
++ *i -= 1;
++ ret
++}
++
++pub fn minus_1(i: &i32) -> i32 {
++ dec_read_dec(&mut i.clone())
++}
++
++fn main() {
++ let mut i = 10;
++ assert_eq!(minus_1(&i), 9);
++ assert_eq!(i, 10);
++ assert_eq!(dec_read_dec(&mut i), 9);
++ assert_eq!(i, 8);
++}
--- /dev/null
--- /dev/null
++const NAN_F32: f32 = f32::NAN;
++const NAN_F64: f64 = f64::NAN;
++
++#[warn(clippy::cmp_nan)]
++#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++ let x = 5f32;
++ x == f32::NAN;
++ x != f32::NAN;
++ x < f32::NAN;
++ x > f32::NAN;
++ x <= f32::NAN;
++ x >= f32::NAN;
++ x == NAN_F32;
++ x != NAN_F32;
++ x < NAN_F32;
++ x > NAN_F32;
++ x <= NAN_F32;
++ x >= NAN_F32;
++
++ let y = 0f64;
++ y == f64::NAN;
++ y != f64::NAN;
++ y < f64::NAN;
++ y > f64::NAN;
++ y <= f64::NAN;
++ y >= f64::NAN;
++ y == NAN_F64;
++ y != NAN_F64;
++ y < NAN_F64;
++ y > NAN_F64;
++ y <= NAN_F64;
++ y >= NAN_F64;
++}
--- /dev/null
--- /dev/null
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:8:5
++ |
++LL | x == f32::NAN;
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cmp-nan` implied by `-D warnings`
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:9:5
++ |
++LL | x != f32::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:10:5
++ |
++LL | x < f32::NAN;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:11:5
++ |
++LL | x > f32::NAN;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:12:5
++ |
++LL | x <= f32::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:13:5
++ |
++LL | x >= f32::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:14:5
++ |
++LL | x == NAN_F32;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:15:5
++ |
++LL | x != NAN_F32;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:16:5
++ |
++LL | x < NAN_F32;
++ | ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:17:5
++ |
++LL | x > NAN_F32;
++ | ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:18:5
++ |
++LL | x <= NAN_F32;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:19:5
++ |
++LL | x >= NAN_F32;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:22:5
++ |
++LL | y == f64::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:23:5
++ |
++LL | y != f64::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:24:5
++ |
++LL | y < f64::NAN;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:25:5
++ |
++LL | y > f64::NAN;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:26:5
++ |
++LL | y <= f64::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:27:5
++ |
++LL | y >= f64::NAN;
++ | ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:28:5
++ |
++LL | y == NAN_F64;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:29:5
++ |
++LL | y != NAN_F64;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:30:5
++ |
++LL | y < NAN_F64;
++ | ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:31:5
++ |
++LL | y > NAN_F64;
++ | ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:32:5
++ |
++LL | y <= NAN_F64;
++ | ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++ --> $DIR/cmp_nan.rs:33:5
++ |
++LL | y >= NAN_F64;
++ | ^^^^^^^^^^^^
++
++error: aborting due to 24 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::cmp_null)]
++#![allow(unused_mut)]
++
++use std::ptr;
++
++fn main() {
++ let x = 0;
++ let p: *const usize = &x;
++ if p == ptr::null() {
++ println!("This is surprising!");
++ }
++ let mut y = 0;
++ let mut m: *mut usize = &mut y;
++ if m == ptr::null_mut() {
++ println!("This is surprising, too!");
++ }
++}
--- /dev/null
--- /dev/null
++error: Comparing with null is better expressed by the `.is_null()` method
++ --> $DIR/cmp_null.rs:9:8
++ |
++LL | if p == ptr::null() {
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::cmp-null` implied by `-D warnings`
++
++error: Comparing with null is better expressed by the `.is_null()` method
++ --> $DIR/cmp_null.rs:14:8
++ |
++LL | if m == ptr::null_mut() {
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::cmp_owned)]
++#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
++fn main() {
++ fn with_to_string(x: &str) {
++ x != "foo";
++
++ "foo" != x;
++ }
++
++ let x = "oh";
++
++ with_to_string(x);
++
++ x != "foo";
++
++ x != "foo";
++
++ 42.to_string() == "42";
++
++ Foo == Foo;
++
++ "abc".chars().filter(|c| *c != 'X');
++
++ "abc".chars().filter(|c| *c != 'X');
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++ // Allow this here, because it emits the lint
++ // without a suggestion. This is tested in
++ // `tests/ui/cmp_owned/without_suggestion.rs`
++ #[allow(clippy::cmp_owned)]
++ fn eq(&self, other: &Self) -> bool {
++ self.to_owned() == *other
++ }
++}
++
++impl ToOwned for Foo {
++ type Owned = Bar;
++ fn to_owned(&self) -> Bar {
++ Bar
++ }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++ fn borrow(&self) -> &Foo {
++ static FOO: Foo = Foo;
++ &FOO
++ }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++ type Owned = Baz;
++ fn to_owned(&self) -> Baz {
++ Baz
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::cmp_owned)]
++#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
++fn main() {
++ fn with_to_string(x: &str) {
++ x != "foo".to_string();
++
++ "foo".to_string() != x;
++ }
++
++ let x = "oh";
++
++ with_to_string(x);
++
++ x != "foo".to_owned();
++
++ x != String::from("foo");
++
++ 42.to_string() == "42";
++
++ Foo.to_owned() == Foo;
++
++ "abc".chars().filter(|c| c.to_owned() != 'X');
++
++ "abc".chars().filter(|c| *c != 'X');
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++ // Allow this here, because it emits the lint
++ // without a suggestion. This is tested in
++ // `tests/ui/cmp_owned/without_suggestion.rs`
++ #[allow(clippy::cmp_owned)]
++ fn eq(&self, other: &Self) -> bool {
++ self.to_owned() == *other
++ }
++}
++
++impl ToOwned for Foo {
++ type Owned = Bar;
++ fn to_owned(&self) -> Bar {
++ Bar
++ }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++ fn borrow(&self) -> &Foo {
++ static FOO: Foo = Foo;
++ &FOO
++ }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++ type Owned = Baz;
++ fn to_owned(&self) -> Baz {
++ Baz
++ }
++}
--- /dev/null
--- /dev/null
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:7:14
++ |
++LL | x != "foo".to_string();
++ | ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++ |
++ = note: `-D clippy::cmp-owned` implied by `-D warnings`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:9:9
++ |
++LL | "foo".to_string() != x;
++ | ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:16:10
++ |
++LL | x != "foo".to_owned();
++ | ^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:18:10
++ |
++LL | x != String::from("foo");
++ | ^^^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:22:5
++ |
++LL | Foo.to_owned() == Foo;
++ | ^^^^^^^^^^^^^^ help: try: `Foo`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/with_suggestion.rs:24:30
++ |
++LL | "abc".chars().filter(|c| c.to_owned() != 'X');
++ | ^^^^^^^^^^^^ help: try: `*c`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#[allow(clippy::unnecessary_operation)]
++
++fn main() {
++ let x = &Baz;
++ let y = &Baz;
++ y.to_owned() == *x;
++
++ let x = &&Baz;
++ let y = &Baz;
++ y.to_owned() == **x;
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++ fn eq(&self, other: &Self) -> bool {
++ self.to_owned() == *other
++ }
++}
++
++impl ToOwned for Foo {
++ type Owned = Bar;
++ fn to_owned(&self) -> Bar {
++ Bar
++ }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++ type Owned = Baz;
++ fn to_owned(&self) -> Baz {
++ Baz
++ }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++ fn borrow(&self) -> &Foo {
++ static FOO: Foo = Foo;
++ &FOO
++ }
++}
--- /dev/null
--- /dev/null
++error: this creates an owned instance just for comparison
++ --> $DIR/without_suggestion.rs:6:5
++ |
++LL | y.to_owned() == *x;
++ | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++ |
++ = note: `-D clippy::cmp-owned` implied by `-D warnings`
++
++error: this creates an owned instance just for comparison
++ --> $DIR/without_suggestion.rs:10:5
++ |
++LL | y.to_owned() == **x;
++ | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++
++error: this creates an owned instance just for comparison
++ --> $DIR/without_suggestion.rs:17:9
++ |
++LL | self.to_owned() == *other
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(clippy::all)]
++#![warn(clippy::cognitive_complexity)]
++#![allow(unused)]
++
++#[rustfmt::skip]
++fn main() {
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++ if true {
++ println!("a");
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn kaboom() {
++ let n = 0;
++ 'a: for i in 0..20 {
++ 'b: for j in i..20 {
++ for k in j..20 {
++ if k == 5 {
++ break 'b;
++ }
++ if j == 3 && k == 6 {
++ continue 'a;
++ }
++ if k == j {
++ continue;
++ }
++ println!("bake");
++ }
++ }
++ println!("cake");
++ }
++}
++
++fn bloo() {
++ match 42 {
++ 0 => println!("hi"),
++ 1 => println!("hai"),
++ 2 => println!("hey"),
++ 3 => println!("hallo"),
++ 4 => println!("hello"),
++ 5 => println!("salut"),
++ 6 => println!("good morning"),
++ 7 => println!("good evening"),
++ 8 => println!("good afternoon"),
++ 9 => println!("good night"),
++ 10 => println!("bonjour"),
++ 11 => println!("hej"),
++ 12 => println!("hej hej"),
++ 13 => println!("greetings earthling"),
++ 14 => println!("take us to you leader"),
++ 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"),
++ 35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"),
++ 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"),
++ _ => println!("bye"),
++ }
++}
++
++// Short circuiting operations don't increase the complexity of a function.
++// Note that the minimum complexity of a function is 1.
++#[clippy::cognitive_complexity = "1"]
++fn lots_of_short_circuits() -> bool {
++ true && false && true && false && true && false && true
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn lots_of_short_circuits2() -> bool {
++ true || false || true || false || true || false || true
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn baa() {
++ let x = || match 99 {
++ 0 => 0,
++ 1 => 1,
++ 2 => 2,
++ 4 => 4,
++ 6 => 6,
++ 9 => 9,
++ _ => 42,
++ };
++ if x() == 42 {
++ println!("x");
++ } else {
++ println!("not x");
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn bar() {
++ match 99 {
++ 0 => println!("hi"),
++ _ => println!("bye"),
++ }
++}
++
++#[test]
++#[clippy::cognitive_complexity = "1"]
++/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to
++/// give lots of false-positives in tests.
++fn dont_warn_on_tests() {
++ match 99 {
++ 0 => println!("hi"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barr() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barr2() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrr() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => panic!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrr2() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => panic!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++ match 99 {
++ 0 => println!("hi"),
++ 1 => panic!("bla"),
++ 2 | 3 => println!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrrr() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => panic!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrrr2() {
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => panic!("blub"),
++ _ => println!("bye"),
++ }
++ match 99 {
++ 0 => println!("hi"),
++ 1 => println!("bla"),
++ 2 | 3 => panic!("blub"),
++ _ => println!("bye"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn cake() {
++ if 4 == 5 {
++ println!("yea");
++ } else {
++ panic!("meh");
++ }
++ println!("whee");
++}
++
++#[clippy::cognitive_complexity = "1"]
++pub fn read_file(input_path: &str) -> String {
++ use std::fs::File;
++ use std::io::{Read, Write};
++ use std::path::Path;
++ let mut file = match File::open(&Path::new(input_path)) {
++ Ok(f) => f,
++ Err(err) => {
++ panic!("Can't open {}: {}", input_path, err);
++ },
++ };
++
++ let mut bytes = Vec::new();
++
++ match file.read_to_end(&mut bytes) {
++ Ok(..) => {},
++ Err(_) => {
++ panic!("Can't read {}", input_path);
++ },
++ };
++
++ match String::from_utf8(bytes) {
++ Ok(contents) => contents,
++ Err(_) => {
++ panic!("{} is not UTF-8 encoded", input_path);
++ },
++ }
++}
++
++enum Void {}
++
++#[clippy::cognitive_complexity = "1"]
++fn void(void: Void) {
++ if true {
++ match void {}
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn mcarton_sees_all() {
++ panic!("meh");
++ panic!("möh");
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn try_() -> Result<i32, &'static str> {
++ match 5 {
++ 5 => Ok(5),
++ _ => return Err("bla"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn try_again() -> Result<i32, &'static str> {
++ let _ = Ok(42)?;
++ let _ = Ok(43)?;
++ let _ = Ok(44)?;
++ let _ = Ok(45)?;
++ let _ = Ok(46)?;
++ let _ = Ok(47)?;
++ let _ = Ok(48)?;
++ let _ = Ok(49)?;
++ match 5 {
++ 5 => Ok(5),
++ _ => return Err("bla"),
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn early() -> Result<i32, &'static str> {
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++ return Ok(5);
++}
++
++#[rustfmt::skip]
++#[clippy::cognitive_complexity = "1"]
++fn early_ret() -> i32 {
++ let a = if true { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ let a = if a < 99 { 42 } else { return 0; };
++ match 5 {
++ 5 => 5,
++ _ => return 6,
++ }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn closures() {
++ let x = |a: i32, b: i32| -> i32 {
++ if true {
++ println!("moo");
++ }
++
++ a + b
++ };
++}
++
++struct Moo;
++
++#[clippy::cognitive_complexity = "1"]
++impl Moo {
++ fn moo(&self) {
++ if true {
++ println!("moo");
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: the function has a cognitive complexity of (28/25)
++ --> $DIR/cognitive_complexity.rs:6:4
++ |
++LL | fn main() {
++ | ^^^^
++ |
++ = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (7/1)
++ --> $DIR/cognitive_complexity.rs:91:4
++ |
++LL | fn kaboom() {
++ | ^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:149:4
++ |
++LL | fn baa() {
++ | ^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:150:13
++ |
++LL | let x = || match 99 {
++ | ^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:167:4
++ |
++LL | fn bar() {
++ | ^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:186:4
++ |
++LL | fn barr() {
++ | ^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++ --> $DIR/cognitive_complexity.rs:196:4
++ |
++LL | fn barr2() {
++ | ^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:212:4
++ |
++LL | fn barrr() {
++ | ^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++ --> $DIR/cognitive_complexity.rs:222:4
++ |
++LL | fn barrr2() {
++ | ^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:238:4
++ |
++LL | fn barrrr() {
++ | ^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++ --> $DIR/cognitive_complexity.rs:248:4
++ |
++LL | fn barrrr2() {
++ | ^^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:264:4
++ |
++LL | fn cake() {
++ | ^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (4/1)
++ --> $DIR/cognitive_complexity.rs:274:8
++ |
++LL | pub fn read_file(input_path: &str) -> String {
++ | ^^^^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:305:4
++ |
++LL | fn void(void: Void) {
++ | ^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (8/1)
++ --> $DIR/cognitive_complexity.rs:356:4
++ |
++LL | fn early_ret() -> i32 {
++ | ^^^^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:377:13
++ |
++LL | let x = |a: i32, b: i32| -> i32 {
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++ --> $DIR/cognitive_complexity.rs:390:8
++ |
++LL | fn moo(&self) {
++ | ^^^
++ |
++ = help: you could split it up into multiple smaller functions
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::cognitive_complexity)]
++#![warn(unused)]
++
++fn main() {
++ kaboom();
++}
++
++#[clippy::cognitive_complexity = "0"]
++fn kaboom() {
++ if 42 == 43 {
++ panic!();
++ } else if "cake" == "lie" {
++ println!("what?");
++ }
++}
--- /dev/null
--- /dev/null
++error: the function has a cognitive complexity of (3/0)
++ --> $DIR/cognitive_complexity_attr_used.rs:9:4
++ |
++LL | fn kaboom() {
++ | ^^^^^^
++ |
++ = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
++ = help: you could split it up into multiple smaller functions
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++ let x = "hello";
++ let y = "world";
++ // Collapse `else { if .. }` to `else if ..`
++ if x == "hello" {
++ print!("Hello ");
++ } else if y == "world" {
++ println!("world!")
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else if let Some(42) = Some(42) {
++ println!("world!")
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else if y == "world" {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else if x == "hello" {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++ let x = "hello";
++ let y = "world";
++ // Collapse `else { if .. }` to `else if ..`
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ if y == "world" {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ if let Some(42) = Some(42) {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ if y == "world" {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else {
++ if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else {
++ if x == "hello" {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++ }
++
++ if let Some(42) = Some(42) {
++ print!("Hello ");
++ } else {
++ if let Some(42) = Some(42) {
++ println!("world")
++ }
++ else {
++ println!("!")
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:12:12
++ |
++LL | } else {
++ | ____________^
++LL | | if y == "world" {
++LL | | println!("world!")
++LL | | }
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::collapsible-if` implied by `-D warnings`
++help: try
++ |
++LL | } else if y == "world" {
++LL | println!("world!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:20:12
++ |
++LL | } else {
++ | ____________^
++LL | | if let Some(42) = Some(42) {
++LL | | println!("world!")
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if let Some(42) = Some(42) {
++LL | println!("world!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:28:12
++ |
++LL | } else {
++ | ____________^
++LL | | if y == "world" {
++LL | | println!("world")
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if y == "world" {
++LL | println!("world")
++LL | }
++LL | else {
++LL | println!("!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:39:12
++ |
++LL | } else {
++ | ____________^
++LL | | if let Some(42) = Some(42) {
++LL | | println!("world")
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if let Some(42) = Some(42) {
++LL | println!("world")
++LL | }
++LL | else {
++LL | println!("!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:50:12
++ |
++LL | } else {
++ | ____________^
++LL | | if let Some(42) = Some(42) {
++LL | | println!("world")
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if let Some(42) = Some(42) {
++LL | println!("world")
++LL | }
++LL | else {
++LL | println!("!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:61:12
++ |
++LL | } else {
++ | ____________^
++LL | | if x == "hello" {
++LL | | println!("world")
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if x == "hello" {
++LL | println!("world")
++LL | }
++LL | else {
++LL | println!("!")
++LL | }
++ |
++
++error: this `else { if .. }` block can be collapsed
++ --> $DIR/collapsible_else_if.rs:72:12
++ |
++LL | } else {
++ | ____________^
++LL | | if let Some(42) = Some(42) {
++LL | | println!("world")
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | } else if let Some(42) = Some(42) {
++LL | println!("world")
++LL | }
++LL | else {
++LL | println!("!")
++LL | }
++ |
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++ let x = "hello";
++ let y = "world";
++ if x == "hello" && y == "world" {
++ println!("Hello world!");
++ }
++
++ if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
++ println!("Hello world!");
++ }
++
++ if x == "hello" && x == "world" && (y == "world" || y == "hello") {
++ println!("Hello world!");
++ }
++
++ if (x == "hello" || x == "world") && y == "world" && y == "hello" {
++ println!("Hello world!");
++ }
++
++ if x == "hello" && x == "world" && y == "world" && y == "hello" {
++ println!("Hello world!");
++ }
++
++ if 42 == 1337 && 'a' != 'A' {
++ println!("world!")
++ }
++
++ // Works because any if with an else statement cannot be collapsed.
++ if x == "hello" {
++ if y == "world" {
++ println!("Hello world!");
++ }
++ } else {
++ println!("Not Hello world");
++ }
++
++ if x == "hello" {
++ if y == "world" {
++ println!("Hello world!");
++ } else {
++ println!("Hello something else");
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ if y == "world" {
++ println!("world!")
++ }
++ }
++
++ if true {
++ } else {
++ assert!(true); // assert! is just an `if`
++ }
++
++
++ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
++ if x == "hello" {// Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" { // Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" {
++ // Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" && y == "world" { // Collapsible
++ println!("Hello world!");
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ // Not collapsible
++ if y == "world" {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ // Not collapsible
++ if let Some(42) = Some(42) {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ /* Not collapsible */
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" { /* Not collapsible */
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ // Test behavior wrt. `let_chains`.
++ // None of the cases below should be collapsed.
++ fn truth() -> bool { true }
++
++ // Prefix:
++ if let 0 = 1 {
++ if truth() {}
++ }
++
++ // Suffix:
++ if truth() {
++ if let 0 = 1 {}
++ }
++
++ // Midfix:
++ if truth() {
++ if let 0 = 1 {
++ if truth() {}
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++ let x = "hello";
++ let y = "world";
++ if x == "hello" {
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" || x == "world" {
++ if y == "world" || y == "hello" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" && x == "world" {
++ if y == "world" || y == "hello" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" || x == "world" {
++ if y == "world" && y == "hello" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" && x == "world" {
++ if y == "world" && y == "hello" {
++ println!("Hello world!");
++ }
++ }
++
++ if 42 == 1337 {
++ if 'a' != 'A' {
++ println!("world!")
++ }
++ }
++
++ // Works because any if with an else statement cannot be collapsed.
++ if x == "hello" {
++ if y == "world" {
++ println!("Hello world!");
++ }
++ } else {
++ println!("Not Hello world");
++ }
++
++ if x == "hello" {
++ if y == "world" {
++ println!("Hello world!");
++ } else {
++ println!("Hello something else");
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ if y == "world" {
++ println!("world!")
++ }
++ }
++
++ if true {
++ } else {
++ assert!(true); // assert! is just an `if`
++ }
++
++
++ // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
++ if x == "hello" {// Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" { // Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" {
++ // Not collapsible
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" {
++ if y == "world" { // Collapsible
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ // Not collapsible
++ if y == "world" {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ print!("Hello ");
++ } else {
++ // Not collapsible
++ if let Some(42) = Some(42) {
++ println!("world!")
++ }
++ }
++
++ if x == "hello" {
++ /* Not collapsible */
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ if x == "hello" { /* Not collapsible */
++ if y == "world" {
++ println!("Hello world!");
++ }
++ }
++
++ // Test behavior wrt. `let_chains`.
++ // None of the cases below should be collapsed.
++ fn truth() -> bool { true }
++
++ // Prefix:
++ if let 0 = 1 {
++ if truth() {}
++ }
++
++ // Suffix:
++ if truth() {
++ if let 0 = 1 {}
++ }
++
++ // Midfix:
++ if truth() {
++ if let 0 = 1 {
++ if truth() {}
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:9:5
++ |
++LL | / if x == "hello" {
++LL | | if y == "world" {
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::collapsible-if` implied by `-D warnings`
++help: try
++ |
++LL | if x == "hello" && y == "world" {
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:15:5
++ |
++LL | / if x == "hello" || x == "world" {
++LL | | if y == "world" || y == "hello" {
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:21:5
++ |
++LL | / if x == "hello" && x == "world" {
++LL | | if y == "world" || y == "hello" {
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") {
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:27:5
++ |
++LL | / if x == "hello" || x == "world" {
++LL | | if y == "world" && y == "hello" {
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" {
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:33:5
++ |
++LL | / if x == "hello" && x == "world" {
++LL | | if y == "world" && y == "hello" {
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if x == "hello" && x == "world" && y == "world" && y == "hello" {
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:39:5
++ |
++LL | / if 42 == 1337 {
++LL | | if 'a' != 'A' {
++LL | | println!("world!")
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if 42 == 1337 && 'a' != 'A' {
++LL | println!("world!")
++LL | }
++ |
++
++error: this `if` statement can be collapsed
++ --> $DIR/collapsible_if.rs:95:5
++ |
++LL | / if x == "hello" {
++LL | | if y == "world" { // Collapsible
++LL | | println!("Hello world!");
++LL | | }
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if x == "hello" && y == "world" { // Collapsible
++LL | println!("Hello world!");
++LL | }
++ |
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_ast;
++extern crate rustc_errors;
++extern crate rustc_lint;
++extern crate rustc_session;
++extern crate rustc_span;
++
++use rustc_ast::ast::Expr;
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++#[allow(unused_variables)]
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++ F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_help<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ option_span: Option<Span>,
++ help: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_note<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ note_span: Option<Span>,
++ note: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_sugg<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ sp: Span,
++ msg: &str,
++ help: &str,
++ sugg: String,
++ applicability: Applicability,
++) {
++}
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl EarlyLintPass for Pass {
++ fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
++ let lint_msg = "lint message";
++ let help_msg = "help message";
++ let note_msg = "note message";
++ let sugg = "new_call()";
++ let predicate = true;
++
++ span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++ span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
++ span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
++ span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
++ span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
++
++ // This expr shouldn't trigger this lint.
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.note(note_msg);
++ if predicate {
++ db.note(note_msg);
++ }
++ })
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_ast;
++extern crate rustc_errors;
++extern crate rustc_lint;
++extern crate rustc_session;
++extern crate rustc_span;
++
++use rustc_ast::ast::Expr;
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++#[allow(unused_variables)]
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++ F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_help<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ option_span: Option<Span>,
++ help: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_note<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ span: Span,
++ msg: &str,
++ note_span: Option<Span>,
++ note: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_sugg<'a, T: LintContext>(
++ cx: &'a T,
++ lint: &'static Lint,
++ sp: Span,
++ msg: &str,
++ help: &str,
++ sugg: String,
++ applicability: Applicability,
++) {
++}
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl EarlyLintPass for Pass {
++ fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
++ let lint_msg = "lint message";
++ let help_msg = "help message";
++ let note_msg = "note message";
++ let sugg = "new_call()";
++ let predicate = true;
++
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++ });
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.span_help(expr.span, help_msg);
++ });
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.help(help_msg);
++ });
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.span_note(expr.span, note_msg);
++ });
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.note(note_msg);
++ });
++
++ // This expr shouldn't trigger this lint.
++ span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++ db.note(note_msg);
++ if predicate {
++ db.note(note_msg);
++ }
++ })
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this call is collapsible
++ --> $DIR/collapsible_span_lint_calls.rs:75:9
++ |
++LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++LL | | });
++ | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)`
++ |
++note: the lint level is defined here
++ --> $DIR/collapsible_span_lint_calls.rs:2:9
++ |
++LL | #![deny(clippy::internal)]
++ | ^^^^^^^^^^^^^^^^
++ = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
++
++error: this call is collapsible
++ --> $DIR/collapsible_span_lint_calls.rs:78:9
++ |
++LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | | db.span_help(expr.span, help_msg);
++LL | | });
++ | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
++
++error: this call is collapsible
++ --> $DIR/collapsible_span_lint_calls.rs:81:9
++ |
++LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | | db.help(help_msg);
++LL | | });
++ | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
++
++error: this call is collspible
++ --> $DIR/collapsible_span_lint_calls.rs:84:9
++ |
++LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | | db.span_note(expr.span, note_msg);
++LL | | });
++ | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
++
++error: this call is collspible
++ --> $DIR/collapsible_span_lint_calls.rs:87:9
++ |
++LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | | db.note(note_msg);
++LL | | });
++ | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![warn(clippy::comparison_chain)]
++
++fn a() {}
++fn b() {}
++fn c() {}
++
++fn f(x: u8, y: u8, z: u8) {
++ // Ignored: Only one branch
++ if x > y {
++ a()
++ }
++
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ }
++
++ // Ignored: Only one explicit conditional
++ if x > y {
++ a()
++ } else {
++ b()
++ }
++
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ } else {
++ c()
++ }
++
++ if x > y {
++ a()
++ } else if y > x {
++ b()
++ } else {
++ c()
++ }
++
++ if x > 1 {
++ a()
++ } else if x < 1 {
++ b()
++ } else if x == 1 {
++ c()
++ }
++
++ // Ignored: Binop args are not equivalent
++ if x > 1 {
++ a()
++ } else if y > 1 {
++ b()
++ } else {
++ c()
++ }
++
++ // Ignored: Binop args are not equivalent
++ if x > y {
++ a()
++ } else if x > z {
++ b()
++ } else if y > z {
++ c()
++ }
++
++ // Ignored: Not binary comparisons
++ if true {
++ a()
++ } else if false {
++ b()
++ } else {
++ c()
++ }
++}
++
++#[allow(clippy::float_cmp)]
++fn g(x: f64, y: f64, z: f64) {
++ // Ignored: f64 doesn't implement Ord
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ }
++
++ // Ignored: f64 doesn't implement Ord
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ } else {
++ c()
++ }
++
++ // Ignored: f64 doesn't implement Ord
++ if x > y {
++ a()
++ } else if y > x {
++ b()
++ } else {
++ c()
++ }
++
++ // Ignored: f64 doesn't implement Ord
++ if x > 1.0 {
++ a()
++ } else if x < 1.0 {
++ b()
++ } else if x == 1.0 {
++ c()
++ }
++}
++
++fn h<T: Ord>(x: T, y: T, z: T) {
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ }
++
++ if x > y {
++ a()
++ } else if x < y {
++ b()
++ } else {
++ c()
++ }
++
++ if x > y {
++ a()
++ } else if y > x {
++ b()
++ } else {
++ c()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:14:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if x < y {
++LL | | b()
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::comparison-chain` implied by `-D warnings`
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:27:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if x < y {
++LL | | b()
++LL | | } else {
++LL | | c()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:35:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if y > x {
++LL | | b()
++LL | | } else {
++LL | | c()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:43:5
++ |
++LL | / if x > 1 {
++LL | | a()
++LL | | } else if x < 1 {
++LL | | b()
++LL | | } else if x == 1 {
++LL | | c()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:117:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if x < y {
++LL | | b()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:123:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if x < y {
++LL | | b()
++LL | | } else {
++LL | | c()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++ --> $DIR/comparison_chain.rs:131:5
++ |
++LL | / if x > y {
++LL | | a()
++LL | | } else if y > x {
++LL | | b()
++LL | | } else {
++LL | | c()
++LL | | }
++ | |_____^
++ |
++ = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)]
++#![feature(associated_type_defaults)]
++
++type Alias = Vec<Vec<Box<(u32, u32, u32, u32)>>>; // no warning here
++
++const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++
++struct S {
++ f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
++}
++
++struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++
++enum E {
++ Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
++ Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
++}
++
++impl S {
++ const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++ fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++}
++
++trait T {
++ const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++ type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++ fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++ fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++}
++
++fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
++ vec![]
++}
++
++fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++
++fn test3() {
++ let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
++}
++
++#[repr(C)]
++struct D {
++ // should not warn, since we don't have control over the signature (#3222)
++ test4: extern "C" fn(
++ itself: &D,
++ a: usize,
++ b: usize,
++ c: usize,
++ d: usize,
++ e: usize,
++ f: usize,
++ g: usize,
++ h: usize,
++ i: usize,
++ ),
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:7:12
++ |
++LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::type-complexity` implied by `-D warnings`
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:8:12
++ |
++LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:11:8
++ |
++LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:14:11
++ |
++LL | struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:17:11
++ |
++LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:18:17
++ |
++LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:22:14
++ |
++LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:23:30
++ |
++LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:27:14
++ |
++LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:28:14
++ |
++LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:29:25
++ |
++LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:30:29
++ |
++LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:33:15
++ |
++LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:37:14
++ |
++LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++ --> $DIR/complex_types.rs:40:13
++ |
++LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 15 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::copy_iterator)]
++
++#[derive(Copy, Clone)]
++struct Countdown(u8);
++
++impl Iterator for Countdown {
++ type Item = u8;
++
++ fn next(&mut self) -> Option<u8> {
++ self.0.checked_sub(1).map(|c| {
++ self.0 = c;
++ c
++ })
++ }
++}
++
++fn main() {
++ let my_iterator = Countdown(5);
++ let a: Vec<_> = my_iterator.take(1).collect();
++ assert_eq!(a.len(), 1);
++ let b: Vec<_> = my_iterator.collect();
++ assert_eq!(b.len(), 5);
++}
--- /dev/null
--- /dev/null
++error: you are implementing `Iterator` on a `Copy` type
++ --> $DIR/copy_iterator.rs:6:1
++ |
++LL | / impl Iterator for Countdown {
++LL | | type Item = u8;
++LL | |
++LL | | fn next(&mut self) -> Option<u8> {
++... |
++LL | | }
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::copy-iterator` implied by `-D warnings`
++ = note: consider implementing `IntoIterator` instead
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
++
++pub trait Trait {
++ const CONSTANT: u8;
++}
++
++impl Trait for u8 {
++ const CONSTANT: u8 = 2;
++}
++
++fn main() {
++ println!("{}", u8::CONSTANT * 10);
++}
--- /dev/null
--- /dev/null
++pub trait Trait {
++ fn fun(par: &str) -> &str;
++}
++
++impl Trait for str {
++ fn fun(par: &str) -> &str {
++ &par[0..1]
++ }
++}
--- /dev/null
--- /dev/null
++// no-prefer-dynamic
++// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
++// crates. If we don't set this, compiletest will override the `crate_type` attribute below and
++// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't
++// contain a proc-macro.
++
++#![feature(repr128)]
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++
++use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
++use std::iter::FromIterator;
++
++#[proc_macro]
++pub fn macro_test(input_stream: TokenStream) -> TokenStream {
++ let first_token = input_stream.into_iter().next().unwrap();
++ let span = first_token.span();
++
++ TokenStream::from_iter(vec![
++ TokenTree::Ident(Ident::new("fn", Span::call_site())),
++ TokenTree::Ident(Ident::new("code", Span::call_site())),
++ TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
++ TokenTree::Group(Group::new(Delimiter::Brace, {
++ let mut clause = Group::new(Delimiter::Brace, TokenStream::new());
++ clause.set_span(span);
++
++ TokenStream::from_iter(vec![
++ TokenTree::Ident(Ident::new("if", Span::call_site())),
++ TokenTree::Ident(Ident::new("true", Span::call_site())),
++ TokenTree::Group(clause.clone()),
++ TokenTree::Ident(Ident::new("else", Span::call_site())),
++ TokenTree::Group(clause),
++ ])
++ })),
++ ])
++}
--- /dev/null
--- /dev/null
++macro_rules! use_self {
++ (
++ impl $ty:ident {
++ fn func(&$this:ident) {
++ [fields($($field:ident)*)]
++ }
++ }
++ ) => (
++ impl $ty {
++ fn func(&$this) {
++ let $ty { $($field),* } = $this;
++ }
++ }
++ )
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#[allow(dead_code)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/478
++
++enum Baz {
++ One,
++ Two,
++}
++
++struct Test {
++ t: Option<usize>,
++ b: Baz,
++}
++
++fn main() {}
++
++pub fn foo() {
++ use Baz::*;
++ let x = Test { t: Some(0), b: One };
++
++ match x {
++ Test { t: Some(_), b: One } => unreachable!(),
++ Test { t: Some(42), b: Two } => unreachable!(),
++ Test { t: None, .. } => unreachable!(),
++ Test { .. } => unreachable!(),
++ }
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::all)]
++#![allow(unused_imports)]
++
++use std::*;
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
++
++fn main() {
++ match 1 {
++ 1 => {},
++ 2 => {
++ [0; 1];
++ },
++ _ => {},
++ }
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(dead_code, unused_variables)]
++
++/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/1782
++use std::{mem, ptr};
++
++fn spanless_eq_ice() {
++ let txt = "something";
++ match txt {
++ "something" => unsafe {
++ ptr::write(
++ ptr::null_mut() as *mut u32,
++ mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
++ )
++ },
++ _ => unsafe {
++ ptr::write(
++ ptr::null_mut() as *mut u32,
++ mem::transmute::<[u8; 4], _>([13, 246, 24, 255]),
++ )
++ },
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
++
++fn main() {}
++
++pub trait Convert {
++ type Action: From<*const f64>;
++
++ fn convert(val: *const f64) -> Self::Action {
++ val.into()
++ }
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
++
++/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
++
++fn f(s: &[u8]) -> bool {
++ let t = s[0] as char;
++
++ match t {
++ 'E' | 'W' => {},
++ 'T' => {
++ if s[0..4] != ['0' as u8; 4] {
++ return false;
++ } else {
++ return true;
++ }
++ },
++ _ => {
++ return false;
++ },
++ }
++ true
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(dead_code, unused_variables)]
++
++/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
++
++fn spanless_hash_ice() {
++ let txt = "something";
++ let empty_header: [u8; 1] = [1; 1];
++
++ match txt {
++ "something" => {
++ let mut headers = [empty_header; 1];
++ },
++ "" => (),
++ _ => (),
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++
++enum Foo {
++ A,
++ B,
++ C,
++}
++
++macro_rules! test_hash {
++ ($foo:expr, $($t:ident => $ord:expr),+ ) => {
++ use self::Foo::*;
++ match $foo {
++ $ ( & $t => $ord,
++ )*
++ };
++ };
++}
++
++fn main() {
++ let a = Foo::A;
++ test_hash!(&a, A => 0, B => 1, C => 2);
++}
--- /dev/null
--- /dev/null
++error: you don't need to add `&` to both the expression and the patterns
++ --> $DIR/ice-2636.rs:12:9
++ |
++LL | / match $foo {
++LL | | $ ( & $t => $ord,
++LL | | )*
++LL | | };
++ | |_________^
++...
++LL | test_hash!(&a, A => 0, B => 1, C => 2);
++ | --------------------------------------- in this macro invocation
++ |
++ = note: `-D clippy::match-ref-pats` implied by `-D warnings`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
++
++pub fn f(new: fn()) {
++ new();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(
++ unused_variables,
++ clippy::blacklisted_name,
++ clippy::needless_pass_by_value,
++ dead_code
++)]
++
++/// This should not compile-fail with:
++///
++/// error[E0277]: the trait bound `T: Foo` is not satisfied
++// See rust-lang/rust-clippy#2760.
++
++trait Foo {
++ type Bar;
++}
++
++struct Baz<T: Foo> {
++ bar: T::Bar,
++}
++
++fn take<T: Foo>(baz: Baz<T>) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++use std::collections::HashSet;
++
++// See rust-lang/rust-clippy#2774.
++
++#[derive(Eq, PartialEq, Debug, Hash)]
++pub struct Bar {
++ foo: Foo,
++}
++
++#[derive(Eq, PartialEq, Debug, Hash)]
++pub struct Foo {}
++
++#[allow(clippy::implicit_hasher)]
++// This should not cause a "cannot relate bound region" ICE.
++pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
++ let mut foos = HashSet::new();
++ foos.extend(bars.iter().map(|b| &b.foo));
++}
++
++#[allow(clippy::implicit_hasher)]
++// Also, this should not cause a "cannot relate bound region" ICE.
++pub fn add_barfoos_to_foos2(bars: &HashSet<&Bar>) {
++ let mut foos = HashSet::new();
++ foos.extend(bars.iter().map(|b| &b.foo));
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
++
++pub trait FooMap {
++ fn map<B, F: Fn() -> B>(&self, f: F) -> B;
++}
++
++impl FooMap for bool {
++ fn map<B, F: Fn() -> B>(&self, f: F) -> B {
++ f()
++ }
++}
++
++fn main() {
++ let a = true;
++ a.map(|| false);
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#[allow(dead_code)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
++
++struct Ice {
++ size: String,
++}
++
++impl<'a> From<String> for Ice {
++ fn from(_: String) -> Self {
++ let text = || "iceberg".to_string();
++ Self { size: text() }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
++
++#[derive(Clone)]
++pub struct HashMap<V, S> {
++ hash_builder: S,
++ table: RawTable<V>,
++}
++
++#[derive(Clone)]
++pub struct RawTable<V> {
++ size: usize,
++ val: V,
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![warn(clippy::all)]
++#![allow(clippy::blacklisted_name)]
++#![allow(unused)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
++
++enum Foo {
++ Bar,
++ Baz,
++}
++
++fn bar(foo: Foo) {
++ macro_rules! baz {
++ () => {
++ if let Foo::Bar = foo {}
++ };
++ }
++
++ baz!();
++ baz!();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++fn main() {}
++
++fn no_panic<T>(slice: &[T]) {
++ let mut iter = slice.iter();
++ loop {
++ let _ = match iter.next() {
++ Some(ele) => ele,
++ None => break,
++ };
++ loop {}
++ }
++}
--- /dev/null
--- /dev/null
++error: this loop could be written as a `while let` loop
++ --> $DIR/ice-360.rs:5:5
++ |
++LL | / loop {
++LL | | let _ = match iter.next() {
++LL | | Some(ele) => ele,
++LL | | None => break,
++LL | | };
++LL | | loop {}
++LL | | }
++ | |_____^ help: try: `while let Some(ele) = iter.next() { .. }`
++ |
++ = note: `-D clippy::while-let-loop` implied by `-D warnings`
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++ --> $DIR/ice-360.rs:10:9
++ |
++LL | loop {}
++ | ^^^^^^^
++ |
++ = note: `-D clippy::empty-loop` implied by `-D warnings`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::implicit_hasher)]
++
++use std::collections::HashSet;
++
++fn main() {}
++
++pub fn ice_3717(_: &HashSet<usize>) {
++ let _ = [0u8; 0];
++ let _: HashSet<usize> = HashSet::new();
++}
--- /dev/null
--- /dev/null
++error: parameter of type `HashSet` should be generalized over different hashers
++ --> $DIR/ice-3717.rs:7:21
++ |
++LL | pub fn ice_3717(_: &HashSet<usize>) {
++ | ^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/ice-3717.rs:1:9
++ |
++LL | #![deny(clippy::implicit_hasher)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++help: consider adding a type parameter
++ |
++LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | let _: HashSet<usize> = HashSet::default();
++ | ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// aux-build:proc_macro_crash.rs
++// run-pass
++
++#![feature(proc_macro_hygiene)]
++#![warn(clippy::suspicious_else_formatting)]
++
++extern crate proc_macro_crash;
++use proc_macro_crash::macro_test;
++
++fn main() {
++ macro_test!(2);
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
++
++macro_rules! a {
++ ( $pub:tt $($attr:tt)* ) => {
++ $($attr)* $pub fn say_hello() {}
++ };
++}
++
++macro_rules! b {
++ () => {
++ a! { pub }
++ };
++}
++
++b! {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++fn main() {
++ 1x;
++}
--- /dev/null
--- /dev/null
++error: invalid suffix `x` for integer literal
++ --> $DIR/ice-3891.rs:2:5
++ |
++LL | 1x;
++ | ^^ invalid suffix `x`
++ |
++ = help: the suffix must be one of the integral types (`u32`, `isize`, etc)
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++use std::mem;
++
++pub struct Foo<A, B>(A, B);
++
++impl<A, B> Foo<A, B> {
++ const HOST_SIZE: usize = mem::size_of::<B>();
++
++ pub fn crash() -> bool {
++ Self::HOST_SIZE == 0
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++fn repro() {
++ trait Foo {
++ type Bar;
++ }
++
++ #[allow(dead_code)]
++ struct Baz<T: Foo> {
++ field: T::Bar,
++ }
++}
++
++fn main() {
++ repro();
++}
--- /dev/null
--- /dev/null
++#![allow(clippy::single_match)]
++
++use std::ptr;
++
++fn main() {
++ match Some(0_usize) {
++ Some(_) => {
++ let s = "012345";
++ unsafe { ptr::read(s.as_ptr().offset(1) as *const [u8; 5]) };
++ },
++ _ => (),
++ };
++}
--- /dev/null
--- /dev/null
++#![warn(clippy::use_self)]
++
++#[macro_use]
++#[path = "auxiliary/use_self_macro.rs"]
++mod use_self_macro;
++
++struct Foo {
++ a: u32,
++}
++
++use_self! {
++ impl Foo {
++ fn func(&self) {
++ [fields(
++ a
++ )]
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![warn(clippy::use_self)]
++
++#[path = "auxiliary/ice-4727-aux.rs"]
++mod aux;
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++const COUNT: usize = 2;
++struct Thing;
++trait Dummy {}
++
++const _: () = {
++ impl Dummy for Thing where [i32; COUNT]: Sized {}
++};
++
++fn main() {}
--- /dev/null
--- /dev/null
++#![feature(const_generics)]
++#![allow(incomplete_features)]
++
++pub struct ArrayWrapper<const N: usize>([usize; N]);
++
++impl<const N: usize> ArrayWrapper<{ N }> {
++ pub fn ice(&self) {
++ for i in self.0.iter() {
++ println!("{}", i);
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// check-pass
++
++// Test for https://github.com/rust-lang/rust-clippy/issues/4968
++
++#![warn(clippy::unsound_collection_transmute)]
++
++trait Trait {
++ type Assoc;
++}
++
++use std::mem::{self, ManuallyDrop};
++
++#[allow(unused)]
++fn func<T: Trait>(slice: Vec<T::Assoc>) {
++ unsafe {
++ let _: Vec<ManuallyDrop<T::Assoc>> = mem::transmute(slice);
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// edition:2018
++
++// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207
++
++pub async fn bar<'a, T: 'a>(_: T) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// Regression test for #5233
++
++#![feature(const_generics)]
++#![allow(incomplete_features)]
++#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)]
++
++pub struct KotomineArray<T, const N: usize> {
++ arr: [T; N],
++}
++
++impl<T: std::clone::Clone, const N: usize> KotomineArray<T, N> {
++ pub fn ice(self) {
++ let _ = self.arr[..];
++ let _ = self.arr.iter().cloned().collect::<Vec<_>>();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562
++
++#![feature(generators, generator_trait)]
++
++fn main() {
++ let _ = || {
++ yield;
++ };
++}
--- /dev/null
--- /dev/null
++// reduced from rustc issue-69020-assoc-const-arith-overflow.rs
++pub fn main() {}
++
++pub trait Foo {
++ const OOB: i32;
++}
++
++impl<T: Foo> Foo for Vec<T> {
++ const OOB: i32 = [1][1] + T::OOB;
++ //~^ ERROR operation will panic
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/700
++
++fn core() {}
++
++fn main() {
++ core();
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
++
++#[allow(dead_code)]
++struct Foo;
++
++impl Iterator for Foo {
++ type Item = ();
++
++ fn next(&mut self) -> Option<()> {
++ let _ = self.len() == 0;
++ unimplemented!()
++ }
++}
++
++impl ExactSizeIterator for Foo {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(clippy::comparison_chain)]
++#![deny(clippy::if_same_then_else)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
++
++fn main() {}
++
++pub fn foo(a: i32, b: i32) -> Option<&'static str> {
++ if a == b {
++ None
++ } else if a > b {
++ Some("a pfeil b")
++ } else {
++ None
++ }
++}
--- /dev/null
--- /dev/null
++#![deny(clippy::multiple_inherent_impl)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
++
++macro_rules! impl_foo {
++ ($struct:ident) => {
++ impl $struct {
++ fn foo() {}
++ }
++ };
++}
++
++macro_rules! impl_bar {
++ ($struct:ident) => {
++ impl $struct {
++ fn bar() {}
++ }
++ };
++}
++
++struct MyStruct;
++
++impl_foo!(MyStruct);
++impl_bar!(MyStruct);
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(warnings)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/825
++
++// this should compile in a reasonable amount of time
++fn rust_type_id(name: &str) {
++ if "bool" == &name[..]
++ || "uint" == &name[..]
++ || "u8" == &name[..]
++ || "u16" == &name[..]
++ || "u32" == &name[..]
++ || "f32" == &name[..]
++ || "f64" == &name[..]
++ || "i8" == &name[..]
++ || "i16" == &name[..]
++ || "i32" == &name[..]
++ || "i64" == &name[..]
++ || "Self" == &name[..]
++ || "str" == &name[..]
++ {
++ unreachable!();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(dead_code)]
++
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596
++pub fn loop_on_block_condition(u: &mut isize) {
++ while { *u < 0 } {
++ *u += 1;
++ }
++}
++
++/// https://github.com/rust-lang/rust-clippy/issues/2584
++fn loop_with_unsafe_condition(ptr: *const u8) {
++ let mut len = 0;
++ while unsafe { *ptr.offset(len) } != 0 {
++ len += 1;
++ }
++}
++
++/// https://github.com/rust-lang/rust-clippy/issues/2710
++static mut RUNNING: bool = true;
++fn loop_on_static_condition() {
++ unsafe {
++ while RUNNING {
++ RUNNING = false;
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::match_same_arms)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
++
++const PRICE_OF_SWEETS: u32 = 5;
++const PRICE_OF_KINDNESS: u32 = 0;
++const PRICE_OF_DRINKS: u32 = 5;
++
++pub fn price(thing: &str) -> u32 {
++ match thing {
++ "rolo" => PRICE_OF_SWEETS,
++ "advice" => PRICE_OF_KINDNESS,
++ "juice" => PRICE_OF_DRINKS,
++ _ => panic!(),
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
++#![allow(dead_code)]
++
++// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need
++// the following three lines and the lazy_static crate.
++//
++// #[macro_use]
++// extern crate lazy_static;
++// use std::collections::HashMap;
++
++/// ensure that we don't suggest `is_nan` and `is_null` inside constants
++/// FIXME: once const fn is stable, suggest these functions again in constants
++
++const BAA: *const i32 = 0 as *const i32;
++static mut BAR: *const i32 = BAA;
++static mut FOO: *const i32 = 0 as *const i32;
++static mut BUH: bool = 42.0 < f32::NAN;
++
++#[allow(unused_variables, unused_mut)]
++fn main() {
++ /*
++ lazy_static! {
++ static ref MUT_MAP : HashMap<usize, &'static str> = {
++ let mut m = HashMap::new();
++ m.insert(0, "zero");
++ m
++ };
++ static ref MUT_COUNT : usize = MUT_MAP.len();
++ }
++ assert_eq!(*MUT_COUNT, 1);
++ */
++ // FIXME: don't lint in array length, requires `check_body`
++ //let _ = [""; (42.0 < f32::NAN) as usize];
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#[deny(clippy::all)]
++#[derive(Debug)]
++pub enum Error {
++ Type(&'static str),
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![deny(clippy::needless_lifetimes)]
++#![allow(dead_code)]
++
++trait Foo {}
++
++struct Bar {}
++
++struct Baz<'a> {
++ bar: &'a Bar,
++}
++
++impl<'a> Foo for Baz<'a> {}
++
++impl Bar {
++ fn baz<'a>(&'a self) -> impl Foo + 'a {
++ Baz { bar: self }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++#[deny(warnings)]
++fn main() {
++ let x = Foo;
++ println!("{:?}", x);
++}
++
++#[derive(ClippyMiniMacroTest, Debug)]
++struct Foo;
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(clippy::blacklisted_name)]
++
++pub fn foo(bar: *const u8) {
++ println!("{:#p}", bar);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
++
++#[deny(warnings)]
++fn cfg_return() -> i32 {
++ #[cfg(unix)]
++ return 1;
++ #[cfg(not(unix))]
++ return 2;
++}
++
++#[deny(warnings)]
++fn cfg_let_and_return() -> i32 {
++ #[cfg(unix)]
++ let x = 1;
++ #[cfg(not(unix))]
++ let x = 2;
++ x
++}
++
++fn main() {
++ cfg_return();
++ cfg_let_and_return();
++}
--- /dev/null
--- /dev/null
++fn main() {
++ let x: [i32; {
++ let u = 2;
++ 4
++ }] = [2; { 4 }];
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![warn(clippy::single_match_else)]
++
++//! Test for https://github.com/rust-lang/rust-clippy/issues/1588
++
++fn main() {
++ let n = match (42, 43) {
++ (42, n) => n,
++ _ => panic!("typeck error"),
++ };
++ assert_eq!(n, 43);
++}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![feature(trivial_bounds)]
++#![allow(unused, trivial_bounds)]
++
++fn test_trivial_bounds()
++where
++ i32: Iterator,
++{
++ for _ in 2i32 {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-pass
++
++#![allow(clippy::useless_attribute)] //issue #2910
++
++#[macro_use]
++extern crate serde_derive;
++
++/// Tests that we do not lint for unused underscores in a `MacroAttribute`
++/// expansion
++#[deny(clippy::used_underscore_binding)]
++#[derive(Deserialize)]
++struct MacroAttributesTest {
++ _foo: u32,
++}
++
++#[test]
++fn macro_attributes_test() {
++ let _ = MacroAttributesTest { _foo: 0 };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++# this is ignored by Clippy, but allowed for other tools like clippy-service
++[third-party]
++clippy-feature = "nightly"
--- /dev/null
--- /dev/null
++fn main() {}
--- /dev/null
--- /dev/null
++// ignore-macos
++// ignore-windows
++
++#![feature(main)]
++
++#[warn(clippy::main_recursion)]
++#[allow(unconditional_recursion)]
++#[main]
++fn a() {
++ println!("Hello, World!");
++ a();
++}
--- /dev/null
--- /dev/null
++error: recursing into entrypoint `a`
++ --> $DIR/entrypoint_recursion.rs:11:5
++ |
++LL | a();
++ | ^
++ |
++ = note: `-D clippy::main-recursion` implied by `-D warnings`
++ = help: consider using another function for this recursion
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// ignore-macos
++// ignore-windows
++
++#![feature(lang_items, link_args, start, libc)]
++#![link_args = "-nostartfiles"]
++#![no_std]
++
++use core::panic::PanicInfo;
++use core::sync::atomic::{AtomicUsize, Ordering};
++
++static N: AtomicUsize = AtomicUsize::new(0);
++
++#[warn(clippy::main_recursion)]
++#[start]
++fn main(argc: isize, argv: *const *const u8) -> isize {
++ let x = N.load(Ordering::Relaxed);
++ N.store(x + 1, Ordering::Relaxed);
++
++ if x < 3 {
++ main(argc, argv);
++ }
++
++ 0
++}
++
++#[allow(clippy::empty_loop)]
++#[panic_handler]
++fn panic(_info: &PanicInfo) -> ! {
++ loop {}
++}
++
++#[lang = "eh_personality"]
++extern "C" fn eh_personality() {}
--- /dev/null
--- /dev/null
++#[warn(clippy::main_recursion)]
++#[allow(unconditional_recursion)]
++fn main() {
++ println!("Hello, World!");
++ main();
++}
--- /dev/null
--- /dev/null
++error: recursing into entrypoint `main`
++ --> $DIR/std_main_recursion.rs:5:5
++ |
++LL | main();
++ | ^^^^
++ |
++ = note: `-D clippy::main-recursion` implied by `-D warnings`
++ = help: consider using another function for this recursion
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![deny(clippy::temporary_cstring_as_ptr)]
++
++fn main() {}
++
++fn temporary_cstring() {
++ use std::ffi::CString;
++
++ CString::new("foo").unwrap().as_ptr();
++ CString::new("foo").expect("dummy").as_ptr();
++}
++
++mod issue4375 {
++ use std::ffi::CString;
++ use std::os::raw::c_char;
++
++ extern "C" {
++ fn foo(data: *const c_char);
++ }
++
++ pub fn bar(v: &[u8]) {
++ let cstr = CString::new(v);
++ unsafe { foo(cstr.unwrap().as_ptr()) }
++ }
++}
--- /dev/null
--- /dev/null
++error: you are getting the inner pointer of a temporary `CString`
++ --> $DIR/cstring.rs:8:5
++ |
++LL | CString::new("foo").unwrap().as_ptr();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/cstring.rs:1:9
++ |
++LL | #![deny(clippy::temporary_cstring_as_ptr)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++ --> $DIR/cstring.rs:8:5
++ |
++LL | CString::new("foo").unwrap().as_ptr();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you are getting the inner pointer of a temporary `CString`
++ --> $DIR/cstring.rs:9:5
++ |
++LL | CString::new("foo").expect("dummy").as_ptr();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++ --> $DIR/cstring.rs:9:5
++ |
++LL | CString::new("foo").expect("dummy").as_ptr();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you are getting the inner pointer of a temporary `CString`
++ --> $DIR/cstring.rs:22:22
++ |
++LL | unsafe { foo(cstr.unwrap().as_ptr()) }
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++ --> $DIR/cstring.rs:22:22
++ |
++LL | unsafe { foo(cstr.unwrap().as_ptr()) }
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// rustc-env:RUST_BACKTRACE=0
++// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo"
++// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs"
++// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
++
++#![deny(clippy::internal)]
++
++fn it_looks_like_you_are_trying_to_kill_clippy() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs
++note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
++
++error: internal compiler error: unexpected panic
++
++note: the compiler unexpectedly panicked. this is a bug.
++
++note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
++
++note: Clippy version: foo
++
--- /dev/null
--- /dev/null
++#![warn(clippy::dbg_macro)]
++
++fn foo(n: u32) -> u32 {
++ if let Some(n) = dbg!(n.checked_sub(4)) {
++ n
++ } else {
++ n
++ }
++}
++
++fn factorial(n: u32) -> u32 {
++ if dbg!(n <= 1) {
++ dbg!(1)
++ } else {
++ dbg!(n * factorial(n - 1))
++ }
++}
++
++fn main() {
++ dbg!(42);
++ dbg!(dbg!(dbg!(42)));
++ foo(3) + dbg!(factorial(4));
++}
--- /dev/null
--- /dev/null
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:4:22
++ |
++LL | if let Some(n) = dbg!(n.checked_sub(4)) {
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::dbg-macro` implied by `-D warnings`
++help: ensure to avoid having uses of it in version control
++ |
++LL | if let Some(n) = n.checked_sub(4) {
++ | ^^^^^^^^^^^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:12:8
++ |
++LL | if dbg!(n <= 1) {
++ | ^^^^^^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | if n <= 1 {
++ | ^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:13:9
++ |
++LL | dbg!(1)
++ | ^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | 1
++ |
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:15:9
++ |
++LL | dbg!(n * factorial(n - 1))
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | n * factorial(n - 1)
++ |
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:20:5
++ |
++LL | dbg!(42);
++ | ^^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | 42;
++ | ^^
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:21:5
++ |
++LL | dbg!(dbg!(dbg!(42)));
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | dbg!(dbg!(42));
++ | ^^^^^^^^^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++ --> $DIR/dbg_macro.rs:22:14
++ |
++LL | foo(3) + dbg!(factorial(4));
++ | ^^^^^^^^^^^^^^^^^^
++ |
++help: ensure to avoid having uses of it in version control
++ |
++LL | foo(3) + factorial(4);
++ | ^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// compile-flags: --edition=2018
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++#![warn(clippy::debug_assert_with_mut_call)]
++#![allow(clippy::redundant_closure_call)]
++
++struct S;
++
++impl S {
++ fn bool_self_ref(&self) -> bool { false }
++ fn bool_self_mut(&mut self) -> bool { false }
++ fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false }
++ fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false }
++ fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false }
++ fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false }
++
++ fn u32_self_ref(&self) -> u32 { 0 }
++ fn u32_self_mut(&mut self) -> u32 { 0 }
++ fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 }
++ fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 }
++ fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 }
++ fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 }
++}
++
++fn bool_ref(_: &u32) -> bool { false }
++fn bool_mut(_: &mut u32) -> bool { false }
++fn u32_ref(_: &u32) -> u32 { 0 }
++fn u32_mut(_: &mut u32) -> u32 { 0 }
++
++fn func_non_mutable() {
++ debug_assert!(bool_ref(&3));
++ debug_assert!(!bool_ref(&3));
++
++ debug_assert_eq!(0, u32_ref(&3));
++ debug_assert_eq!(u32_ref(&3), 0);
++
++ debug_assert_ne!(1, u32_ref(&3));
++ debug_assert_ne!(u32_ref(&3), 1);
++}
++
++fn func_mutable() {
++ debug_assert!(bool_mut(&mut 3));
++ debug_assert!(!bool_mut(&mut 3));
++
++ debug_assert_eq!(0, u32_mut(&mut 3));
++ debug_assert_eq!(u32_mut(&mut 3), 0);
++
++ debug_assert_ne!(1, u32_mut(&mut 3));
++ debug_assert_ne!(u32_mut(&mut 3), 1);
++}
++
++fn method_non_mutable() {
++ debug_assert!(S.bool_self_ref());
++ debug_assert!(S.bool_self_ref_arg_ref(&3));
++
++ debug_assert_eq!(S.u32_self_ref(), 0);
++ debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0);
++
++ debug_assert_ne!(S.u32_self_ref(), 1);
++ debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1);
++}
++
++fn method_mutable() {
++ debug_assert!(S.bool_self_mut());
++ debug_assert!(!S.bool_self_mut());
++ debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
++ debug_assert!(S.bool_self_mut_arg_ref(&3));
++ debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
++
++ debug_assert_eq!(S.u32_self_mut(), 0);
++ debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
++ debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
++ debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
++
++ debug_assert_ne!(S.u32_self_mut(), 1);
++ debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
++ debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
++ debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
++}
++
++fn misc() {
++ // with variable
++ let mut v: Vec<u32> = vec![1, 2, 3, 4];
++ debug_assert_eq!(v.get(0), Some(&1));
++ debug_assert_ne!(v[0], 2);
++ debug_assert_eq!(v.pop(), Some(1));
++ debug_assert_ne!(Some(3), v.pop());
++
++ let a = &mut 3;
++ debug_assert!(bool_mut(a));
++
++ // nested
++ debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
++
++ // chained
++ debug_assert_eq!(v.pop().unwrap(), 3);
++
++ // format args
++ debug_assert!(bool_ref(&3), "w/o format");
++ debug_assert!(bool_mut(&mut 3), "w/o format");
++ debug_assert!(bool_ref(&3), "{} format", "w/");
++ debug_assert!(bool_mut(&mut 3), "{} format", "w/");
++
++ // sub block
++ let mut x = 42_u32;
++ debug_assert!({
++ bool_mut(&mut x);
++ x > 10
++ });
++
++ // closures
++ debug_assert!((|| {
++ let mut x = 42;
++ bool_mut(&mut x);
++ x > 10
++ })());
++}
++
++async fn debug_await() {
++ debug_assert!(async {
++ true
++ }.await);
++}
++
++fn main() {
++ func_non_mutable();
++ func_mutable();
++ method_non_mutable();
++ method_mutable();
++
++ misc();
++ debug_await();
++}
--- /dev/null
--- /dev/null
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:42:19
++ |
++LL | debug_assert!(bool_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::debug-assert-with-mut-call` implied by `-D warnings`
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:43:20
++ |
++LL | debug_assert!(!bool_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:45:25
++ |
++LL | debug_assert_eq!(0, u32_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:46:22
++ |
++LL | debug_assert_eq!(u32_mut(&mut 3), 0);
++ | ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:48:25
++ |
++LL | debug_assert_ne!(1, u32_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:49:22
++ |
++LL | debug_assert_ne!(u32_mut(&mut 3), 1);
++ | ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:64:19
++ |
++LL | debug_assert!(S.bool_self_mut());
++ | ^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:65:20
++ |
++LL | debug_assert!(!S.bool_self_mut());
++ | ^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:66:19
++ |
++LL | debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:67:19
++ |
++LL | debug_assert!(S.bool_self_mut_arg_ref(&3));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:68:19
++ |
++LL | debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:70:22
++ |
++LL | debug_assert_eq!(S.u32_self_mut(), 0);
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:71:22
++ |
++LL | debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:72:22
++ |
++LL | debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:73:22
++ |
++LL | debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:75:22
++ |
++LL | debug_assert_ne!(S.u32_self_mut(), 1);
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:76:22
++ |
++LL | debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:77:22
++ |
++LL | debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:78:22
++ |
++LL | debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:86:22
++ |
++LL | debug_assert_eq!(v.pop(), Some(1));
++ | ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++ --> $DIR/debug_assert_with_mut_call.rs:87:31
++ |
++LL | debug_assert_ne!(Some(3), v.pop());
++ | ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:90:19
++ |
++LL | debug_assert!(bool_mut(a));
++ | ^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:93:31
++ |
++LL | debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
++ | ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++ --> $DIR/debug_assert_with_mut_call.rs:96:22
++ |
++LL | debug_assert_eq!(v.pop().unwrap(), 3);
++ | ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:100:19
++ |
++LL | debug_assert!(bool_mut(&mut 3), "w/o format");
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:102:19
++ |
++LL | debug_assert!(bool_mut(&mut 3), "{} format", "w/");
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:107:9
++ |
++LL | bool_mut(&mut x);
++ | ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++ --> $DIR/debug_assert_with_mut_call.rs:114:9
++ |
++LL | bool_mut(&mut x);
++ | ^^^^^^^^^^^^^^^^
++
++error: aborting due to 28 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::decimal_literal_representation)]
++#[allow(unused_variables)]
++#[rustfmt::skip]
++fn main() {
++ let good = ( // Hex:
++ 127, // 0x7F
++ 256, // 0x100
++ 511, // 0x1FF
++ 2048, // 0x800
++ 4090, // 0xFFA
++ 16_371, // 0x3FF3
++ 61_683, // 0xF0F3
++ 2_131_750_925, // 0x7F0F_F00D
++ );
++ let bad = ( // Hex:
++ 0x8005, // 0x8005
++ 0xFF00, // 0xFF00
++ 0x7F0F_F00F, // 0x7F0F_F00F
++ 0x7FFF_FFFF, // 0x7FFF_FFFF
++ #[allow(overflowing_literals)]
++ 0xF0F0_F0F0, // 0xF0F0_F0F0
++ 0x8005_usize, // 0x8005_usize
++ 0x7F0F_F00F_isize, // 0x7F0F_F00F_isize
++ );
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::decimal_literal_representation)]
++#[allow(unused_variables)]
++#[rustfmt::skip]
++fn main() {
++ let good = ( // Hex:
++ 127, // 0x7F
++ 256, // 0x100
++ 511, // 0x1FF
++ 2048, // 0x800
++ 4090, // 0xFFA
++ 16_371, // 0x3FF3
++ 61_683, // 0xF0F3
++ 2_131_750_925, // 0x7F0F_F00D
++ );
++ let bad = ( // Hex:
++ 32_773, // 0x8005
++ 65_280, // 0xFF00
++ 2_131_750_927, // 0x7F0F_F00F
++ 2_147_483_647, // 0x7FFF_FFFF
++ #[allow(overflowing_literals)]
++ 4_042_322_160, // 0xF0F0_F0F0
++ 32_773usize, // 0x8005_usize
++ 2_131_750_927isize, // 0x7F0F_F00F_isize
++ );
++}
--- /dev/null
--- /dev/null
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:18:9
++ |
++LL | 32_773, // 0x8005
++ | ^^^^^^ help: consider: `0x8005`
++ |
++ = note: `-D clippy::decimal-literal-representation` implied by `-D warnings`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:19:9
++ |
++LL | 65_280, // 0xFF00
++ | ^^^^^^ help: consider: `0xFF00`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:20:9
++ |
++LL | 2_131_750_927, // 0x7F0F_F00F
++ | ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:21:9
++ |
++LL | 2_147_483_647, // 0x7FFF_FFFF
++ | ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:23:9
++ |
++LL | 4_042_322_160, // 0xF0F0_F0F0
++ | ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:24:9
++ |
++LL | 32_773usize, // 0x8005_usize
++ | ^^^^^^^^^^^ help: consider: `0x8005_usize`
++
++error: integer literal has a better hexadecimal representation
++ --> $DIR/decimal_literal_representation.rs:25:9
++ |
++LL | 2_131_750_927isize, // 0x7F0F_F00F_isize
++ | ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::declare_interior_mutable_const)]
++
++use std::borrow::Cow;
++use std::cell::Cell;
++use std::fmt::Display;
++use std::sync::atomic::AtomicUsize;
++use std::sync::Once;
++
++const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
++const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
++const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++//~^ ERROR interior mutable
++
++macro_rules! declare_const {
++ ($name:ident: $ty:ty = $e:expr) => {
++ const $name: $ty = $e;
++ };
++}
++declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
++
++// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
++
++const INTEGER: u8 = 8;
++const STRING: String = String::new();
++const STR: &str = "012345";
++const COW: Cow<str> = Cow::Borrowed("abcdef");
++//^ note: a const item of Cow is used in the `postgres` package.
++
++const NO_ANN: &dyn Display = &70;
++
++static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
++//^ there should be no lints on this line
++
++#[allow(clippy::declare_interior_mutable_const)]
++const ONCE_INIT: Once = Once::new();
++
++trait Trait<T>: Copy {
++ type NonCopyType;
++
++ const ATOMIC: AtomicUsize; //~ ERROR interior mutable
++ const INTEGER: u64;
++ const STRING: String;
++ const SELF: Self; // (no error)
++ const INPUT: T;
++ //~^ ERROR interior mutable
++ //~| HELP consider requiring `T` to be `Copy`
++ const ASSOC: Self::NonCopyType;
++ //~^ ERROR interior mutable
++ //~| HELP consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
++
++ const AN_INPUT: T = Self::INPUT;
++ //~^ ERROR interior mutable
++ //~| ERROR consider requiring `T` to be `Copy`
++ declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
++}
++
++trait Trait2 {
++ type CopyType: Copy;
++
++ const SELF_2: Self;
++ //~^ ERROR interior mutable
++ //~| HELP consider requiring `Self` to be `Copy`
++ const ASSOC_2: Self::CopyType; // (no error)
++}
++
++// we don't lint impl of traits, because an impl has no power to change the interface.
++impl Trait<u32> for u64 {
++ type NonCopyType = u16;
++
++ const ATOMIC: AtomicUsize = AtomicUsize::new(9);
++ const INTEGER: u64 = 10;
++ const STRING: String = String::new();
++ const SELF: Self = 11;
++ const INPUT: u32 = 12;
++ const ASSOC: Self::NonCopyType = 13;
++}
++
++struct Local<T, U>(T, U);
++
++impl<T: Trait2 + Trait<u32>, U: Trait2> Local<T, U> {
++ const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
++ const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
++ const T_SELF: T = T::SELF_2;
++ const U_SELF: U = U::SELF_2;
++ //~^ ERROR interior mutable
++ //~| HELP consider requiring `U` to be `Copy`
++ const T_ASSOC: T::NonCopyType = T::ASSOC;
++ //~^ ERROR interior mutable
++ //~| HELP consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
++ const U_ASSOC: U::CopyType = U::ASSOC_2;
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:9:1
++ |
++LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | make this a static item (maybe with lazy_static)
++ |
++ = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:10:1
++ |
++LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | make this a static item (maybe with lazy_static)
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:11:1
++ |
++LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | make this a static item (maybe with lazy_static)
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:16:9
++ |
++LL | const $name: $ty = $e;
++ | ^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
++ | ------------------------------------------ in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:40:5
++ |
++LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:44:5
++ |
++LL | const INPUT: T;
++ | ^^^^^^^^^^^^^-^
++ | |
++ | consider requiring `T` to be `Copy`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:47:5
++ |
++LL | const ASSOC: Self::NonCopyType;
++ | ^^^^^^^^^^^^^-----------------^
++ | |
++ | consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:51:5
++ |
++LL | const AN_INPUT: T = Self::INPUT;
++ | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^
++ | |
++ | consider requiring `T` to be `Copy`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:16:9
++ |
++LL | const $name: $ty = $e;
++ | ^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
++ | ----------------------------------------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:60:5
++ |
++LL | const SELF_2: Self;
++ | ^^^^^^^^^^^^^^----^
++ | |
++ | consider requiring `Self` to be `Copy`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:81:5
++ |
++LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:84:5
++ |
++LL | const U_SELF: U = U::SELF_2;
++ | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^
++ | |
++ | consider requiring `U` to be `Copy`
++
++error: a `const` item should never be interior mutable
++ --> $DIR/declare_interior_mutable_const.rs:87:5
++ |
++LL | const T_ASSOC: T::NonCopyType = T::ASSOC;
++ | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^
++ | |
++ | consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-windows
++// ignore-macos
++
++#![feature(no_core, lang_items, start)]
++#![no_core]
++
++#[link(name = "c")]
++extern "C" {}
++
++#[lang = "sized"]
++pub trait Sized {}
++#[lang = "copy"]
++pub trait Copy {}
++#[lang = "freeze"]
++pub unsafe trait Freeze {}
++
++#[lang = "start"]
++#[start]
++fn start(_argc: isize, _argv: *const *const u8) -> isize {
++ 0
++}
++
++pub struct A;
++
++impl A {
++ pub fn as_ref(self) -> &'static str {
++ "A"
++ }
++}
--- /dev/null
--- /dev/null
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++ --> $DIR/def_id_nocore.rs:26:19
++ |
++LL | pub fn as_ref(self) -> &'static str {
++ | ^^^^
++ |
++ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++#[macro_use]
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++extern crate rustc_lint;
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT_DEFAULT,
++ Warn,
++ "default lint description",
++ report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]);
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: the lint `TEST_LINT_DEFAULT` has the default lint description
++ --> $DIR/default_lint.rs:17:1
++ |
++LL | / declare_tool_lint! {
++LL | | pub clippy::TEST_LINT_DEFAULT,
++LL | | Warn,
++LL | | "default lint description",
++LL | | report_in_external_macro: true
++LL | | }
++ | |_^
++ |
++note: the lint level is defined here
++ --> $DIR/default_lint.rs:1:9
++ |
++LL | #![deny(clippy::internal)]
++ | ^^^^^^^^^^^^^^^^
++ = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::default_trait_access)]
++
++use std::default;
++use std::default::Default as D2;
++use std::string;
++
++fn main() {
++ let s1: String = Default::default();
++
++ let s2 = String::default();
++
++ let s3: String = D2::default();
++
++ let s4: String = std::default::Default::default();
++
++ let s5 = string::String::default();
++
++ let s6: String = default::Default::default();
++
++ let s7 = std::string::String::default();
++
++ let s8: String = DefaultFactory::make_t_badly();
++
++ let s9: String = DefaultFactory::make_t_nicely();
++
++ let s10 = DerivedDefault::default();
++
++ let s11: GenericDerivedDefault<String> = Default::default();
++
++ let s12 = GenericDerivedDefault::<String>::default();
++
++ let s13 = TupleDerivedDefault::default();
++
++ let s14: TupleDerivedDefault = Default::default();
++
++ let s15: ArrayDerivedDefault = Default::default();
++
++ let s16 = ArrayDerivedDefault::default();
++
++ let s17: TupleStructDerivedDefault = Default::default();
++
++ let s18 = TupleStructDerivedDefault::default();
++
++ let s19 = <DerivedDefault as Default>::default();
++
++ println!(
++ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
++ s1,
++ s2,
++ s3,
++ s4,
++ s5,
++ s6,
++ s7,
++ s8,
++ s9,
++ s10,
++ s11,
++ s12,
++ s13,
++ s14,
++ s15,
++ s16,
++ s17,
++ s18,
++ s19,
++ );
++}
++
++struct DefaultFactory;
++
++impl DefaultFactory {
++ pub fn make_t_badly<T: Default>() -> T {
++ Default::default()
++ }
++
++ pub fn make_t_nicely<T: Default>() -> T {
++ T::default()
++ }
++}
++
++#[derive(Debug, Default)]
++struct DerivedDefault {
++ pub s: String,
++}
++
++#[derive(Debug, Default)]
++struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
++ pub s: T,
++}
++
++#[derive(Debug, Default)]
++struct TupleDerivedDefault {
++ pub s: (String, String),
++}
++
++#[derive(Debug, Default)]
++struct ArrayDerivedDefault {
++ pub s: [String; 10],
++}
++
++#[derive(Debug, Default)]
++struct TupleStructDerivedDefault(String);
--- /dev/null
--- /dev/null
++error: Calling `std::string::String::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:8:22
++ |
++LL | let s1: String = Default::default();
++ | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++ |
++ = note: `-D clippy::default-trait-access` implied by `-D warnings`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:12:22
++ |
++LL | let s3: String = D2::default();
++ | ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:14:22
++ |
++LL | let s4: String = std::default::Default::default();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:18:22
++ |
++LL | let s6: String = default::Default::default();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:28:46
++ |
++LL | let s11: GenericDerivedDefault<String> = Default::default();
++ | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
++
++error: Calling `TupleDerivedDefault::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:34:36
++ |
++LL | let s14: TupleDerivedDefault = Default::default();
++ | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
++
++error: Calling `ArrayDerivedDefault::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:36:36
++ |
++LL | let s15: ArrayDerivedDefault = Default::default();
++ | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
++
++error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression
++ --> $DIR/default_trait_access.rs:40:42
++ |
++LL | let s17: TupleStructDerivedDefault = Default::default();
++ | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::str_to_string)]
++#[warn(clippy::string_to_string)]
++#[warn(clippy::unstable_as_slice)]
++#[warn(clippy::unstable_as_mut_slice)]
++#[warn(clippy::misaligned_transmute)]
++#[warn(clippy::unused_collect)]
++#[warn(clippy::invalid_ref)]
++#[warn(clippy::into_iter_on_array)]
++#[warn(clippy::unused_label)]
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated.rs:1:8
++ |
++LL | #[warn(clippy::str_to_string)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated.rs:2:8
++ |
++LL | #[warn(clippy::string_to_string)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
++ --> $DIR/deprecated.rs:3:8
++ |
++LL | #[warn(clippy::unstable_as_slice)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
++ --> $DIR/deprecated.rs:4: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:5: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:6:8
++ |
++LL | #[warn(clippy::unused_collect)]
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value``
++ --> $DIR/deprecated.rs:7:8
++ |
++LL | #[warn(clippy::invalid_ref)]
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter``
++ --> $DIR/deprecated.rs:8:8
++ |
++LL | #[warn(clippy::into_iter_on_array)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels``
++ --> $DIR/deprecated.rs:9:8
++ |
++LL | #[warn(clippy::unused_label)]
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated.rs:1:8
++ |
++LL | #[warn(clippy::str_to_string)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(str_to_string)]
++#[warn(string_to_string)]
++#[warn(unstable_as_slice)]
++#[warn(unstable_as_mut_slice)]
++#[warn(misaligned_transmute)]
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated_old.rs:1:8
++ |
++LL | #[warn(str_to_string)]
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated_old.rs:2:8
++ |
++LL | #[warn(string_to_string)]
++ | ^^^^^^^^^^^^^^^^
++
++error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
++ --> $DIR/deprecated_old.rs:3:8
++ |
++LL | #[warn(unstable_as_slice)]
++ | ^^^^^^^^^^^^^^^^^
++
++error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
++ --> $DIR/deprecated_old.rs:4:8
++ |
++LL | #[warn(unstable_as_mut_slice)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr`
++ --> $DIR/deprecated_old.rs:5:8
++ |
++LL | #[warn(misaligned_transmute)]
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++ --> $DIR/deprecated_old.rs:1:8
++ |
++LL | #[warn(str_to_string)]
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn get_number() -> usize {
++ 10
++}
++
++fn get_reference(n: &usize) -> &usize {
++ n
++}
++
++#[allow(clippy::many_single_char_names, clippy::double_parens)]
++#[allow(unused_variables, unused_parens)]
++#[warn(clippy::deref_addrof)]
++fn main() {
++ let a = 10;
++ let aref = &a;
++
++ let b = a;
++
++ let b = get_number();
++
++ let b = *get_reference(&a);
++
++ let bytes: Vec<usize> = vec![1, 2, 3, 4];
++ let b = bytes[1..2][0];
++
++ //This produces a suggestion of 'let b = (a);' which
++ //will trigger the 'unused_parens' lint
++ let b = (a);
++
++ let b = a;
++
++ #[rustfmt::skip]
++ let b = a;
++
++ let b = &a;
++
++ let b = *aref;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn get_number() -> usize {
++ 10
++}
++
++fn get_reference(n: &usize) -> &usize {
++ n
++}
++
++#[allow(clippy::many_single_char_names, clippy::double_parens)]
++#[allow(unused_variables, unused_parens)]
++#[warn(clippy::deref_addrof)]
++fn main() {
++ let a = 10;
++ let aref = &a;
++
++ let b = *&a;
++
++ let b = *&get_number();
++
++ let b = *get_reference(&a);
++
++ let bytes: Vec<usize> = vec![1, 2, 3, 4];
++ let b = *&bytes[1..2][0];
++
++ //This produces a suggestion of 'let b = (a);' which
++ //will trigger the 'unused_parens' lint
++ let b = *&(a);
++
++ let b = *(&a);
++
++ #[rustfmt::skip]
++ let b = *((&a));
++
++ let b = *&&a;
++
++ let b = **&aref;
++}
--- /dev/null
--- /dev/null
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:18:13
++ |
++LL | let b = *&a;
++ | ^^^ help: try this: `a`
++ |
++ = note: `-D clippy::deref-addrof` implied by `-D warnings`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:20:13
++ |
++LL | let b = *&get_number();
++ | ^^^^^^^^^^^^^^ help: try this: `get_number()`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:25:13
++ |
++LL | let b = *&bytes[1..2][0];
++ | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:29:13
++ |
++LL | let b = *&(a);
++ | ^^^^^ help: try this: `(a)`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:31:13
++ |
++LL | let b = *(&a);
++ | ^^^^^ help: try this: `a`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:34:13
++ |
++LL | let b = *((&a));
++ | ^^^^^^^ help: try this: `a`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:36:13
++ |
++LL | let b = *&&a;
++ | ^^^^ help: try this: `&a`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof.rs:38:14
++ |
++LL | let b = **&aref;
++ | ^^^^^^ help: try this: `aref`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// This test can't work with run-rustfix because it needs two passes of test+fix
++
++#[warn(clippy::deref_addrof)]
++#[allow(unused_variables, unused_mut)]
++fn main() {
++ let a = 10;
++
++ //This produces a suggestion of 'let b = *&a;' which
++ //will trigger the 'clippy::deref_addrof' lint again
++ let b = **&&a;
++
++ {
++ let mut x = 10;
++ let y = *&mut x;
++ }
++
++ {
++ //This produces a suggestion of 'let y = *&mut x' which
++ //will trigger the 'clippy::deref_addrof' lint again
++ let mut x = 10;
++ let y = **&mut &mut x;
++ }
++}
--- /dev/null
--- /dev/null
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof_double_trigger.rs:10:14
++ |
++LL | let b = **&&a;
++ | ^^^^ help: try this: `&a`
++ |
++ = note: `-D clippy::deref-addrof` implied by `-D warnings`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof_double_trigger.rs:14:17
++ |
++LL | let y = *&mut x;
++ | ^^^^^^^ help: try this: `x`
++
++error: immediately dereferencing a reference
++ --> $DIR/deref_addrof_double_trigger.rs:21:18
++ |
++LL | let y = **&mut &mut x;
++ | ^^^^^^^^^^^^ help: try this: `&mut x`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++macro_rules! m {
++ ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] };
++}
++
++#[warn(clippy::deref_addrof)]
++fn f() -> [(i32, &'static str); 3] {
++ *m![1, 2, 3] // should be fine
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
++#![warn(clippy::explicit_deref_methods)]
++
++use std::ops::{Deref, DerefMut};
++
++fn concat(deref_str: &str) -> String {
++ format!("{}bar", deref_str)
++}
++
++fn just_return(deref_str: &str) -> &str {
++ deref_str
++}
++
++struct CustomVec(Vec<u8>);
++impl Deref for CustomVec {
++ type Target = Vec<u8>;
++
++ fn deref(&self) -> &Vec<u8> {
++ &self.0
++ }
++}
++
++fn main() {
++ let a: &mut String = &mut String::from("foo");
++
++ // these should require linting
++
++ let b: &str = &*a;
++
++ let b: &mut str = &mut *a;
++
++ // both derefs should get linted here
++ let b: String = format!("{}, {}", &*a, &*a);
++
++ println!("{}", &*a);
++
++ #[allow(clippy::match_single_binding)]
++ match &*a {
++ _ => (),
++ }
++
++ let b: String = concat(&*a);
++
++ let b = &*just_return(a);
++
++ let b: String = concat(&*just_return(a));
++
++ let b: &str = &*a.deref();
++
++ let opt_a = Some(a.clone());
++ let b = &*opt_a.unwrap();
++
++ // following should not require linting
++
++ let cv = CustomVec(vec![0, 42]);
++ let c = cv.deref()[0];
++
++ let b: &str = &*a.deref();
++
++ let b: String = a.deref().clone();
++
++ let b: usize = a.deref_mut().len();
++
++ let b: &usize = &a.deref().len();
++
++ let b: &str = &*a;
++
++ let b: &mut str = &mut *a;
++
++ macro_rules! expr_deref {
++ ($body:expr) => {
++ $body.deref()
++ };
++ }
++ let b: &str = expr_deref!(a);
++
++ // The struct does not implement Deref trait
++ #[derive(Copy, Clone)]
++ struct NoLint(u32);
++ impl NoLint {
++ pub fn deref(self) -> u32 {
++ self.0
++ }
++ pub fn deref_mut(self) -> u32 {
++ self.0
++ }
++ }
++ let no_lint = NoLint(42);
++ let b = no_lint.deref();
++ let b = no_lint.deref_mut();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
++#![warn(clippy::explicit_deref_methods)]
++
++use std::ops::{Deref, DerefMut};
++
++fn concat(deref_str: &str) -> String {
++ format!("{}bar", deref_str)
++}
++
++fn just_return(deref_str: &str) -> &str {
++ deref_str
++}
++
++struct CustomVec(Vec<u8>);
++impl Deref for CustomVec {
++ type Target = Vec<u8>;
++
++ fn deref(&self) -> &Vec<u8> {
++ &self.0
++ }
++}
++
++fn main() {
++ let a: &mut String = &mut String::from("foo");
++
++ // these should require linting
++
++ let b: &str = a.deref();
++
++ let b: &mut str = a.deref_mut();
++
++ // both derefs should get linted here
++ let b: String = format!("{}, {}", a.deref(), a.deref());
++
++ println!("{}", a.deref());
++
++ #[allow(clippy::match_single_binding)]
++ match a.deref() {
++ _ => (),
++ }
++
++ let b: String = concat(a.deref());
++
++ let b = just_return(a).deref();
++
++ let b: String = concat(just_return(a).deref());
++
++ let b: &str = a.deref().deref();
++
++ let opt_a = Some(a.clone());
++ let b = opt_a.unwrap().deref();
++
++ // following should not require linting
++
++ let cv = CustomVec(vec![0, 42]);
++ let c = cv.deref()[0];
++
++ let b: &str = &*a.deref();
++
++ let b: String = a.deref().clone();
++
++ let b: usize = a.deref_mut().len();
++
++ let b: &usize = &a.deref().len();
++
++ let b: &str = &*a;
++
++ let b: &mut str = &mut *a;
++
++ macro_rules! expr_deref {
++ ($body:expr) => {
++ $body.deref()
++ };
++ }
++ let b: &str = expr_deref!(a);
++
++ // The struct does not implement Deref trait
++ #[derive(Copy, Clone)]
++ struct NoLint(u32);
++ impl NoLint {
++ pub fn deref(self) -> u32 {
++ self.0
++ }
++ pub fn deref_mut(self) -> u32 {
++ self.0
++ }
++ }
++ let no_lint = NoLint(42);
++ let b = no_lint.deref();
++ let b = no_lint.deref_mut();
++}
--- /dev/null
--- /dev/null
++error: explicit deref method call
++ --> $DIR/dereference.rs:30:19
++ |
++LL | let b: &str = a.deref();
++ | ^^^^^^^^^ help: try this: `&*a`
++ |
++ = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
++
++error: explicit deref_mut method call
++ --> $DIR/dereference.rs:32:23
++ |
++LL | let b: &mut str = a.deref_mut();
++ | ^^^^^^^^^^^^^ help: try this: `&mut *a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:35:39
++ |
++LL | let b: String = format!("{}, {}", a.deref(), a.deref());
++ | ^^^^^^^^^ help: try this: `&*a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:35:50
++ |
++LL | let b: String = format!("{}, {}", a.deref(), a.deref());
++ | ^^^^^^^^^ help: try this: `&*a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:37:20
++ |
++LL | println!("{}", a.deref());
++ | ^^^^^^^^^ help: try this: `&*a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:40:11
++ |
++LL | match a.deref() {
++ | ^^^^^^^^^ help: try this: `&*a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:44:28
++ |
++LL | let b: String = concat(a.deref());
++ | ^^^^^^^^^ help: try this: `&*a`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:46:13
++ |
++LL | let b = just_return(a).deref();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:48:28
++ |
++LL | let b: String = concat(just_return(a).deref());
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:50:19
++ |
++LL | let b: &str = a.deref().deref();
++ | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()`
++
++error: explicit deref method call
++ --> $DIR/dereference.rs:53:13
++ |
++LL | let b = opt_a.unwrap().deref();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(untagged_unions)]
++#![allow(dead_code)]
++#![warn(clippy::expl_impl_clone_on_copy)]
++
++#[derive(Copy)]
++struct Qux;
++
++impl Clone for Qux {
++ fn clone(&self) -> Self {
++ Qux
++ }
++}
++
++// looks like unions don't support deriving Clone for now
++#[derive(Copy)]
++union Union {
++ a: u8,
++}
++
++impl Clone for Union {
++ fn clone(&self) -> Self {
++ Union { a: 42 }
++ }
++}
++
++// See #666
++#[derive(Copy)]
++struct Lt<'a> {
++ a: &'a u8,
++}
++
++impl<'a> Clone for Lt<'a> {
++ fn clone(&self) -> Self {
++ unimplemented!()
++ }
++}
++
++// Ok, `Clone` cannot be derived because of the big array
++#[derive(Copy)]
++struct BigArray {
++ a: [u8; 65],
++}
++
++impl Clone for BigArray {
++ fn clone(&self) -> Self {
++ unimplemented!()
++ }
++}
++
++// Ok, function pointers are not always Clone
++#[derive(Copy)]
++struct FnPtr {
++ a: fn() -> !,
++}
++
++impl Clone for FnPtr {
++ fn clone(&self) -> Self {
++ unimplemented!()
++ }
++}
++
++// Ok, generics
++#[derive(Copy)]
++struct Generic<T> {
++ a: T,
++}
++
++impl<T> Clone for Generic<T> {
++ fn clone(&self) -> Self {
++ unimplemented!()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you are implementing `Clone` explicitly on a `Copy` type
++ --> $DIR/derive.rs:8:1
++ |
++LL | / impl Clone for Qux {
++LL | | fn clone(&self) -> Self {
++LL | | Qux
++LL | | }
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings`
++note: consider deriving `Clone` or removing `Copy`
++ --> $DIR/derive.rs:8:1
++ |
++LL | / impl Clone for Qux {
++LL | | fn clone(&self) -> Self {
++LL | | Qux
++LL | | }
++LL | | }
++ | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++ --> $DIR/derive.rs:32:1
++ |
++LL | / impl<'a> Clone for Lt<'a> {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++ |
++note: consider deriving `Clone` or removing `Copy`
++ --> $DIR/derive.rs:32:1
++ |
++LL | / impl<'a> Clone for Lt<'a> {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++ --> $DIR/derive.rs:44:1
++ |
++LL | / impl Clone for BigArray {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++ |
++note: consider deriving `Clone` or removing `Copy`
++ --> $DIR/derive.rs:44:1
++ |
++LL | / impl Clone for BigArray {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++ --> $DIR/derive.rs:56:1
++ |
++LL | / impl Clone for FnPtr {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++ |
++note: consider deriving `Clone` or removing `Copy`
++ --> $DIR/derive.rs:56:1
++ |
++LL | / impl Clone for FnPtr {
++LL | | fn clone(&self) -> Self {
++LL | | unimplemented!()
++LL | | }
++LL | | }
++ | |_^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#[derive(PartialEq, Hash)]
++struct Foo;
++
++impl PartialEq<u64> for Foo {
++ fn eq(&self, _: &u64) -> bool {
++ true
++ }
++}
++
++#[derive(Hash)]
++struct Bar;
++
++impl PartialEq for Bar {
++ fn eq(&self, _: &Bar) -> bool {
++ true
++ }
++}
++
++#[derive(Hash)]
++struct Baz;
++
++impl PartialEq<Baz> for Baz {
++ fn eq(&self, _: &Baz) -> bool {
++ true
++ }
++}
++
++#[derive(PartialEq)]
++struct Bah;
++
++impl std::hash::Hash for Bah {
++ fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++}
++
++#[derive(PartialEq)]
++struct Foo2;
++
++trait Hash {}
++
++// We don't want to lint on user-defined traits called `Hash`
++impl Hash for Foo2 {}
++
++mod use_hash {
++ use std::hash::{Hash, Hasher};
++
++ #[derive(PartialEq)]
++ struct Foo3;
++
++ impl Hash for Foo3 {
++ fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you are deriving `Hash` but have implemented `PartialEq` explicitly
++ --> $DIR/derive_hash_xor_eq.rs:10:10
++ |
++LL | #[derive(Hash)]
++ | ^^^^
++ |
++ = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default
++note: `PartialEq` implemented here
++ --> $DIR/derive_hash_xor_eq.rs:13:1
++ |
++LL | / impl PartialEq for Bar {
++LL | | fn eq(&self, _: &Bar) -> bool {
++LL | | true
++LL | | }
++LL | | }
++ | |_^
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `Hash` but have implemented `PartialEq` explicitly
++ --> $DIR/derive_hash_xor_eq.rs:19:10
++ |
++LL | #[derive(Hash)]
++ | ^^^^
++ |
++note: `PartialEq` implemented here
++ --> $DIR/derive_hash_xor_eq.rs:22:1
++ |
++LL | / impl PartialEq<Baz> for Baz {
++LL | | fn eq(&self, _: &Baz) -> bool {
++LL | | true
++LL | | }
++LL | | }
++ | |_^
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are implementing `Hash` explicitly but have derived `PartialEq`
++ --> $DIR/derive_hash_xor_eq.rs:31:1
++ |
++LL | / impl std::hash::Hash for Bah {
++LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++LL | | }
++ | |_^
++ |
++note: `PartialEq` implemented here
++ --> $DIR/derive_hash_xor_eq.rs:28:10
++ |
++LL | #[derive(PartialEq)]
++ | ^^^^^^^^^
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are implementing `Hash` explicitly but have derived `PartialEq`
++ --> $DIR/derive_hash_xor_eq.rs:49:5
++ |
++LL | / impl Hash for Foo3 {
++LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++LL | | }
++ | |_____^
++ |
++note: `PartialEq` implemented here
++ --> $DIR/derive_hash_xor_eq.rs:46:14
++ |
++LL | #[derive(PartialEq)]
++ | ^^^^^^^^^
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::diverging_sub_expression)]
++#![allow(clippy::match_same_arms, clippy::logic_bug)]
++
++#[allow(clippy::empty_loop)]
++fn diverge() -> ! {
++ loop {}
++}
++
++struct A;
++
++impl A {
++ fn foo(&self) -> ! {
++ diverge()
++ }
++}
++
++#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
++fn main() {
++ let b = true;
++ b || diverge();
++ b || A.foo();
++}
++
++#[allow(dead_code, unused_variables)]
++fn foobar() {
++ loop {
++ let x = match 5 {
++ 4 => return,
++ 5 => continue,
++ 6 => true || return,
++ 7 => true || continue,
++ 8 => break,
++ 9 => diverge(),
++ 3 => true || diverge(),
++ 10 => match 42 {
++ 99 => return,
++ _ => true || panic!("boo"),
++ },
++ _ => true || break,
++ };
++ }
++}
--- /dev/null
--- /dev/null
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:20:10
++ |
++LL | b || diverge();
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:21:10
++ |
++LL | b || A.foo();
++ | ^^^^^^^
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:30:26
++ |
++LL | 6 => true || return,
++ | ^^^^^^
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:31:26
++ |
++LL | 7 => true || continue,
++ | ^^^^^^^^
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:34:26
++ |
++LL | 3 => true || diverge(),
++ | ^^^^^^^^^
++
++error: sub-expression diverges
++ --> $DIR/diverging_sub_expression.rs:39:26
++ |
++LL | _ => true || break,
++ | ^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(associated_type_defaults)]
++#![warn(clippy::linkedlist)]
++#![allow(dead_code, clippy::needless_pass_by_value)]
++
++extern crate alloc;
++use alloc::collections::linked_list::LinkedList;
++
++trait Foo {
++ type Baz = LinkedList<u8>;
++ fn foo(_: LinkedList<u8>);
++ const BAR: Option<LinkedList<u8>>;
++}
++
++// Ok, we don’t want to warn for implementations; see issue #605.
++impl Foo for LinkedList<u8> {
++ fn foo(_: LinkedList<u8>) {}
++ const BAR: Option<LinkedList<u8>> = None;
++}
++
++struct Bar;
++impl Bar {
++ fn foo(_: LinkedList<u8>) {}
++}
++
++pub fn test(my_favourite_linked_list: LinkedList<u8>) {
++ println!("{:?}", my_favourite_linked_list)
++}
++
++pub fn test_ret() -> Option<LinkedList<u8>> {
++ unimplemented!();
++}
++
++pub fn test_local_not_linted() {
++ let _: LinkedList<u8>;
++}
++
++fn main() {
++ test(LinkedList::new());
++ test_local_not_linted();
++}
--- /dev/null
--- /dev/null
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:9:16
++ |
++LL | type Baz = LinkedList<u8>;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::linkedlist` implied by `-D warnings`
++ = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:10:15
++ |
++LL | fn foo(_: LinkedList<u8>);
++ | ^^^^^^^^^^^^^^
++ |
++ = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:11:23
++ |
++LL | const BAR: Option<LinkedList<u8>>;
++ | ^^^^^^^^^^^^^^
++ |
++ = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:22:15
++ |
++LL | fn foo(_: LinkedList<u8>) {}
++ | ^^^^^^^^^^^^^^
++ |
++ = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:25:39
++ |
++LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
++ | ^^^^^^^^^^^^^^
++ |
++ = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++ --> $DIR/dlist.rs:29:29
++ |
++LL | pub fn test_ret() -> Option<LinkedList<u8>> {
++ | ^^^^^^^^^^^^^^
++ |
++ = help: a `VecDeque` might work
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++//! This file tests for the `DOC_MARKDOWN` lint.
++
++#![allow(dead_code)]
++#![warn(clippy::doc_markdown)]
++#![feature(custom_inner_attributes)]
++#![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 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() {}
++
++/// Ok: CamelCase (It should not be surrounded by backticks)
++fn issue_2395() {}
++
++/// 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() {}
--- /dev/null
--- /dev/null
++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 `link_with_underscores` between ticks in the documentation
++ --> $DIR/doc.rs:52: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:55: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:65:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++ --> $DIR/doc.rs:73:8
++ |
++LL | /// ## CamelCaseThing
++ | ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++ --> $DIR/doc.rs:76:7
++ |
++LL | /// # CamelCaseThing
++ | ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++ --> $DIR/doc.rs:78: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:79: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:86: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:99:5
++ |
++LL | /// be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++ --> $DIR/doc.rs:110:43
++ |
++LL | /** E.g., serialization of an empty list: FooBar
++ | ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++ --> $DIR/doc.rs:115: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:116:1
++ |
++LL | be_sure_we_got_to_the_end_of_it
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++ --> $DIR/doc.rs:121:43
++ |
++LL | /** E.g., serialization of an empty list: FooBar
++ | ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++ --> $DIR/doc.rs:126: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:127: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:138: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:165:13
++ |
++LL | /// Not ok: http://www.unicode.org
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++ --> $DIR/doc.rs:166:13
++ |
++LL | /// Not ok: https://www.unicode.org
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++ --> $DIR/doc.rs:167:13
++ |
++LL | /// Not ok: http://www.unicode.org/
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++ --> $DIR/doc.rs:168: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:174:22
++ |
++LL | /// An iterator over mycrate::Collection's values.
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 30 previous errors
++
--- /dev/null
--- /dev/null
++// edition:2018
++#![warn(clippy::missing_errors_doc)]
++
++use std::io;
++
++pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++/// This is not sufficiently documented.
++pub fn pub_fn_returning_io_result() -> io::Result<()> {
++ unimplemented!();
++}
++
++/// This is not sufficiently documented.
++pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
++ unimplemented!();
++}
++
++/// # Errors
++/// A description of the errors goes here.
++pub fn pub_fn_with_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++/// # Errors
++/// A description of the errors goes here.
++pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++/// This function doesn't require the documentation because it is private
++fn priv_fn_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++/// This function doesn't require the documentation because it is private
++async fn async_priv_fn_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++}
++
++pub struct Struct1;
++
++impl Struct1 {
++ /// This is not sufficiently documented.
++ pub fn pub_method_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ /// This is not sufficiently documented.
++ pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ /// # Errors
++ /// A description of the errors goes here.
++ pub fn pub_method_with_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ /// # Errors
++ /// A description of the errors goes here.
++ pub async fn async_pub_method_with_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ /// This function doesn't require the documentation because it is private.
++ fn priv_method_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ /// This function doesn't require the documentation because it is private.
++ async fn async_priv_method_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++}
++
++pub trait Trait1 {
++ /// This is not sufficiently documented.
++ fn trait_method_missing_errors_header() -> Result<(), ()>;
++
++ /// # Errors
++ /// A description of the errors goes here.
++ fn trait_method_with_errors_header() -> Result<(), ()>;
++}
++
++impl Trait1 for Struct1 {
++ fn trait_method_missing_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++
++ fn trait_method_with_errors_header() -> Result<(), ()> {
++ unimplemented!();
++ }
++}
++
++fn main() -> Result<(), ()> {
++ Ok(())
++}
--- /dev/null
--- /dev/null
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:6:1
++ |
++LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
++LL | | unimplemented!();
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::missing-errors-doc` implied by `-D warnings`
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:10:1
++ |
++LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
++LL | | unimplemented!();
++LL | | }
++ | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:15:1
++ |
++LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> {
++LL | | unimplemented!();
++LL | | }
++ | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:20:1
++ |
++LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
++LL | | unimplemented!();
++LL | | }
++ | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:50:5
++ |
++LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:55:5
++ |
++LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: docs for function returning `Result` missing `# Errors` section
++ --> $DIR/doc_errors.rs:84:5
++ |
++LL | fn trait_method_missing_errors_header() -> Result<(), ()>;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:doc_unsafe_macros.rs
++
++#[macro_use]
++extern crate doc_unsafe_macros;
++
++/// This is not sufficiently documented
++pub unsafe fn destroy_the_planet() {
++ unimplemented!();
++}
++
++/// This one is
++///
++/// # Safety
++///
++/// This function shouldn't be called unless the horsemen are ready
++pub unsafe fn apocalypse(universe: &mut ()) {
++ unimplemented!();
++}
++
++/// This is a private function, so docs aren't necessary
++unsafe fn you_dont_see_me() {
++ unimplemented!();
++}
++
++mod private_mod {
++ pub unsafe fn only_crate_wide_accessible() {
++ unimplemented!();
++ }
++
++ pub unsafe fn republished() {
++ unimplemented!();
++ }
++}
++
++pub use private_mod::republished;
++
++pub trait UnsafeTrait {
++ unsafe fn woefully_underdocumented(self);
++
++ /// # Safety
++ unsafe fn at_least_somewhat_documented(self);
++}
++
++pub struct Struct;
++
++impl UnsafeTrait for Struct {
++ unsafe fn woefully_underdocumented(self) {
++ // all is well
++ }
++
++ unsafe fn at_least_somewhat_documented(self) {
++ // all is still well
++ }
++}
++
++impl Struct {
++ pub unsafe fn more_undocumented_unsafe() -> Self {
++ unimplemented!();
++ }
++
++ /// # Safety
++ pub unsafe fn somewhat_documented(&self) {
++ unimplemented!();
++ }
++
++ unsafe fn private(&self) {
++ unimplemented!();
++ }
++}
++
++macro_rules! very_unsafe {
++ () => {
++ pub unsafe fn whee() {
++ unimplemented!()
++ }
++
++ /// # Safety
++ ///
++ /// Please keep the seat belt fastened
++ pub unsafe fn drive() {
++ whee()
++ }
++ };
++}
++
++very_unsafe!();
++
++// we don't lint code from external macros
++undocd_unsafe!();
++
++fn main() {
++ unsafe {
++ you_dont_see_me();
++ destroy_the_planet();
++ let mut universe = ();
++ apocalypse(&mut universe);
++ private_mod::only_crate_wide_accessible();
++ drive();
++ }
++}
--- /dev/null
--- /dev/null
++error: unsafe function's docs miss `# Safety` section
++ --> $DIR/doc_unsafe.rs:7:1
++ |
++LL | / pub unsafe fn destroy_the_planet() {
++LL | | unimplemented!();
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
++
++error: unsafe function's docs miss `# Safety` section
++ --> $DIR/doc_unsafe.rs:30:5
++ |
++LL | / pub unsafe fn republished() {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: unsafe function's docs miss `# Safety` section
++ --> $DIR/doc_unsafe.rs:38:5
++ |
++LL | unsafe fn woefully_underdocumented(self);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: unsafe function's docs miss `# Safety` section
++ --> $DIR/doc_unsafe.rs:57:5
++ |
++LL | / pub unsafe fn more_undocumented_unsafe() -> Self {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: unsafe function's docs miss `# Safety` section
++ --> $DIR/doc_unsafe.rs:73:9
++ |
++LL | / pub unsafe fn whee() {
++LL | | unimplemented!()
++LL | | }
++ | |_________^
++...
++LL | very_unsafe!();
++ | --------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn main() {
++ let x = 1;
++ let y = 2;
++ if x <= y {
++ // do something
++ }
++ if x <= y {
++ // do something
++ }
++ if x >= y {
++ // do something
++ }
++ if x >= y {
++ // do something
++ }
++ if x != y {
++ // do something
++ }
++ if x != y {
++ // do something
++ }
++ if x == y {
++ // do something
++ }
++ if x == y {
++ // do something
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn main() {
++ let x = 1;
++ let y = 2;
++ if x == y || x < y {
++ // do something
++ }
++ if x < y || x == y {
++ // do something
++ }
++ if x == y || x > y {
++ // do something
++ }
++ if x > y || x == y {
++ // do something
++ }
++ if x < y || x > y {
++ // do something
++ }
++ if x > y || x < y {
++ // do something
++ }
++ if x <= y && x >= y {
++ // do something
++ }
++ if x >= y && x <= y {
++ // do something
++ }
++}
--- /dev/null
--- /dev/null
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:6:8
++ |
++LL | if x == y || x < y {
++ | ^^^^^^^^^^^^^^^ help: try: `x <= y`
++ |
++ = note: `-D clippy::double-comparisons` implied by `-D warnings`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:9:8
++ |
++LL | if x < y || x == y {
++ | ^^^^^^^^^^^^^^^ help: try: `x <= y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:12:8
++ |
++LL | if x == y || x > y {
++ | ^^^^^^^^^^^^^^^ help: try: `x >= y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:15:8
++ |
++LL | if x > y || x == y {
++ | ^^^^^^^^^^^^^^^ help: try: `x >= y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:18:8
++ |
++LL | if x < y || x > y {
++ | ^^^^^^^^^^^^^^ help: try: `x != y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:21:8
++ |
++LL | if x > y || x < y {
++ | ^^^^^^^^^^^^^^ help: try: `x != y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:24:8
++ |
++LL | if x <= y && x >= y {
++ | ^^^^^^^^^^^^^^^^ help: try: `x == y`
++
++error: This binary expression can be simplified
++ --> $DIR/double_comparison.rs:27:8
++ |
++LL | if x >= y && x <= y {
++ | ^^^^^^^^^^^^^^^^ help: try: `x == y`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::double_must_use)]
++
++#[must_use]
++pub fn must_use_result() -> Result<(), ()> {
++ unimplemented!();
++}
++
++#[must_use]
++pub fn must_use_tuple() -> (Result<(), ()>, u8) {
++ unimplemented!();
++}
++
++#[must_use]
++pub fn must_use_array() -> [Result<(), ()>; 1] {
++ unimplemented!();
++}
++
++#[must_use = "With note"]
++pub fn must_use_with_note() -> Result<(), ()> {
++ unimplemented!();
++}
++
++fn main() {
++ must_use_result();
++ must_use_tuple();
++ must_use_with_note();
++}
--- /dev/null
--- /dev/null
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++ --> $DIR/double_must_use.rs:4:1
++ |
++LL | pub fn must_use_result() -> Result<(), ()> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::double-must-use` implied by `-D warnings`
++ = help: either add some descriptive text or remove the attribute
++
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++ --> $DIR/double_must_use.rs:9:1
++ |
++LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: either add some descriptive text or remove the attribute
++
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++ --> $DIR/double_must_use.rs:14:1
++ |
++LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: either add some descriptive text or remove the attribute
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::double_neg)]
++fn main() {
++ let x = 1;
++ -x;
++ -(-x);
++ --x;
++}
--- /dev/null
--- /dev/null
++error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op
++ --> $DIR/double_neg.rs:6:5
++ |
++LL | --x;
++ | ^^^
++ |
++ = note: `-D clippy::double-neg` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::double_parens)]
++#![allow(dead_code)]
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++fn dummy_fn<T>(_: T) {}
++
++struct DummyStruct;
++
++impl DummyStruct {
++ fn dummy_method<T>(self, _: T) {}
++}
++
++fn simple_double_parens() -> i32 {
++ ((0))
++}
++
++fn fn_double_parens() {
++ dummy_fn((0));
++}
++
++fn method_double_parens(x: DummyStruct) {
++ x.dummy_method((0));
++}
++
++fn tuple_double_parens() -> (i32, i32) {
++ ((1, 2))
++}
++
++fn unit_double_parens() {
++ (())
++}
++
++fn fn_tuple_ok() {
++ dummy_fn((1, 2));
++}
++
++fn method_tuple_ok(x: DummyStruct) {
++ x.dummy_method((1, 2));
++}
++
++fn fn_unit_ok() {
++ dummy_fn(());
++}
++
++fn method_unit_ok(x: DummyStruct) {
++ x.dummy_method(());
++}
++
++// Issue #3206
++fn inside_macro() {
++ assert_eq!((1, 2), (1, 2), "Error");
++ assert_eq!(((1, 2)), (1, 2), "Error");
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:15:5
++ |
++LL | ((0))
++ | ^^^^^
++ |
++ = note: `-D clippy::double-parens` implied by `-D warnings`
++
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:19:14
++ |
++LL | dummy_fn((0));
++ | ^^^
++
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:23:20
++ |
++LL | x.dummy_method((0));
++ | ^^^
++
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:27:5
++ |
++LL | ((1, 2))
++ | ^^^^^^^^
++
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:31:5
++ |
++LL | (())
++ | ^^^^
++
++error: Consider removing unnecessary double parentheses
++ --> $DIR/double_parens.rs:53:16
++ |
++LL | assert_eq!(((1, 2)), (1, 2), "Error");
++ | ^^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++fn foo<T: Drop>() {}
++fn bar<T>()
++where
++ T: Drop,
++{
++}
++fn main() {}
--- /dev/null
--- /dev/null
++error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
++ --> $DIR/drop_bounds.rs:2:11
++ |
++LL | fn foo<T: Drop>() {}
++ | ^^^^
++ |
++ = note: `#[deny(clippy::drop_bounds)]` on by default
++
++error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
++ --> $DIR/drop_bounds.rs:5:8
++ |
++LL | T: Drop,
++ | ^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::drop_copy, clippy::forget_copy)]
++#![allow(clippy::toplevel_ref_arg, clippy::drop_ref, clippy::forget_ref, unused_mut)]
++
++use std::mem::{drop, forget};
++use std::vec::Vec;
++
++#[derive(Copy, Clone)]
++struct SomeStruct {}
++
++struct AnotherStruct {
++ x: u8,
++ y: u8,
++ z: Vec<u8>,
++}
++
++impl Clone for AnotherStruct {
++ fn clone(&self) -> AnotherStruct {
++ AnotherStruct {
++ x: self.x,
++ y: self.y,
++ z: self.z.clone(),
++ }
++ }
++}
++
++fn main() {
++ let s1 = SomeStruct {};
++ let s2 = s1;
++ let s3 = &s1;
++ let mut s4 = s1;
++ let ref s5 = s1;
++
++ drop(s1);
++ drop(s2);
++ drop(s3);
++ drop(s4);
++ drop(s5);
++
++ forget(s1);
++ forget(s2);
++ forget(s3);
++ forget(s4);
++ forget(s5);
++
++ let a1 = AnotherStruct {
++ x: 255,
++ y: 0,
++ z: vec![1, 2, 3],
++ };
++ let a2 = &a1;
++ let mut a3 = a1.clone();
++ let ref a4 = a1;
++ let a5 = a1.clone();
++
++ drop(a2);
++ drop(a3);
++ drop(a4);
++ drop(a5);
++
++ forget(a2);
++ let a3 = &a1;
++ forget(a3);
++ forget(a4);
++ let a5 = a1.clone();
++ forget(a5);
++}
--- /dev/null
--- /dev/null
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:33:5
++ |
++LL | drop(s1);
++ | ^^^^^^^^
++ |
++ = note: `-D clippy::drop-copy` implied by `-D warnings`
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:33:10
++ |
++LL | drop(s1);
++ | ^^
++
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:34:5
++ |
++LL | drop(s2);
++ | ^^^^^^^^
++ |
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:34:10
++ |
++LL | drop(s2);
++ | ^^
++
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:36:5
++ |
++LL | drop(s4);
++ | ^^^^^^^^
++ |
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:36:10
++ |
++LL | drop(s4);
++ | ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:39:5
++ |
++LL | forget(s1);
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::forget-copy` implied by `-D warnings`
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:39:12
++ |
++LL | forget(s1);
++ | ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:40:5
++ |
++LL | forget(s2);
++ | ^^^^^^^^^^
++ |
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:40:12
++ |
++LL | forget(s2);
++ | ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++ --> $DIR/drop_forget_copy.rs:42:5
++ |
++LL | forget(s4);
++ | ^^^^^^^^^^
++ |
++note: argument has type SomeStruct
++ --> $DIR/drop_forget_copy.rs:42:12
++ |
++LL | forget(s4);
++ | ^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::drop_ref)]
++#![allow(clippy::toplevel_ref_arg)]
++
++use std::mem::drop;
++
++struct SomeStruct;
++
++fn main() {
++ drop(&SomeStruct);
++
++ let mut owned1 = SomeStruct;
++ drop(&owned1);
++ drop(&&owned1);
++ drop(&mut owned1);
++ drop(owned1); //OK
++
++ let reference1 = &SomeStruct;
++ drop(reference1);
++
++ let reference2 = &mut SomeStruct;
++ drop(reference2);
++
++ let ref reference3 = SomeStruct;
++ drop(reference3);
++}
++
++#[allow(dead_code)]
++fn test_generic_fn_drop<T>(val: T) {
++ drop(&val);
++ drop(val); //OK
++}
++
++#[allow(dead_code)]
++fn test_similarly_named_function() {
++ fn drop<T>(_val: T) {}
++ drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name
++ std::mem::drop(&SomeStruct);
++}
++
++#[derive(Copy, Clone)]
++pub struct Error;
++fn produce_half_owl_error() -> Result<(), Error> {
++ Ok(())
++}
++
++fn produce_half_owl_ok() -> Result<bool, ()> {
++ Ok(true)
++}
++
++#[allow(dead_code)]
++fn test_owl_result() -> Result<(), ()> {
++ produce_half_owl_error().map_err(|_| ())?;
++ produce_half_owl_ok().map(|_| ())?;
++ // the following should not be linted,
++ // we should not force users to use toilet closures
++ // to produce owl results when drop is more convenient
++ produce_half_owl_error().map_err(drop)?;
++ produce_half_owl_ok().map_err(drop)?;
++ Ok(())
++}
++
++#[allow(dead_code)]
++fn test_owl_result_2() -> Result<u8, ()> {
++ produce_half_owl_error().map_err(|_| ())?;
++ produce_half_owl_ok().map(|_| ())?;
++ // the following should not be linted,
++ // we should not force users to use toilet closures
++ // to produce owl results when drop is more convenient
++ produce_half_owl_error().map_err(drop)?;
++ produce_half_owl_ok().map(drop)?;
++ Ok(1)
++}
--- /dev/null
--- /dev/null
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:9:5
++ |
++LL | drop(&SomeStruct);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::drop-ref` implied by `-D warnings`
++note: argument has type `&SomeStruct`
++ --> $DIR/drop_ref.rs:9:10
++ |
++LL | drop(&SomeStruct);
++ | ^^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:12:5
++ |
++LL | drop(&owned1);
++ | ^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/drop_ref.rs:12:10
++ |
++LL | drop(&owned1);
++ | ^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:13:5
++ |
++LL | drop(&&owned1);
++ | ^^^^^^^^^^^^^^
++ |
++note: argument has type `&&SomeStruct`
++ --> $DIR/drop_ref.rs:13:10
++ |
++LL | drop(&&owned1);
++ | ^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:14:5
++ |
++LL | drop(&mut owned1);
++ | ^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&mut SomeStruct`
++ --> $DIR/drop_ref.rs:14:10
++ |
++LL | drop(&mut owned1);
++ | ^^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:18:5
++ |
++LL | drop(reference1);
++ | ^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/drop_ref.rs:18:10
++ |
++LL | drop(reference1);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:21:5
++ |
++LL | drop(reference2);
++ | ^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&mut SomeStruct`
++ --> $DIR/drop_ref.rs:21:10
++ |
++LL | drop(reference2);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:24:5
++ |
++LL | drop(reference3);
++ | ^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/drop_ref.rs:24:10
++ |
++LL | drop(reference3);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:29:5
++ |
++LL | drop(&val);
++ | ^^^^^^^^^^
++ |
++note: argument has type `&T`
++ --> $DIR/drop_ref.rs:29:10
++ |
++LL | drop(&val);
++ | ^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++ --> $DIR/drop_ref.rs:37:5
++ |
++LL | std::mem::drop(&SomeStruct);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/drop_ref.rs:37:20
++ |
++LL | std::mem::drop(&SomeStruct);
++ | ^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::duplicate_underscore_argument)]
++#[allow(dead_code, unused)]
++
++fn join_the_dark_side(darth: i32, _darth: i32) {}
++fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one
++
++fn main() {
++ join_the_dark_side(0, 0);
++ join_the_light_side(0, 0);
++}
--- /dev/null
--- /dev/null
++error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
++ --> $DIR/duplicate_underscore_argument.rs:4:23
++ |
++LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
++ | ^^^^^
++ |
++ = note: `-D clippy::duplicate-underscore-argument` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::duration_subsec)]
++
++use std::time::Duration;
++
++fn main() {
++ let dur = Duration::new(5, 0);
++
++ let bad_millis_1 = dur.subsec_millis();
++ let bad_millis_2 = dur.subsec_millis();
++ let good_millis = dur.subsec_millis();
++ assert_eq!(bad_millis_1, good_millis);
++ assert_eq!(bad_millis_2, good_millis);
++
++ let bad_micros = dur.subsec_micros();
++ let good_micros = dur.subsec_micros();
++ assert_eq!(bad_micros, good_micros);
++
++ // Handle refs
++ let _ = (&dur).subsec_micros();
++
++ // Handle constants
++ const NANOS_IN_MICRO: u32 = 1_000;
++ let _ = dur.subsec_micros();
++
++ // Other literals aren't linted
++ let _ = dur.subsec_nanos() / 699;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::duration_subsec)]
++
++use std::time::Duration;
++
++fn main() {
++ let dur = Duration::new(5, 0);
++
++ let bad_millis_1 = dur.subsec_micros() / 1_000;
++ let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
++ let good_millis = dur.subsec_millis();
++ assert_eq!(bad_millis_1, good_millis);
++ assert_eq!(bad_millis_2, good_millis);
++
++ let bad_micros = dur.subsec_nanos() / 1_000;
++ let good_micros = dur.subsec_micros();
++ assert_eq!(bad_micros, good_micros);
++
++ // Handle refs
++ let _ = (&dur).subsec_nanos() / 1_000;
++
++ // Handle constants
++ const NANOS_IN_MICRO: u32 = 1_000;
++ let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
++
++ // Other literals aren't linted
++ let _ = dur.subsec_nanos() / 699;
++}
--- /dev/null
--- /dev/null
++error: Calling `subsec_millis()` is more concise than this calculation
++ --> $DIR/duration_subsec.rs:10:24
++ |
++LL | let bad_millis_1 = dur.subsec_micros() / 1_000;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
++ |
++ = note: `-D clippy::duration-subsec` implied by `-D warnings`
++
++error: Calling `subsec_millis()` is more concise than this calculation
++ --> $DIR/duration_subsec.rs:11:24
++ |
++LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++ --> $DIR/duration_subsec.rs:16:22
++ |
++LL | let bad_micros = dur.subsec_nanos() / 1_000;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++ --> $DIR/duration_subsec.rs:21:13
++ |
++LL | let _ = (&dur).subsec_nanos() / 1_000;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++ --> $DIR/duration_subsec.rs:25:13
++ |
++LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![warn(clippy::else_if_without_else)]
++
++fn bla1() -> bool {
++ unimplemented!()
++}
++fn bla2() -> bool {
++ unimplemented!()
++}
++fn bla3() -> bool {
++ unimplemented!()
++}
++
++fn main() {
++ if bla1() {
++ println!("if");
++ }
++
++ if bla1() {
++ println!("if");
++ } else {
++ println!("else");
++ }
++
++ if bla1() {
++ println!("if");
++ } else if bla2() {
++ println!("else if");
++ } else {
++ println!("else")
++ }
++
++ if bla1() {
++ println!("if");
++ } else if bla2() {
++ println!("else if 1");
++ } else if bla3() {
++ println!("else if 2");
++ } else {
++ println!("else")
++ }
++
++ if bla1() {
++ println!("if");
++ } else if bla2() {
++ //~ ERROR else if without else
++ println!("else if");
++ }
++
++ if bla1() {
++ println!("if");
++ } else if bla2() {
++ println!("else if 1");
++ } else if bla3() {
++ //~ ERROR else if without else
++ println!("else if 2");
++ }
++}
--- /dev/null
--- /dev/null
++error: `if` expression with an `else if`, but without a final `else`
++ --> $DIR/else_if_without_else.rs:45:12
++ |
++LL | } else if bla2() {
++ | ____________^
++LL | | //~ ERROR else if without else
++LL | | println!("else if");
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::else-if-without-else` implied by `-D warnings`
++ = help: add an `else` block here
++
++error: `if` expression with an `else if`, but without a final `else`
++ --> $DIR/else_if_without_else.rs:54:12
++ |
++LL | } else if bla3() {
++ | ____________^
++LL | | //~ ERROR else if without else
++LL | | println!("else if 2");
++LL | | }
++ | |_____^
++ |
++ = help: add an `else` block here
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![warn(clippy::empty_enum)]
++
++enum Empty {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: enum with no variants
++ --> $DIR/empty_enum.rs:4:1
++ |
++LL | enum Empty {}
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::empty-enum` implied by `-D warnings`
++ = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::empty_line_after_outer_attr)]
++#![allow(clippy::assertions_on_constants)]
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++/// some comment
++fn with_one_newline_and_comment() { assert!(true) }
++
++// This should not produce a warning
++#[crate_type = "lib"]
++/// some comment
++fn with_no_newline_and_comment() { assert!(true) }
++
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++fn with_one_newline() { assert!(true) }
++
++// This should produce a warning, too
++#[crate_type = "lib"]
++
++
++fn with_two_newlines() { assert!(true) }
++
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++enum Baz {
++ One,
++ Two
++}
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++struct Foo {
++ one: isize,
++ two: isize
++}
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++mod foo {
++}
++
++/// This doc comment should not produce a warning
++
++/** This is also a doc comment and should not produce a warning
++ */
++
++// This should not produce a warning
++#[allow(non_camel_case_types)]
++#[allow(missing_docs)]
++#[allow(missing_docs)]
++fn three_attributes() { assert!(true) }
++
++// This should not produce a warning
++#[doc = "
++Returns the escaped value of the textual representation of
++
++"]
++pub fn function() -> bool {
++ true
++}
++
++// This should not produce a warning
++#[derive(Clone, Copy)]
++pub enum FooFighter {
++ Bar1,
++
++ Bar2,
++
++ Bar3,
++
++ Bar4
++}
++
++// This should not produce a warning because the empty line is inside a block comment
++#[crate_type = "lib"]
++/*
++
++*/
++pub struct S;
++
++// This should not produce a warning
++#[crate_type = "lib"]
++/* test */
++pub struct T;
++
++fn main() { }
--- /dev/null
--- /dev/null
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:7:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | /// some comment
++LL | | fn with_one_newline_and_comment() { assert!(true) }
++ | |_
++ |
++ = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:19:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | fn with_one_newline() { assert!(true) }
++ | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:24:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | |
++LL | | fn with_two_newlines() { assert!(true) }
++ | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:31:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | enum Baz {
++ | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:39:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | struct Foo {
++ | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++ --> $DIR/empty_line_after_outer_attribute.rs:47:1
++ |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | mod foo {
++ | |_
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:macro_rules.rs
++
++#![warn(clippy::empty_loop)]
++
++#[macro_use]
++extern crate macro_rules;
++
++fn should_trigger() {
++ loop {}
++ loop {
++ loop {}
++ }
++
++ 'outer: loop {
++ 'inner: loop {}
++ }
++}
++
++fn should_not_trigger() {
++ loop {
++ panic!("This is fine")
++ }
++ let ten_millis = std::time::Duration::from_millis(10);
++ loop {
++ std::thread::sleep(ten_millis)
++ }
++
++ #[allow(clippy::never_loop)]
++ 'outer: loop {
++ 'inner: loop {
++ break 'inner;
++ }
++ break 'outer;
++ }
++
++ // Make sure `allow` works for this lint
++ #[allow(clippy::empty_loop)]
++ loop {}
++
++ // We don't lint loops inside macros
++ macro_rules! foo {
++ () => {
++ loop {}
++ };
++ }
++
++ // We don't lint external macros
++ foofoo!()
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++ --> $DIR/empty_loop.rs:9:5
++ |
++LL | loop {}
++ | ^^^^^^^
++ |
++ = note: `-D clippy::empty-loop` implied by `-D warnings`
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++ --> $DIR/empty_loop.rs:11:9
++ |
++LL | loop {}
++ | ^^^^^^^
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++ --> $DIR/empty_loop.rs:15:9
++ |
++LL | 'inner: loop {}
++ | ^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ m.entry(k).or_insert(v);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ m.insert(k, v);
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++ --> $DIR/entry_fixable.rs:12:5
++ |
++LL | / if !m.contains_key(&k) {
++LL | | m.insert(k, v);
++LL | | }
++ | |_____^ help: consider using: `m.entry(k).or_insert(v);`
++ |
++ = note: `-D clippy::map-entry` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ m.insert(k, v)
++ } else {
++ None
++ };
++}
++
++fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ if m.contains_key(&k) {
++ None
++ } else {
++ m.insert(k, v)
++ };
++}
++
++fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ foo();
++ m.insert(k, v)
++ } else {
++ None
++ };
++}
++
++fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++ if m.contains_key(&k) {
++ None
++ } else {
++ foo();
++ m.insert(k, v)
++ };
++}
++
++fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ foo();
++ m.insert(k, v)
++ } else {
++ None
++ };
++}
++
++// should not trigger
++fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
++ if !m.contains_key(&k) {
++ m.insert(o, v);
++ }
++}
++
++// should not trigger, because the one uses different HashMap from another one
++fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ n.insert(k, v);
++ }
++}
++
++// should not trigger, because the one uses different HashMap from another one
++fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
++ if !m.contains_key(&k) {
++ n.insert(k, v);
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++ --> $DIR/entry_unfixable.rs:10:5
++ |
++LL | / if !m.contains_key(&k) {
++LL | | m.insert(k, v)
++LL | | } else {
++LL | | None
++LL | | };
++ | |_____^ consider using `m.entry(k)`
++ |
++ = note: `-D clippy::map-entry` implied by `-D warnings`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++ --> $DIR/entry_unfixable.rs:18:5
++ |
++LL | / if m.contains_key(&k) {
++LL | | None
++LL | | } else {
++LL | | m.insert(k, v)
++LL | | };
++ | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++ --> $DIR/entry_unfixable.rs:26:5
++ |
++LL | / if !m.contains_key(&k) {
++LL | | foo();
++LL | | m.insert(k, v)
++LL | | } else {
++LL | | None
++LL | | };
++ | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++ --> $DIR/entry_unfixable.rs:35:5
++ |
++LL | / if m.contains_key(&k) {
++LL | | None
++LL | | } else {
++LL | | foo();
++LL | | m.insert(k, v)
++LL | | };
++ | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `BTreeMap`
++ --> $DIR/entry_unfixable.rs:44:5
++ |
++LL | / if !m.contains_key(&k) {
++LL | | foo();
++LL | | m.insert(k, v)
++LL | | } else {
++LL | | None
++LL | | };
++ | |_____^ consider using `m.entry(k)`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-x86
++
++#![warn(clippy::enum_clike_unportable_variant)]
++#![allow(unused, non_upper_case_globals)]
++
++#[repr(usize)]
++enum NonPortable {
++ X = 0x1_0000_0000,
++ Y = 0,
++ Z = 0x7FFF_FFFF,
++ A = 0xFFFF_FFFF,
++}
++
++enum NonPortableNoHint {
++ X = 0x1_0000_0000,
++ Y = 0,
++ Z = 0x7FFF_FFFF,
++ A = 0xFFFF_FFFF,
++}
++
++#[repr(isize)]
++enum NonPortableSigned {
++ X = -1,
++ Y = 0x7FFF_FFFF,
++ Z = 0xFFFF_FFFF,
++ A = 0x1_0000_0000,
++ B = i32::MIN as isize,
++ C = (i32::MIN as isize) - 1,
++}
++
++enum NonPortableSignedNoHint {
++ X = -1,
++ Y = 0x7FFF_FFFF,
++ Z = 0xFFFF_FFFF,
++ A = 0x1_0000_0000,
++}
++
++#[repr(usize)]
++enum NonPortable2 {
++ X = <usize as Trait>::Number,
++ Y = 0,
++}
++
++trait Trait {
++ const Number: usize = 0x1_0000_0000;
++}
++
++impl Trait for usize {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:8:5
++ |
++LL | X = 0x1_0000_0000,
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:15:5
++ |
++LL | X = 0x1_0000_0000,
++ | ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:18:5
++ |
++LL | A = 0xFFFF_FFFF,
++ | ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:25:5
++ |
++LL | Z = 0xFFFF_FFFF,
++ | ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:26:5
++ |
++LL | A = 0x1_0000_0000,
++ | ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:28:5
++ |
++LL | C = (i32::MIN as isize) - 1,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:34:5
++ |
++LL | Z = 0xFFFF_FFFF,
++ | ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:35:5
++ |
++LL | A = 0x1_0000_0000,
++ | ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++ --> $DIR/enum_clike_unportable_variant.rs:40:5
++ |
++LL | X = <usize as Trait>::Number,
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::enum_glob_use)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++use std::cmp::Ordering::Less;
++
++enum Enum {
++ Foo,
++}
++
++use self::Enum::Foo;
++
++mod in_fn_test {
++ fn blarg() {
++ use crate::Enum::Foo;
++
++ let _ = Foo;
++ }
++}
++
++mod blurg {
++ pub use std::cmp::Ordering::*; // ok, re-export
++}
++
++fn main() {
++ let _ = Foo;
++ let _ = Less;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::enum_glob_use)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++use std::cmp::Ordering::*;
++
++enum Enum {
++ Foo,
++}
++
++use self::Enum::*;
++
++mod in_fn_test {
++ fn blarg() {
++ use crate::Enum::*;
++
++ let _ = Foo;
++ }
++}
++
++mod blurg {
++ pub use std::cmp::Ordering::*; // ok, re-export
++}
++
++fn main() {
++ let _ = Foo;
++ let _ = Less;
++}
--- /dev/null
--- /dev/null
++error: usage of wildcard import for enum variants
++ --> $DIR/enum_glob_use.rs:7:5
++ |
++LL | use std::cmp::Ordering::*;
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::cmp::Ordering::Less`
++ |
++ = note: `-D clippy::enum-glob-use` implied by `-D warnings`
++
++error: usage of wildcard import for enum variants
++ --> $DIR/enum_glob_use.rs:13:5
++ |
++LL | use self::Enum::*;
++ | ^^^^^^^^^^^^^ help: try: `self::Enum::Foo`
++
++error: usage of wildcard import for enum variants
++ --> $DIR/enum_glob_use.rs:17:13
++ |
++LL | use crate::Enum::*;
++ | ^^^^^^^^^^^^^^ help: try: `crate::Enum::Foo`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(non_ascii_idents)]
++#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)]
++#![allow(non_camel_case_types)]
++
++enum FakeCallType {
++ CALL,
++ CREATE,
++}
++
++enum FakeCallType2 {
++ CALL,
++ CREATELL,
++}
++
++enum Foo {
++ cFoo,
++ cBar,
++ cBaz,
++}
++
++enum Fooo {
++ cFoo, // no error, threshold is 3 variants by default
++ cBar,
++}
++
++enum Food {
++ FoodGood,
++ FoodMiddle,
++ FoodBad,
++}
++
++enum Stuff {
++ StuffBad, // no error
++}
++
++enum BadCallType {
++ CallTypeCall,
++ CallTypeCreate,
++ CallTypeDestroy,
++}
++
++enum TwoCallType {
++ // no error
++ CallTypeCall,
++ CallTypeCreate,
++}
++
++enum Consts {
++ ConstantInt,
++ ConstantCake,
++ ConstantLie,
++}
++
++enum Two {
++ // no error here
++ ConstantInt,
++ ConstantInfer,
++}
++
++enum Something {
++ CCall,
++ CCreate,
++ CCryogenize,
++}
++
++enum Seal {
++ With,
++ Without,
++}
++
++enum Seall {
++ With,
++ WithOut,
++ Withbroken,
++}
++
++enum Sealll {
++ With,
++ WithOut,
++}
++
++enum Seallll {
++ WithOutCake,
++ WithOutTea,
++ WithOut,
++}
++
++enum NonCaps {
++ Prefix的,
++ PrefixTea,
++ PrefixCake,
++}
++
++pub enum PubSeall {
++ WithOutCake,
++ WithOutTea,
++ WithOut,
++}
++
++#[allow(clippy::pub_enum_variant_names)]
++mod allowed {
++ pub enum PubAllowed {
++ SomeThis,
++ SomeThat,
++ SomeOtherWhat,
++ }
++}
++
++// should not lint
++enum Pat {
++ Foo,
++ Bar,
++ Path,
++}
++
++// should not lint
++enum N {
++ Pos,
++ Neg,
++ Float,
++}
++
++// should not lint
++enum Peek {
++ Peek1,
++ Peek2,
++ Peek3,
++}
++
++// should not lint
++pub enum NetworkLayer {
++ Layer2,
++ Layer3,
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Variant name ends with the enum's name
++ --> $DIR/enum_variants.rs:16:5
++ |
++LL | cFoo,
++ | ^^^^
++ |
++ = note: `-D clippy::enum-variant-names` implied by `-D warnings`
++
++error: Variant name starts with the enum's name
++ --> $DIR/enum_variants.rs:27:5
++ |
++LL | FoodGood,
++ | ^^^^^^^^
++
++error: Variant name starts with the enum's name
++ --> $DIR/enum_variants.rs:28:5
++ |
++LL | FoodMiddle,
++ | ^^^^^^^^^^
++
++error: Variant name starts with the enum's name
++ --> $DIR/enum_variants.rs:29:5
++ |
++LL | FoodBad,
++ | ^^^^^^^
++
++error: All variants have the same prefix: `Food`
++ --> $DIR/enum_variants.rs:26:1
++ |
++LL | / enum Food {
++LL | | FoodGood,
++LL | | FoodMiddle,
++LL | | FoodBad,
++LL | | }
++ | |_^
++ |
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `CallType`
++ --> $DIR/enum_variants.rs:36:1
++ |
++LL | / enum BadCallType {
++LL | | CallTypeCall,
++LL | | CallTypeCreate,
++LL | | CallTypeDestroy,
++LL | | }
++ | |_^
++ |
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `Constant`
++ --> $DIR/enum_variants.rs:48:1
++ |
++LL | / enum Consts {
++LL | | ConstantInt,
++LL | | ConstantCake,
++LL | | ConstantLie,
++LL | | }
++ | |_^
++ |
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `With`
++ --> $DIR/enum_variants.rs:82:1
++ |
++LL | / enum Seallll {
++LL | | WithOutCake,
++LL | | WithOutTea,
++LL | | WithOut,
++LL | | }
++ | |_^
++ |
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `Prefix`
++ --> $DIR/enum_variants.rs:88:1
++ |
++LL | / enum NonCaps {
++LL | | Prefix的,
++LL | | PrefixTea,
++LL | | PrefixCake,
++LL | | }
++ | |_^
++ |
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `With`
++ --> $DIR/enum_variants.rs:94:1
++ |
++LL | / pub enum PubSeall {
++LL | | WithOutCake,
++LL | | WithOutTea,
++LL | | WithOut,
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings`
++ = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++// does not test any rustfixable lints
++
++#[rustfmt::skip]
++#[warn(clippy::eq_op)]
++#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
++#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
++#[allow(clippy::nonminimal_bool)]
++#[allow(unused)]
++fn main() {
++ // simple values and comparisons
++ 1 == 1;
++ "no" == "no";
++ // even though I agree that no means no ;-)
++ false != false;
++ 1.5 < 1.5;
++ 1u64 >= 1u64;
++
++ // casts, methods, parentheses
++ (1 as u64) & (1 as u64);
++ 1 ^ ((((((1))))));
++
++ // unary and binary operators
++ (-(2) < -(2));
++ ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++ (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
++
++ // various other things
++ ([1] != [1]);
++ ((1, 2) != (1, 2));
++ vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
++
++ // const folding
++ 1 + 1 == 2;
++ 1 - 1 == 0;
++
++ 1 - 1;
++ 1 / 1;
++ true && true;
++
++ true || true;
++
++
++ let a: u32 = 0;
++ let b: u32 = 0;
++
++ a == b && b == a;
++ a != b && b != a;
++ a < b && b > a;
++ a <= b && b >= a;
++
++ let mut a = vec![1];
++ a == a;
++ 2*a.len() == 2*a.len(); // ok, functions
++ a.pop() == a.pop(); // ok, functions
++
++ check_ignore_macro();
++
++ // named constants
++ const A: u32 = 10;
++ const B: u32 = 10;
++ const C: u32 = A / B; // ok, different named constants
++ const D: u32 = A / A;
++}
++
++#[rustfmt::skip]
++macro_rules! check_if_named_foo {
++ ($expression:expr) => (
++ if stringify!($expression) == "foo" {
++ println!("foo!");
++ } else {
++ println!("not foo.");
++ }
++ )
++}
++
++macro_rules! bool_macro {
++ ($expression:expr) => {
++ true
++ };
++}
++
++#[allow(clippy::short_circuit_statement)]
++fn check_ignore_macro() {
++ check_if_named_foo!(foo);
++ // checks if the lint ignores macros with `!` operator
++ !bool_macro!(1) && !bool_macro!("");
++}
--- /dev/null
--- /dev/null
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:11:5
++ |
++LL | 1 == 1;
++ | ^^^^^^
++ |
++ = note: `-D clippy::eq-op` implied by `-D warnings`
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:12:5
++ |
++LL | "no" == "no";
++ | ^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++ --> $DIR/eq_op.rs:14:5
++ |
++LL | false != false;
++ | ^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `<`
++ --> $DIR/eq_op.rs:15:5
++ |
++LL | 1.5 < 1.5;
++ | ^^^^^^^^^
++
++error: equal expressions as operands to `>=`
++ --> $DIR/eq_op.rs:16:5
++ |
++LL | 1u64 >= 1u64;
++ | ^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++ --> $DIR/eq_op.rs:19:5
++ |
++LL | (1 as u64) & (1 as u64);
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `^`
++ --> $DIR/eq_op.rs:20:5
++ |
++LL | 1 ^ ((((((1))))));
++ | ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `<`
++ --> $DIR/eq_op.rs:23:5
++ |
++LL | (-(2) < -(2));
++ | ^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:24:5
++ |
++LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++ --> $DIR/eq_op.rs:24:6
++ |
++LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++ | ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++ --> $DIR/eq_op.rs:24:27
++ |
++LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++ | ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:25:5
++ |
++LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++ --> $DIR/eq_op.rs:28:5
++ |
++LL | ([1] != [1]);
++ | ^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++ --> $DIR/eq_op.rs:29:5
++ |
++LL | ((1, 2) != (1, 2));
++ | ^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:33:5
++ |
++LL | 1 + 1 == 2;
++ | ^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:34:5
++ |
++LL | 1 - 1 == 0;
++ | ^^^^^^^^^^
++
++error: equal expressions as operands to `-`
++ --> $DIR/eq_op.rs:34:5
++ |
++LL | 1 - 1 == 0;
++ | ^^^^^
++
++error: equal expressions as operands to `-`
++ --> $DIR/eq_op.rs:36:5
++ |
++LL | 1 - 1;
++ | ^^^^^
++
++error: equal expressions as operands to `/`
++ --> $DIR/eq_op.rs:37:5
++ |
++LL | 1 / 1;
++ | ^^^^^
++
++error: equal expressions as operands to `&&`
++ --> $DIR/eq_op.rs:38:5
++ |
++LL | true && true;
++ | ^^^^^^^^^^^^
++
++error: equal expressions as operands to `||`
++ --> $DIR/eq_op.rs:40:5
++ |
++LL | true || true;
++ | ^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++ --> $DIR/eq_op.rs:46:5
++ |
++LL | a == b && b == a;
++ | ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++ --> $DIR/eq_op.rs:47:5
++ |
++LL | a != b && b != a;
++ | ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++ --> $DIR/eq_op.rs:48:5
++ |
++LL | a < b && b > a;
++ | ^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++ --> $DIR/eq_op.rs:49:5
++ |
++LL | a <= b && b >= a;
++ | ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++ --> $DIR/eq_op.rs:52:5
++ |
++LL | a == a;
++ | ^^^^^^
++
++error: equal expressions as operands to `/`
++ --> $DIR/eq_op.rs:62:20
++ |
++LL | const D: u32 = A / A;
++ | ^^^^^
++
++error: aborting due to 27 previous errors
++
--- /dev/null
--- /dev/null
++#[allow(clippy::no_effect)]
++#[warn(clippy::erasing_op)]
++fn main() {
++ let x: u8 = 0;
++
++ x * 0;
++ 0 & x;
++ 0 / x;
++}
--- /dev/null
--- /dev/null
++error: this operation will always return zero. This is likely not the intended outcome
++ --> $DIR/erasing_op.rs:6:5
++ |
++LL | x * 0;
++ | ^^^^^
++ |
++ = note: `-D clippy::erasing-op` implied by `-D warnings`
++
++error: this operation will always return zero. This is likely not the intended outcome
++ --> $DIR/erasing_op.rs:7:5
++ |
++LL | 0 & x;
++ | ^^^^^
++
++error: this operation will always return zero. This is likely not the intended outcome
++ --> $DIR/erasing_op.rs:8:5
++ |
++LL | 0 / x;
++ | ^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(box_syntax)]
++#![allow(
++ clippy::borrowed_box,
++ clippy::needless_pass_by_value,
++ clippy::unused_unit,
++ clippy::redundant_clone,
++ clippy::match_single_binding
++)]
++#![warn(clippy::boxed_local)]
++
++#[derive(Clone)]
++struct A;
++
++impl A {
++ fn foo(&self) {}
++}
++
++trait Z {
++ fn bar(&self);
++}
++
++impl Z for A {
++ fn bar(&self) {
++ //nothing
++ }
++}
++
++fn main() {}
++
++fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
++ let boxed_local = boxed_trait;
++ // done
++}
++
++fn warn_call() {
++ let x = box A;
++ x.foo();
++}
++
++fn warn_arg(x: Box<A>) {
++ x.foo();
++}
++
++fn nowarn_closure_arg() {
++ let x = Some(box A);
++ x.map_or((), |x| take_ref(&x));
++}
++
++fn warn_rename_call() {
++ let x = box A;
++
++ let y = x;
++ y.foo(); // via autoderef
++}
++
++fn warn_notuse() {
++ let bz = box A;
++}
++
++fn warn_pass() {
++ let bz = box A;
++ take_ref(&bz); // via deref coercion
++}
++
++fn nowarn_return() -> Box<A> {
++ box A // moved out, "escapes"
++}
++
++fn nowarn_move() {
++ let bx = box A;
++ drop(bx) // moved in, "escapes"
++}
++fn nowarn_call() {
++ let bx = box A;
++ bx.clone(); // method only available to Box, not via autoderef
++}
++
++fn nowarn_pass() {
++ let bx = box A;
++ take_box(&bx); // fn needs &Box
++}
++
++fn take_box(x: &Box<A>) {}
++fn take_ref(x: &A) {}
++
++fn nowarn_ref_take() {
++ // false positive, should actually warn
++ let x = box A;
++ let y = &x;
++ take_box(y);
++}
++
++fn nowarn_match() {
++ let x = box A; // moved into a match
++ match x {
++ y => drop(y),
++ }
++}
++
++fn warn_match() {
++ let x = box A;
++ match &x {
++ // not moved
++ ref y => (),
++ }
++}
++
++fn nowarn_large_array() {
++ // should not warn, is large array
++ // and should not be on stack
++ let x = box [1; 10000];
++ match &x {
++ // not moved
++ ref y => (),
++ }
++}
++
++/// ICE regression test
++pub trait Foo {
++ type Item;
++}
++
++impl<'a> Foo for &'a () {
++ type Item = ();
++}
++
++pub struct PeekableSeekable<I: Foo> {
++ _peeked: I::Item,
++}
++
++pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
++
++/// Regression for #916, #1123
++///
++/// This shouldn't warn for `boxed_local`as the implementation of a trait
++/// can't change much about the trait definition.
++trait BoxedAction {
++ fn do_sth(self: Box<Self>);
++}
++
++impl BoxedAction for u64 {
++ fn do_sth(self: Box<Self>) {
++ println!("{}", *self)
++ }
++}
++
++/// Regression for #1478
++///
++/// This shouldn't warn for `boxed_local`as self itself is a box type.
++trait MyTrait {
++ fn do_sth(self);
++}
++
++impl<T> MyTrait for Box<T> {
++ fn do_sth(self) {}
++}
++
++// Issue #3739 - capture in closures
++mod issue_3739 {
++ use super::A;
++
++ fn consume<T>(_: T) {}
++ fn borrow<T>(_: &T) {}
++
++ fn closure_consume(x: Box<A>) {
++ let _ = move || {
++ consume(x);
++ };
++ }
++
++ fn closure_borrow(x: Box<A>) {
++ let _ = || {
++ borrow(&x);
++ };
++ }
++}
--- /dev/null
--- /dev/null
++error: local variable doesn't need to be boxed here
++ --> $DIR/escape_analysis.rs:40:13
++ |
++LL | fn warn_arg(x: Box<A>) {
++ | ^
++ |
++ = note: `-D clippy::boxed-local` implied by `-D warnings`
++
++error: local variable doesn't need to be boxed here
++ --> $DIR/escape_analysis.rs:131:12
++ |
++LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
++ | ^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(
++ unused,
++ clippy::no_effect,
++ clippy::redundant_closure_call,
++ clippy::many_single_char_names,
++ clippy::needless_pass_by_value,
++ clippy::option_map_unit_fn
++)]
++#![warn(
++ clippy::redundant_closure,
++ clippy::redundant_closure_for_method_calls,
++ clippy::needless_borrow
++)]
++
++use std::path::PathBuf;
++
++fn main() {
++ let a = Some(1u8).map(foo);
++ meta(foo);
++ let c = Some(1u8).map(|a| {1+2; foo}(a));
++ let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
++ all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
++ unsafe {
++ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
++ }
++
++ // See #815
++ let e = Some(1u8).map(|a| divergent(a));
++ let e = Some(1u8).map(generic);
++ let e = Some(1u8).map(generic);
++ // See #515
++ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
++ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
++}
++
++trait TestTrait {
++ fn trait_foo(self) -> bool;
++ fn trait_foo_ref(&self) -> bool;
++}
++
++struct TestStruct<'a> {
++ some_ref: &'a i32,
++}
++
++impl<'a> TestStruct<'a> {
++ fn foo(self) -> bool {
++ false
++ }
++ unsafe fn foo_unsafe(self) -> bool {
++ true
++ }
++}
++
++impl<'a> TestTrait for TestStruct<'a> {
++ fn trait_foo(self) -> bool {
++ false
++ }
++ fn trait_foo_ref(&self) -> bool {
++ false
++ }
++}
++
++impl<'a> std::ops::Deref for TestStruct<'a> {
++ type Target = char;
++ fn deref(&self) -> &char {
++ &'a'
++ }
++}
++
++fn test_redundant_closures_containing_method_calls() {
++ let i = 10;
++ let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++ let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++ let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
++ let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++ let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++ let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++ unsafe {
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
++ }
++ let e = Some("str").map(std::string::ToString::to_string);
++ let e = Some("str").map(str::to_string);
++ let e = Some('a').map(char::to_uppercase);
++ let e = Some('a').map(char::to_uppercase);
++ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
++ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++ let p = Some(PathBuf::new());
++ let e = p.as_ref().and_then(|s| s.to_str());
++ let c = Some(TestStruct { some_ref: &i })
++ .as_ref()
++ .map(|c| c.to_ascii_uppercase());
++
++ fn test_different_borrow_levels<T>(t: &[&T])
++ where
++ T: TestTrait,
++ {
++ t.iter().filter(|x| x.trait_foo_ref());
++ t.iter().map(|x| x.trait_foo_ref());
++ }
++
++ let mut some = Some(|x| x * x);
++ let arr = [Ok(1), Err(2)];
++ let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++}
++
++struct Thunk<T>(Box<dyn FnMut() -> T>);
++
++impl<T> Thunk<T> {
++ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
++ let mut option = Some(f);
++ // This should not trigger redundant_closure (#1439)
++ Thunk(Box::new(move || option.take().unwrap()()))
++ }
++
++ fn unwrap(self) -> T {
++ let Thunk(mut f) = self;
++ f()
++ }
++}
++
++fn foobar() {
++ let thunk = Thunk::new(|| println!("Hello, world!"));
++ thunk.unwrap()
++}
++
++fn meta<F>(f: F)
++where
++ F: Fn(u8),
++{
++ f(1u8)
++}
++
++fn foo(_: u8) {}
++
++fn foo2(_: u8) -> u8 {
++ 1u8
++}
++
++fn all<X, F>(x: &[X], y: &X, f: F) -> bool
++where
++ F: Fn(&X, &X) -> bool,
++{
++ x.iter().all(|e| f(e, y))
++}
++
++fn below(x: &u8, y: &u8) -> bool {
++ x < y
++}
++
++unsafe fn unsafe_fn(_: u8) {}
++
++fn divergent(_: u8) -> ! {
++ unimplemented!()
++}
++
++fn generic<T>(_: T) -> u8 {
++ 0
++}
++
++fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
++ requires_fn_once(|| x());
++}
++fn requires_fn_once<T: FnOnce()>(_: T) {}
++
++fn test_redundant_closure_with_function_pointer() {
++ type FnPtrType = fn(u8);
++ let foo_ptr: FnPtrType = foo;
++ let a = Some(1u8).map(foo_ptr);
++}
++
++fn test_redundant_closure_with_another_closure() {
++ let closure = |a| println!("{}", a);
++ let a = Some(1u8).map(closure);
++}
++
++fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
++ // Currently f is called when result of make_lazy is called.
++ // If the closure is removed, f will be called when make_lazy itself is
++ // called. This changes semantics, so the closure must stay.
++ Box::new(move |x| f()(x))
++}
++
++fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
++ f(&mut "Hello".to_owned())
++}
++fn test_difference_in_mutability() {
++ call(|s| s.clone());
++}
++
++struct Bar;
++impl std::ops::Deref for Bar {
++ type Target = str;
++ fn deref(&self) -> &str {
++ "hi"
++ }
++}
++
++fn test_deref_with_trait_method() {
++ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(
++ unused,
++ clippy::no_effect,
++ clippy::redundant_closure_call,
++ clippy::many_single_char_names,
++ clippy::needless_pass_by_value,
++ clippy::option_map_unit_fn
++)]
++#![warn(
++ clippy::redundant_closure,
++ clippy::redundant_closure_for_method_calls,
++ clippy::needless_borrow
++)]
++
++use std::path::PathBuf;
++
++fn main() {
++ let a = Some(1u8).map(|a| foo(a));
++ meta(|a| foo(a));
++ let c = Some(1u8).map(|a| {1+2; foo}(a));
++ let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
++ all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
++ unsafe {
++ Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
++ }
++
++ // See #815
++ let e = Some(1u8).map(|a| divergent(a));
++ let e = Some(1u8).map(|a| generic(a));
++ let e = Some(1u8).map(generic);
++ // See #515
++ let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
++ Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
++}
++
++trait TestTrait {
++ fn trait_foo(self) -> bool;
++ fn trait_foo_ref(&self) -> bool;
++}
++
++struct TestStruct<'a> {
++ some_ref: &'a i32,
++}
++
++impl<'a> TestStruct<'a> {
++ fn foo(self) -> bool {
++ false
++ }
++ unsafe fn foo_unsafe(self) -> bool {
++ true
++ }
++}
++
++impl<'a> TestTrait for TestStruct<'a> {
++ fn trait_foo(self) -> bool {
++ false
++ }
++ fn trait_foo_ref(&self) -> bool {
++ false
++ }
++}
++
++impl<'a> std::ops::Deref for TestStruct<'a> {
++ type Target = char;
++ fn deref(&self) -> &char {
++ &'a'
++ }
++}
++
++fn test_redundant_closures_containing_method_calls() {
++ let i = 10;
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
++ let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
++ let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++ let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
++ let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++ unsafe {
++ let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
++ }
++ let e = Some("str").map(|s| s.to_string());
++ let e = Some("str").map(str::to_string);
++ let e = Some('a').map(|s| s.to_uppercase());
++ let e = Some('a').map(char::to_uppercase);
++ let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
++ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
++ let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++ let p = Some(PathBuf::new());
++ let e = p.as_ref().and_then(|s| s.to_str());
++ let c = Some(TestStruct { some_ref: &i })
++ .as_ref()
++ .map(|c| c.to_ascii_uppercase());
++
++ fn test_different_borrow_levels<T>(t: &[&T])
++ where
++ T: TestTrait,
++ {
++ t.iter().filter(|x| x.trait_foo_ref());
++ t.iter().map(|x| x.trait_foo_ref());
++ }
++
++ let mut some = Some(|x| x * x);
++ let arr = [Ok(1), Err(2)];
++ let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++}
++
++struct Thunk<T>(Box<dyn FnMut() -> T>);
++
++impl<T> Thunk<T> {
++ fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
++ let mut option = Some(f);
++ // This should not trigger redundant_closure (#1439)
++ Thunk(Box::new(move || option.take().unwrap()()))
++ }
++
++ fn unwrap(self) -> T {
++ let Thunk(mut f) = self;
++ f()
++ }
++}
++
++fn foobar() {
++ let thunk = Thunk::new(|| println!("Hello, world!"));
++ thunk.unwrap()
++}
++
++fn meta<F>(f: F)
++where
++ F: Fn(u8),
++{
++ f(1u8)
++}
++
++fn foo(_: u8) {}
++
++fn foo2(_: u8) -> u8 {
++ 1u8
++}
++
++fn all<X, F>(x: &[X], y: &X, f: F) -> bool
++where
++ F: Fn(&X, &X) -> bool,
++{
++ x.iter().all(|e| f(e, y))
++}
++
++fn below(x: &u8, y: &u8) -> bool {
++ x < y
++}
++
++unsafe fn unsafe_fn(_: u8) {}
++
++fn divergent(_: u8) -> ! {
++ unimplemented!()
++}
++
++fn generic<T>(_: T) -> u8 {
++ 0
++}
++
++fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
++ requires_fn_once(|| x());
++}
++fn requires_fn_once<T: FnOnce()>(_: T) {}
++
++fn test_redundant_closure_with_function_pointer() {
++ type FnPtrType = fn(u8);
++ let foo_ptr: FnPtrType = foo;
++ let a = Some(1u8).map(|a| foo_ptr(a));
++}
++
++fn test_redundant_closure_with_another_closure() {
++ let closure = |a| println!("{}", a);
++ let a = Some(1u8).map(|a| closure(a));
++}
++
++fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
++ // Currently f is called when result of make_lazy is called.
++ // If the closure is removed, f will be called when make_lazy itself is
++ // called. This changes semantics, so the closure must stay.
++ Box::new(move |x| f()(x))
++}
++
++fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
++ f(&mut "Hello".to_owned())
++}
++fn test_difference_in_mutability() {
++ call(|s| s.clone());
++}
++
++struct Bar;
++impl std::ops::Deref for Bar {
++ type Target = str;
++ fn deref(&self) -> &str {
++ "hi"
++ }
++}
++
++fn test_deref_with_trait_method() {
++ let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
++}
--- /dev/null
--- /dev/null
++error: redundant closure found
++ --> $DIR/eta.rs:20:27
++ |
++LL | let a = Some(1u8).map(|a| foo(a));
++ | ^^^^^^^^^^ help: remove closure as shown: `foo`
++ |
++ = note: `-D clippy::redundant-closure` implied by `-D warnings`
++
++error: redundant closure found
++ --> $DIR/eta.rs:21:10
++ |
++LL | meta(|a| foo(a));
++ | ^^^^^^^^^^ help: remove closure as shown: `foo`
++
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++ --> $DIR/eta.rs:24:21
++ |
++LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
++ | ^^^ help: change this to: `&2`
++ |
++ = note: `-D clippy::needless-borrow` implied by `-D warnings`
++
++error: redundant closure found
++ --> $DIR/eta.rs:31:27
++ |
++LL | let e = Some(1u8).map(|a| generic(a));
++ | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic`
++
++error: redundant closure found
++ --> $DIR/eta.rs:74:51
++ |
++LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
++ | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo`
++ |
++ = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
++
++error: redundant closure found
++ --> $DIR/eta.rs:76:51
++ |
++LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
++ | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo`
++
++error: redundant closure found
++ --> $DIR/eta.rs:79:42
++ |
++LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
++ | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear`
++
++error: redundant closure found
++ --> $DIR/eta.rs:84:29
++ |
++LL | let e = Some("str").map(|s| s.to_string());
++ | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string`
++
++error: redundant closure found
++ --> $DIR/eta.rs:86:27
++ |
++LL | let e = Some('a').map(|s| s.to_uppercase());
++ | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase`
++
++error: redundant closure found
++ --> $DIR/eta.rs:89:65
++ |
++LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase`
++
++error: redundant closure found
++ --> $DIR/eta.rs:172:27
++ |
++LL | let a = Some(1u8).map(|a| foo_ptr(a));
++ | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr`
++
++error: redundant closure found
++ --> $DIR/eta.rs:177:27
++ |
++LL | let a = Some(1u8).map(|a| closure(a));
++ | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::eval_order_dependence)]
++#[allow(
++ unused_assignments,
++ unused_variables,
++ clippy::many_single_char_names,
++ clippy::no_effect,
++ dead_code,
++ clippy::blacklisted_name
++)]
++fn main() {
++ let mut x = 0;
++ let a = {
++ x = 1;
++ 1
++ } + x;
++
++ // Example from iss#277
++ x += {
++ x = 20;
++ 2
++ };
++
++ // Does it work in weird places?
++ // ...in the base for a struct expression?
++ struct Foo {
++ a: i32,
++ b: i32,
++ };
++ let base = Foo { a: 4, b: 5 };
++ let foo = Foo {
++ a: x,
++ ..{
++ x = 6;
++ base
++ }
++ };
++ // ...inside a closure?
++ let closure = || {
++ let mut x = 0;
++ x += {
++ x = 20;
++ 2
++ };
++ };
++ // ...not across a closure?
++ let mut y = 0;
++ let b = (y, || y = 1);
++
++ // && and || evaluate left-to-right.
++ let a = {
++ x = 1;
++ true
++ } && (x == 3);
++ let a = {
++ x = 1;
++ true
++ } || (x == 3);
++
++ // Make sure we don't get confused by alpha conversion.
++ let a = {
++ let mut x = 1;
++ x = 2;
++ 1
++ } + x;
++
++ // No warning if we don't read the variable...
++ x = {
++ x = 20;
++ 2
++ };
++ // ...if the assignment is in a closure...
++ let b = {
++ || {
++ x = 1;
++ };
++ 1
++ } + x;
++ // ... or the access is under an address.
++ let b = (
++ {
++ let p = &x;
++ 1
++ },
++ {
++ x = 1;
++ x
++ },
++ );
++
++ // Limitation: l-values other than simple variables don't trigger
++ // the warning.
++ let mut tup = (0, 0);
++ let c = {
++ tup.0 = 1;
++ 1
++ } + tup.0;
++ // Limitation: you can get away with a read under address-of.
++ let mut z = 0;
++ let b = (
++ &{
++ z = x;
++ x
++ },
++ {
++ x = 3;
++ x
++ },
++ );
++}
--- /dev/null
--- /dev/null
++error: unsequenced read of a variable
++ --> $DIR/eval_order_dependence.rs:15:9
++ |
++LL | } + x;
++ | ^
++ |
++ = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
++note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:13:9
++ |
++LL | x = 1;
++ | ^^^^^
++
++error: unsequenced read of a variable
++ --> $DIR/eval_order_dependence.rs:18:5
++ |
++LL | x += {
++ | ^
++ |
++note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:19:9
++ |
++LL | x = 20;
++ | ^^^^^^
++
++error: unsequenced read of a variable
++ --> $DIR/eval_order_dependence.rs:31:12
++ |
++LL | a: x,
++ | ^
++ |
++note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:33:13
++ |
++LL | x = 6;
++ | ^^^^^
++
++error: unsequenced read of a variable
++ --> $DIR/eval_order_dependence.rs:40:9
++ |
++LL | x += {
++ | ^
++ |
++note: whether read occurs before this write depends on evaluation order
++ --> $DIR/eval_order_dependence.rs:41:13
++ |
++LL | x = 20;
++ | ^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::excessive_precision)]
++#![allow(dead_code, unused_variables, clippy::print_literal)]
++
++fn main() {
++ // Consts
++ const GOOD32: f32 = 0.123_456;
++ const GOOD32_SM: f32 = 0.000_000_000_1;
++ const GOOD32_DOT: f32 = 10_000_000_000.0;
++ const GOOD32_EDGE: f32 = 1.000_000_8;
++ const GOOD64: f64 = 0.123_456_789_012;
++ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
++ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
++
++ const BAD32_1: f32 = 0.123_456_79_f32;
++ const BAD32_2: f32 = 0.123_456_79;
++ const BAD32_3: f32 = 0.1;
++ const BAD32_EDGE: f32 = 1.000_001;
++
++ const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
++ const BAD64_2: f64 = 0.123_456_789_012_345_66;
++ const BAD64_3: f64 = 0.1;
++
++ // Literal as param
++ println!("{:?}", 8.888_888_888_888_89);
++
++ // // TODO add inferred type tests for f32
++ // Locals
++ let good32: f32 = 0.123_456_f32;
++ let good32_2: f32 = 0.123_456;
++
++ let good64: f64 = 0.123_456_789_012;
++ let good64_suf: f64 = 0.123_456_789_012f64;
++ let good64_inf = 0.123_456_789_012;
++
++ let bad32: f32 = 1.123_456_8;
++ let bad32_suf: f32 = 1.123_456_8_f32;
++ let bad32_inf = 1.123_456_8_f32;
++
++ let bad64: f64 = 0.123_456_789_012_345_66;
++ let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
++ let bad64_inf = 0.123_456_789_012_345_66;
++
++ // Vectors
++ let good_vec32: Vec<f32> = vec![0.123_456];
++ let good_vec64: Vec<f64> = vec![0.123_456_789];
++
++ let bad_vec32: Vec<f32> = vec![0.123_456_79];
++ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
++
++ // Exponential float notation
++ let good_e32: f32 = 1e-10;
++ let bad_e32: f32 = 1.123_456_8e-10;
++
++ let good_bige32: f32 = 1E-10;
++ let bad_bige32: f32 = 1.123_456_8E-10;
++
++ // Inferred type
++ let good_inferred: f32 = 1f32 * 1_000_000_000.;
++
++ // issue #2840
++ let num = 0.000_000_000_01e-10f64;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::excessive_precision)]
++#![allow(dead_code, unused_variables, clippy::print_literal)]
++
++fn main() {
++ // Consts
++ const GOOD32: f32 = 0.123_456;
++ const GOOD32_SM: f32 = 0.000_000_000_1;
++ const GOOD32_DOT: f32 = 10_000_000_000.0;
++ const GOOD32_EDGE: f32 = 1.000_000_8;
++ const GOOD64: f64 = 0.123_456_789_012;
++ const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
++ const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
++
++ const BAD32_1: f32 = 0.123_456_789_f32;
++ const BAD32_2: f32 = 0.123_456_789;
++ const BAD32_3: f32 = 0.100_000_000_000_1;
++ const BAD32_EDGE: f32 = 1.000_000_9;
++
++ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
++ const BAD64_2: f64 = 0.123_456_789_012_345_67;
++ const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
++
++ // Literal as param
++ println!("{:?}", 8.888_888_888_888_888_888_888);
++
++ // // TODO add inferred type tests for f32
++ // Locals
++ let good32: f32 = 0.123_456_f32;
++ let good32_2: f32 = 0.123_456;
++
++ let good64: f64 = 0.123_456_789_012;
++ let good64_suf: f64 = 0.123_456_789_012f64;
++ let good64_inf = 0.123_456_789_012;
++
++ let bad32: f32 = 1.123_456_789;
++ let bad32_suf: f32 = 1.123_456_789_f32;
++ let bad32_inf = 1.123_456_789_f32;
++
++ let bad64: f64 = 0.123_456_789_012_345_67;
++ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
++ let bad64_inf = 0.123_456_789_012_345_67;
++
++ // Vectors
++ let good_vec32: Vec<f32> = vec![0.123_456];
++ let good_vec64: Vec<f64> = vec![0.123_456_789];
++
++ let bad_vec32: Vec<f32> = vec![0.123_456_789];
++ let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
++
++ // Exponential float notation
++ let good_e32: f32 = 1e-10;
++ let bad_e32: f32 = 1.123_456_788_888e-10;
++
++ let good_bige32: f32 = 1E-10;
++ let bad_bige32: f32 = 1.123_456_788_888E-10;
++
++ // Inferred type
++ let good_inferred: f32 = 1f32 * 1_000_000_000.;
++
++ // issue #2840
++ let num = 0.000_000_000_01e-10f64;
++}
--- /dev/null
--- /dev/null
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:15:26
++ |
++LL | const BAD32_1: f32 = 0.123_456_789_f32;
++ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
++ |
++ = note: `-D clippy::excessive-precision` implied by `-D warnings`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:16:26
++ |
++LL | const BAD32_2: f32 = 0.123_456_789;
++ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:17:26
++ |
++LL | const BAD32_3: f32 = 0.100_000_000_000_1;
++ | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:18:29
++ |
++LL | const BAD32_EDGE: f32 = 1.000_000_9;
++ | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:20:26
++ |
++LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:21:26
++ |
++LL | const BAD64_2: f64 = 0.123_456_789_012_345_67;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:22:26
++ |
++LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:25:22
++ |
++LL | println!("{:?}", 8.888_888_888_888_888_888_888);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:36:22
++ |
++LL | let bad32: f32 = 1.123_456_789;
++ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:37:26
++ |
++LL | let bad32_suf: f32 = 1.123_456_789_f32;
++ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:38:21
++ |
++LL | let bad32_inf = 1.123_456_789_f32;
++ | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:40:22
++ |
++LL | let bad64: f64 = 0.123_456_789_012_345_67;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:41:26
++ |
++LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:42:21
++ |
++LL | let bad64_inf = 0.123_456_789_012_345_67;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:48:36
++ |
++LL | let bad_vec32: Vec<f32> = vec![0.123_456_789];
++ | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:49:36
++ |
++LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:53:24
++ |
++LL | let bad_e32: f32 = 1.123_456_788_888e-10;
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
++
++error: float has excessive precision
++ --> $DIR/excessive_precision.rs:56:27
++ |
++LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::exit)]
++
++fn not_main() {
++ if true {
++ std::process::exit(4);
++ }
++}
++
++fn main() {
++ if true {
++ std::process::exit(2);
++ };
++ not_main();
++ std::process::exit(1);
++}
--- /dev/null
--- /dev/null
++error: usage of `process::exit`
++ --> $DIR/exit1.rs:5:9
++ |
++LL | std::process::exit(4);
++ | ^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::exit` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#[warn(clippy::exit)]
++
++fn also_not_main() {
++ std::process::exit(3);
++}
++
++fn main() {
++ if true {
++ std::process::exit(2);
++ };
++ also_not_main();
++ std::process::exit(1);
++}
--- /dev/null
--- /dev/null
++error: usage of `process::exit`
++ --> $DIR/exit2.rs:4:5
++ |
++LL | std::process::exit(3);
++ | ^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::exit` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#[warn(clippy::exit)]
++
++fn main() {
++ if true {
++ std::process::exit(2);
++ };
++ std::process::exit(1);
++}
--- /dev/null
--- /dev/null
++#![warn(clippy::option_expect_used, clippy::result_expect_used)]
++
++fn expect_option() {
++ let opt = Some(0);
++ let _ = opt.expect("");
++}
++
++fn expect_result() {
++ let res: Result<u8, ()> = Ok(0);
++ let _ = res.expect("");
++}
++
++fn main() {
++ expect_option();
++ expect_result();
++}
--- /dev/null
--- /dev/null
++error: used `expect()` on `an Option` value
++ --> $DIR/expect.rs:5:13
++ |
++LL | let _ = opt.expect("");
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::option-expect-used` implied by `-D warnings`
++ = help: if this value is an `None`, it will panic
++
++error: used `expect()` on `a Result` value
++ --> $DIR/expect.rs:10:13
++ |
++LL | let _ = res.expect("");
++ | ^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::result-expect-used` implied by `-D warnings`
++ = help: if this value is an `Err`, it will panic
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::expect_fun_call)]
++
++/// Checks implementation of the `EXPECT_FUN_CALL` lint
++
++fn main() {
++ struct Foo;
++
++ impl Foo {
++ fn new() -> Self {
++ Foo
++ }
++
++ fn expect(&self, msg: &str) {
++ panic!("{}", msg)
++ }
++ }
++
++ let with_some = Some("value");
++ with_some.expect("error");
++
++ let with_none: Option<i32> = None;
++ with_none.expect("error");
++
++ let error_code = 123_i32;
++ let with_none_and_format: Option<i32> = None;
++ with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
++
++ let with_none_and_as_str: Option<i32> = None;
++ with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
++
++ let with_ok: Result<(), ()> = Ok(());
++ with_ok.expect("error");
++
++ let with_err: Result<(), ()> = Err(());
++ with_err.expect("error");
++
++ let error_code = 123_i32;
++ let with_err_and_format: Result<(), ()> = Err(());
++ with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
++
++ let with_err_and_as_str: Result<(), ()> = Err(());
++ with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
++
++ let with_dummy_type = Foo::new();
++ with_dummy_type.expect("another test string");
++
++ let with_dummy_type_and_format = Foo::new();
++ with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
++
++ let with_dummy_type_and_as_str = Foo::new();
++ with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++ //Issue #2937
++ Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2));
++
++ //Issue #2979 - this should not lint
++ {
++ let msg = "bar";
++ Some("foo").expect(msg);
++ }
++
++ {
++ fn get_string() -> String {
++ "foo".to_string()
++ }
++
++ fn get_static_str() -> &'static str {
++ "foo"
++ }
++
++ fn get_non_static_str(_: &u32) -> &str {
++ "foo"
++ }
++
++ Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++ Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++ Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++
++ Some("foo").unwrap_or_else(|| { panic!(get_static_str()) });
++ Some("foo").unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) });
++ }
++
++ //Issue #3839
++ Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2));
++
++ //Issue #4912 - the receiver is a &Option
++ {
++ let opt = Some(1);
++ let opt_ref = &opt;
++ opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref));
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::expect_fun_call)]
++
++/// Checks implementation of the `EXPECT_FUN_CALL` lint
++
++fn main() {
++ struct Foo;
++
++ impl Foo {
++ fn new() -> Self {
++ Foo
++ }
++
++ fn expect(&self, msg: &str) {
++ panic!("{}", msg)
++ }
++ }
++
++ let with_some = Some("value");
++ with_some.expect("error");
++
++ let with_none: Option<i32> = None;
++ with_none.expect("error");
++
++ let error_code = 123_i32;
++ let with_none_and_format: Option<i32> = None;
++ with_none_and_format.expect(&format!("Error {}: fake error", error_code));
++
++ let with_none_and_as_str: Option<i32> = None;
++ with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++ let with_ok: Result<(), ()> = Ok(());
++ with_ok.expect("error");
++
++ let with_err: Result<(), ()> = Err(());
++ with_err.expect("error");
++
++ let error_code = 123_i32;
++ let with_err_and_format: Result<(), ()> = Err(());
++ with_err_and_format.expect(&format!("Error {}: fake error", error_code));
++
++ let with_err_and_as_str: Result<(), ()> = Err(());
++ with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++ let with_dummy_type = Foo::new();
++ with_dummy_type.expect("another test string");
++
++ let with_dummy_type_and_format = Foo::new();
++ with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
++
++ let with_dummy_type_and_as_str = Foo::new();
++ with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++ //Issue #2937
++ Some("foo").expect(format!("{} {}", 1, 2).as_ref());
++
++ //Issue #2979 - this should not lint
++ {
++ let msg = "bar";
++ Some("foo").expect(msg);
++ }
++
++ {
++ fn get_string() -> String {
++ "foo".to_string()
++ }
++
++ fn get_static_str() -> &'static str {
++ "foo"
++ }
++
++ fn get_non_static_str(_: &u32) -> &str {
++ "foo"
++ }
++
++ Some("foo").expect(&get_string());
++ Some("foo").expect(get_string().as_ref());
++ Some("foo").expect(get_string().as_str());
++
++ Some("foo").expect(get_static_str());
++ Some("foo").expect(get_non_static_str(&0));
++ }
++
++ //Issue #3839
++ Some(true).expect(&format!("key {}, {}", 1, 2));
++
++ //Issue #4912 - the receiver is a &Option
++ {
++ let opt = Some(1);
++ let opt_ref = &opt;
++ opt_ref.expect(&format!("{:?}", opt_ref));
++ }
++}
--- /dev/null
--- /dev/null
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:28:26
++ |
++LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
++ |
++ = note: `-D clippy::expect-fun-call` implied by `-D warnings`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:31:26
++ |
++LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:41:25
++ |
++LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:44:25
++ |
++LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:56:17
++ |
++LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:77:21
++ |
++LL | Some("foo").expect(&get_string());
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:78:21
++ |
++LL | Some("foo").expect(get_string().as_ref());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:79:21
++ |
++LL | Some("foo").expect(get_string().as_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:81:21
++ |
++LL | Some("foo").expect(get_static_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_static_str()) })`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:82:21
++ |
++LL | Some("foo").expect(get_non_static_str(&0));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) })`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:86:16
++ |
++LL | Some(true).expect(&format!("key {}, {}", 1, 2));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
++
++error: use of `expect` followed by a function call
++ --> $DIR/expect_fun_call.rs:92:17
++ |
++LL | opt_ref.expect(&format!("{:?}", opt_ref));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::explicit_counter_loop)]
++
++fn main() {
++ let mut vec = vec![1, 2, 3, 4];
++ let mut _index = 0;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 1;
++ _index = 0;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &mut vec {
++ _index += 1;
++ }
++
++ let mut _index = 0;
++ for _v in vec {
++ _index += 1;
++ }
++}
++
++mod issue_1219 {
++ pub fn test() {
++ // should not trigger the lint because variable is used after the loop #473
++ let vec = vec![1, 2, 3];
++ let mut index = 0;
++ for _v in &vec {
++ index += 1
++ }
++ println!("index: {}", index);
++
++ // should not trigger the lint because the count is conditional #1219
++ let text = "banana";
++ let mut count = 0;
++ for ch in text.chars() {
++ if ch == 'a' {
++ continue;
++ }
++ count += 1;
++ println!("{}", count);
++ }
++
++ // should not trigger the lint because the count is conditional
++ let text = "banana";
++ let mut count = 0;
++ for ch in text.chars() {
++ if ch == 'a' {
++ count += 1;
++ }
++ println!("{}", count);
++ }
++
++ // should trigger the lint because the count is not conditional
++ let text = "banana";
++ let mut count = 0;
++ for ch in text.chars() {
++ count += 1;
++ if ch == 'a' {
++ continue;
++ }
++ println!("{}", count);
++ }
++
++ // should trigger the lint because the count is not conditional
++ let text = "banana";
++ let mut count = 0;
++ for ch in text.chars() {
++ count += 1;
++ for i in 0..2 {
++ let _ = 123;
++ }
++ println!("{}", count);
++ }
++
++ // should not trigger the lint because the count is incremented multiple times
++ let text = "banana";
++ let mut count = 0;
++ for ch in text.chars() {
++ count += 1;
++ for i in 0..2 {
++ count += 1;
++ }
++ println!("{}", count);
++ }
++ }
++}
++
++mod issue_3308 {
++ pub fn test() {
++ // should not trigger the lint because the count is incremented multiple times
++ let mut skips = 0;
++ let erasures = vec![];
++ for i in 0..10 {
++ while erasures.contains(&(i + skips)) {
++ skips += 1;
++ }
++ println!("{}", skips);
++ }
++
++ // should not trigger the lint because the count is incremented multiple times
++ let mut skips = 0;
++ for i in 0..10 {
++ let mut j = 0;
++ while j < 5 {
++ skips += 1;
++ j += 1;
++ }
++ println!("{}", skips);
++ }
++
++ // should not trigger the lint because the count is incremented multiple times
++ let mut skips = 0;
++ for i in 0..10 {
++ for j in 0..5 {
++ skips += 1;
++ }
++ println!("{}", skips);
++ }
++ }
++}
++
++mod issue_1670 {
++ pub fn test() {
++ let mut count = 0;
++ for _i in 3..10 {
++ count += 1;
++ }
++ }
++}
++
++mod issue_4732 {
++ pub fn test() {
++ let slice = &[1, 2, 3];
++ let mut index = 0;
++
++ // should not trigger the lint because the count is used after the loop
++ for _v in slice {
++ index += 1
++ }
++ let _closure = || println!("index: {}", index);
++ }
++}
--- /dev/null
--- /dev/null
++error: the variable `_index` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:6:5
++ |
++LL | for _v in &vec {
++ | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
++ |
++ = note: `-D clippy::explicit-counter-loop` implied by `-D warnings`
++
++error: the variable `_index` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:12:5
++ |
++LL | for _v in &vec {
++ | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
++
++error: the variable `_index` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:17:5
++ |
++LL | for _v in &mut vec {
++ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()`
++
++error: the variable `_index` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:22:5
++ |
++LL | for _v in vec {
++ | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:61:9
++ |
++LL | for ch in text.chars() {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:72:9
++ |
++LL | for ch in text.chars() {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++ --> $DIR/explicit_counter_loop.rs:130:9
++ |
++LL | for _i in 3..10 {
++ | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(clippy::explicit_write)]
++
++fn stdout() -> String {
++ String::new()
++}
++
++fn stderr() -> String {
++ String::new()
++}
++
++fn main() {
++ // these should warn
++ {
++ use std::io::Write;
++ print!("test");
++ eprint!("test");
++ println!("test");
++ eprintln!("test");
++ print!("test");
++ eprint!("test");
++
++ // including newlines
++ println!("test\ntest");
++ eprintln!("test\ntest");
++ }
++ // these should not warn, different destination
++ {
++ use std::fmt::Write;
++ let mut s = String::new();
++ write!(s, "test").unwrap();
++ write!(s, "test").unwrap();
++ writeln!(s, "test").unwrap();
++ writeln!(s, "test").unwrap();
++ s.write_fmt(format_args!("test")).unwrap();
++ s.write_fmt(format_args!("test")).unwrap();
++ write!(stdout(), "test").unwrap();
++ write!(stderr(), "test").unwrap();
++ writeln!(stdout(), "test").unwrap();
++ writeln!(stderr(), "test").unwrap();
++ stdout().write_fmt(format_args!("test")).unwrap();
++ stderr().write_fmt(format_args!("test")).unwrap();
++ }
++ // these should not warn, no unwrap
++ {
++ use std::io::Write;
++ std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
++ std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(clippy::explicit_write)]
++
++fn stdout() -> String {
++ String::new()
++}
++
++fn stderr() -> String {
++ String::new()
++}
++
++fn main() {
++ // these should warn
++ {
++ use std::io::Write;
++ write!(std::io::stdout(), "test").unwrap();
++ write!(std::io::stderr(), "test").unwrap();
++ writeln!(std::io::stdout(), "test").unwrap();
++ writeln!(std::io::stderr(), "test").unwrap();
++ std::io::stdout().write_fmt(format_args!("test")).unwrap();
++ std::io::stderr().write_fmt(format_args!("test")).unwrap();
++
++ // including newlines
++ writeln!(std::io::stdout(), "test\ntest").unwrap();
++ writeln!(std::io::stderr(), "test\ntest").unwrap();
++ }
++ // these should not warn, different destination
++ {
++ use std::fmt::Write;
++ let mut s = String::new();
++ write!(s, "test").unwrap();
++ write!(s, "test").unwrap();
++ writeln!(s, "test").unwrap();
++ writeln!(s, "test").unwrap();
++ s.write_fmt(format_args!("test")).unwrap();
++ s.write_fmt(format_args!("test")).unwrap();
++ write!(stdout(), "test").unwrap();
++ write!(stderr(), "test").unwrap();
++ writeln!(stdout(), "test").unwrap();
++ writeln!(stderr(), "test").unwrap();
++ stdout().write_fmt(format_args!("test")).unwrap();
++ stderr().write_fmt(format_args!("test")).unwrap();
++ }
++ // these should not warn, no unwrap
++ {
++ use std::io::Write;
++ std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
++ std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
++ }
++}
--- /dev/null
--- /dev/null
++error: use of `write!(stdout(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:17:9
++ |
++LL | write!(std::io::stdout(), "test").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
++ |
++ = note: `-D clippy::explicit-write` implied by `-D warnings`
++
++error: use of `write!(stderr(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:18:9
++ |
++LL | write!(std::io::stderr(), "test").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
++
++error: use of `writeln!(stdout(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:19:9
++ |
++LL | writeln!(std::io::stdout(), "test").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
++
++error: use of `writeln!(stderr(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:20:9
++ |
++LL | writeln!(std::io::stderr(), "test").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
++
++error: use of `stdout().write_fmt(...).unwrap()`
++ --> $DIR/explicit_write.rs:21:9
++ |
++LL | std::io::stdout().write_fmt(format_args!("test")).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
++
++error: use of `stderr().write_fmt(...).unwrap()`
++ --> $DIR/explicit_write.rs:22:9
++ |
++LL | std::io::stderr().write_fmt(format_args!("test")).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
++
++error: use of `writeln!(stdout(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:25:9
++ |
++LL | writeln!(std::io::stdout(), "test/ntest").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
++
++error: use of `writeln!(stderr(), ...).unwrap()`
++ --> $DIR/explicit_write.rs:26:9
++ |
++LL | writeln!(std::io::stderr(), "test/ntest").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused_imports, clippy::blacklisted_name)]
++#![warn(clippy::explicit_write)]
++
++fn main() {
++ use std::io::Write;
++ let bar = "bar";
++ writeln!(std::io::stderr(), "foo {}", bar).unwrap();
++}
--- /dev/null
--- /dev/null
++error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
++ --> $DIR/explicit_write_non_rustfix.rs:7:5
++ |
++LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::explicit-write` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)]
++#![warn(clippy::extra_unused_lifetimes)]
++
++fn empty() {}
++
++fn used_lt<'a>(x: &'a u8) {}
++
++fn unused_lt<'a>(x: u8) {}
++
++fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
++ // 'a is useless here since it's not directly bound
++}
++
++fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 {
++ panic!()
++}
++
++fn lt_return_only<'a>() -> &'a u8 {
++ panic!()
++}
++
++fn unused_lt_blergh<'a>(x: Option<Box<dyn Send + 'a>>) {}
++
++trait Foo<'a> {
++ fn x(&self, a: &'a u8);
++}
++
++impl<'a> Foo<'a> for u8 {
++ fn x(&self, a: &'a u8) {}
++}
++
++struct Bar;
++
++impl Bar {
++ fn x<'a>(&self) {}
++}
++
++// test for #489 (used lifetimes in bounds)
++pub fn parse<'a, I: Iterator<Item = &'a str>>(_it: &mut I) {
++ unimplemented!()
++}
++pub fn parse2<'a, I>(_it: &mut I)
++where
++ I: Iterator<Item = &'a str>,
++{
++ unimplemented!()
++}
++
++struct X {
++ x: u32,
++}
++
++impl X {
++ fn self_ref_with_lifetime<'a>(&'a self) {}
++ fn explicit_self_with_lifetime<'a>(self: &'a Self) {}
++}
++
++// Methods implementing traits must have matching lifetimes
++mod issue4291 {
++ trait BadTrait {
++ fn unused_lt<'a>(x: u8) {}
++ }
++
++ impl BadTrait for () {
++ fn unused_lt<'a>(_x: u8) {}
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this lifetime isn't used in the function definition
++ --> $DIR/extra_unused_lifetimes.rs:8:14
++ |
++LL | fn unused_lt<'a>(x: u8) {}
++ | ^^
++ |
++ = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
++
++error: this lifetime isn't used in the function definition
++ --> $DIR/extra_unused_lifetimes.rs:10:25
++ |
++LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
++ | ^^
++
++error: this lifetime isn't used in the function definition
++ --> $DIR/extra_unused_lifetimes.rs:35:10
++ |
++LL | fn x<'a>(&self) {}
++ | ^^
++
++error: this lifetime isn't used in the function definition
++ --> $DIR/extra_unused_lifetimes.rs:61:22
++ |
++LL | fn unused_lt<'a>(x: u8) {}
++ | ^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::fallible_impl_from)]
++
++// docs example
++struct Foo(i32);
++impl From<String> for Foo {
++ fn from(s: String) -> Self {
++ Foo(s.parse().unwrap())
++ }
++}
++
++struct Valid(Vec<u8>);
++
++impl<'a> From<&'a str> for Valid {
++ fn from(s: &'a str) -> Valid {
++ Valid(s.to_owned().into_bytes())
++ }
++}
++impl From<usize> for Valid {
++ fn from(i: usize) -> Valid {
++ Valid(Vec::with_capacity(i))
++ }
++}
++
++struct Invalid;
++
++impl From<usize> for Invalid {
++ fn from(i: usize) -> Invalid {
++ if i != 42 {
++ panic!();
++ }
++ Invalid
++ }
++}
++
++impl From<Option<String>> for Invalid {
++ fn from(s: Option<String>) -> Invalid {
++ let s = s.unwrap();
++ if !s.is_empty() {
++ panic!(42);
++ } else if s.parse::<u32>().unwrap() != 42 {
++ panic!("{:?}", s);
++ }
++ Invalid
++ }
++}
++
++trait ProjStrTrait {
++ type ProjString;
++}
++impl<T> ProjStrTrait for Box<T> {
++ type ProjString = String;
++}
++impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
++ fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
++ if s.parse::<u32>().ok().unwrap() != 42 {
++ panic!("{:?}", s);
++ }
++ Invalid
++ }
++}
++
++struct Unreachable;
++
++impl From<String> for Unreachable {
++ fn from(s: String) -> Unreachable {
++ if s.is_empty() {
++ return Unreachable;
++ }
++ match s.chars().next() {
++ Some(_) => Unreachable,
++ None => unreachable!(), // do not lint the unreachable macro
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: consider implementing `TryFrom` instead
++ --> $DIR/fallible_impl_from.rs:5:1
++ |
++LL | / impl From<String> for Foo {
++LL | | fn from(s: String) -> Self {
++LL | | Foo(s.parse().unwrap())
++LL | | }
++LL | | }
++ | |_^
++ |
++note: the lint level is defined here
++ --> $DIR/fallible_impl_from.rs:1:9
++ |
++LL | #![deny(clippy::fallible_impl_from)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++ --> $DIR/fallible_impl_from.rs:7:13
++ |
++LL | Foo(s.parse().unwrap())
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider implementing `TryFrom` instead
++ --> $DIR/fallible_impl_from.rs:26:1
++ |
++LL | / impl From<usize> for Invalid {
++LL | | fn from(i: usize) -> Invalid {
++LL | | if i != 42 {
++LL | | panic!();
++... |
++LL | | }
++LL | | }
++ | |_^
++ |
++ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++ --> $DIR/fallible_impl_from.rs:29:13
++ |
++LL | panic!();
++ | ^^^^^^^^^
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: consider implementing `TryFrom` instead
++ --> $DIR/fallible_impl_from.rs:35:1
++ |
++LL | / impl From<Option<String>> for Invalid {
++LL | | fn from(s: Option<String>) -> Invalid {
++LL | | let s = s.unwrap();
++LL | | if !s.is_empty() {
++... |
++LL | | }
++LL | | }
++ | |_^
++ |
++ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++ --> $DIR/fallible_impl_from.rs:37:17
++ |
++LL | let s = s.unwrap();
++ | ^^^^^^^^^^
++LL | if !s.is_empty() {
++LL | panic!(42);
++ | ^^^^^^^^^^^
++LL | } else if s.parse::<u32>().unwrap() != 42 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | panic!("{:?}", s);
++ | ^^^^^^^^^^^^^^^^^^
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: consider implementing `TryFrom` instead
++ --> $DIR/fallible_impl_from.rs:53:1
++ |
++LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
++LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
++LL | | if s.parse::<u32>().ok().unwrap() != 42 {
++LL | | panic!("{:?}", s);
++... |
++LL | | }
++LL | | }
++ | |_^
++ |
++ = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++ --> $DIR/fallible_impl_from.rs:55:12
++ |
++LL | if s.parse::<u32>().ok().unwrap() != 42 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | panic!("{:?}", s);
++ | ^^^^^^^^^^^^^^^^^^
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::filetype_is_file)]
++
++fn main() -> std::io::Result<()> {
++ use std::fs;
++ use std::ops::BitOr;
++
++ // !filetype.is_dir()
++ if fs::metadata("foo.txt")?.file_type().is_file() {
++ // read file
++ }
++
++ // positive of filetype.is_dir()
++ if !fs::metadata("foo.txt")?.file_type().is_file() {
++ // handle dir
++ }
++
++ // false positive of filetype.is_dir()
++ if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
++ // ...
++ }
++
++ Ok(())
++}
--- /dev/null
--- /dev/null
++error: `FileType::is_file()` only covers regular files
++ --> $DIR/filetype_is_file.rs:8:8
++ |
++LL | if fs::metadata("foo.txt")?.file_type().is_file() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::filetype-is-file` implied by `-D warnings`
++ = help: use `!FileType::is_dir()` instead
++
++error: `!FileType::is_file()` only denies regular files
++ --> $DIR/filetype_is_file.rs:13:8
++ |
++LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: use `FileType::is_dir()` instead
++
++error: `FileType::is_file()` only covers regular files
++ --> $DIR/filetype_is_file.rs:18:9
++ |
++LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: use `!FileType::is_dir()` instead
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all, clippy::pedantic)]
++
++fn main() {
++ let a = ["1", "lol", "3", "NaN", "5"];
++
++ let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++ assert_eq!(element, Some(1));
++
++ #[rustfmt::skip]
++ let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
++ .into_iter()
++ .filter_map(|x| {
++ if x == 2 {
++ Some(x * 2)
++ } else {
++ None
++ }
++ })
++ .next();
++}
--- /dev/null
--- /dev/null
++error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
++ --> $DIR/filter_map_next.rs:6:32
++ |
++LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::filter-map-next` implied by `-D warnings`
++ = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())`
++
++error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
++ --> $DIR/filter_map_next.rs:10:26
++ |
++LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
++ | __________________________^
++LL | | .into_iter()
++LL | | .filter_map(|x| {
++LL | | if x == 2 {
++... |
++LL | | })
++LL | | .next();
++ | |_______________^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++ let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
++
++ let _: Vec<_> = vec![5_i8; 6]
++ .into_iter()
++ .filter(|&x| x == 0)
++ .flat_map(|x| x.checked_mul(2))
++ .collect();
++
++ let _: Vec<_> = vec![5_i8; 6]
++ .into_iter()
++ .filter_map(|x| x.checked_mul(2))
++ .flat_map(|x| x.checked_mul(2))
++ .collect();
++
++ let _: Vec<_> = vec![5_i8; 6]
++ .into_iter()
++ .filter_map(|x| x.checked_mul(2))
++ .map(|x| x.checked_mul(2))
++ .collect();
++}
--- /dev/null
--- /dev/null
++error: called `filter(p).map(q)` on an `Iterator`
++ --> $DIR/filter_methods.rs:5:21
++ |
++LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::filter-map` implied by `-D warnings`
++ = help: this is more succinctly expressed by calling `.filter_map(..)` instead
++
++error: called `filter(p).flat_map(q)` on an `Iterator`
++ --> $DIR/filter_methods.rs:7:21
++ |
++LL | let _: Vec<_> = vec![5_i8; 6]
++ | _____________________^
++LL | | .into_iter()
++LL | | .filter(|&x| x == 0)
++LL | | .flat_map(|x| x.checked_mul(2))
++ | |_______________________________________^
++ |
++ = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
++
++error: called `filter_map(p).flat_map(q)` on an `Iterator`
++ --> $DIR/filter_methods.rs:13:21
++ |
++LL | let _: Vec<_> = vec![5_i8; 6]
++ | _____________________^
++LL | | .into_iter()
++LL | | .filter_map(|x| x.checked_mul(2))
++LL | | .flat_map(|x| x.checked_mul(2))
++ | |_______________________________________^
++ |
++ = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
++
++error: called `filter_map(p).map(q)` on an `Iterator`
++ --> $DIR/filter_methods.rs:19:21
++ |
++LL | let _: Vec<_> = vec![5_i8; 6]
++ | _____________________^
++LL | | .into_iter()
++LL | | .filter_map(|x| x.checked_mul(2))
++LL | | .map(|x| x.checked_mul(2))
++ | |__________________________________^
++ |
++ = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all, clippy::pedantic)]
++
++#[derive(Debug, Copy, Clone)]
++enum Flavor {
++ Chocolate,
++}
++
++#[derive(Debug, Copy, Clone)]
++enum Dessert {
++ Banana,
++ Pudding,
++ Cake(Flavor),
++}
++
++fn main() {
++ let desserts_of_the_week = vec![Dessert::Banana, Dessert::Cake(Flavor::Chocolate), Dessert::Pudding];
++
++ let a = ["lol", "NaN", "2", "5", "Xunda"];
++
++ let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
++
++ let _: Option<Flavor> = desserts_of_the_week
++ .iter()
++ .find(|dessert| match *dessert {
++ Dessert::Cake(_) => true,
++ _ => false,
++ })
++ .map(|dessert| match *dessert {
++ Dessert::Cake(ref flavor) => *flavor,
++ _ => unreachable!(),
++ });
++}
--- /dev/null
--- /dev/null
++error: called `find(p).map(q)` on an `Iterator`
++ --> $DIR/find_map.rs:20:26
++ |
++LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::find-map` implied by `-D warnings`
++ = help: this is more succinctly expressed by calling `.find_map(..)` instead
++
++error: called `find(p).map(q)` on an `Iterator`
++ --> $DIR/find_map.rs:22:29
++ |
++LL | let _: Option<Flavor> = desserts_of_the_week
++ | _____________________________^
++LL | | .iter()
++LL | | .find(|dessert| match *dessert {
++LL | | Dessert::Cake(_) => true,
++... |
++LL | | _ => unreachable!(),
++LL | | });
++ | |__________^
++ |
++ = help: this is more succinctly expressed by calling `.find_map(..)` instead
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
++#![allow(
++ unused,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::op_ref
++)]
++
++#[rustfmt::skip]
++fn main() {
++ let mut f = 1.0f32;
++
++ f * 2.0;
++
++ 1.0 + f;
++ f * 2.0;
++ f / 2.0;
++ f - 2.0 * 4.2;
++ -f;
++
++ f += 1.0;
++ f -= 1.0;
++ f *= 2.0;
++ f /= 2.0;
++}
++
++// also warn about floating point arith with references involved
++
++pub fn float_arith_ref() {
++ 3.1_f32 + &1.2_f32;
++ &3.4_f32 + 1.5_f32;
++ &3.5_f32 + &1.3_f32;
++}
++
++pub fn float_foo(f: &f32) -> f32 {
++ let a = 5.1;
++ a + f
++}
++
++pub fn float_bar(f1: &f32, f2: &f32) -> f32 {
++ f1 + f2
++}
++
++pub fn float_baz(f1: f32, f2: &f32) -> f32 {
++ f1 + f2
++}
++
++pub fn float_qux(f1: f32, f2: f32) -> f32 {
++ (&f1 + &f2)
++}
--- /dev/null
--- /dev/null
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:15:5
++ |
++LL | f * 2.0;
++ | ^^^^^^^
++ |
++ = note: `-D clippy::float-arithmetic` implied by `-D warnings`
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:17:5
++ |
++LL | 1.0 + f;
++ | ^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:18:5
++ |
++LL | f * 2.0;
++ | ^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:19:5
++ |
++LL | f / 2.0;
++ | ^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:20:5
++ |
++LL | f - 2.0 * 4.2;
++ | ^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:21:5
++ |
++LL | -f;
++ | ^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:23:5
++ |
++LL | f += 1.0;
++ | ^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:24:5
++ |
++LL | f -= 1.0;
++ | ^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:25:5
++ |
++LL | f *= 2.0;
++ | ^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:26:5
++ |
++LL | f /= 2.0;
++ | ^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:32:5
++ |
++LL | 3.1_f32 + &1.2_f32;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:33:5
++ |
++LL | &3.4_f32 + 1.5_f32;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:34:5
++ |
++LL | &3.5_f32 + &1.3_f32;
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:39:5
++ |
++LL | a + f
++ | ^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:43:5
++ |
++LL | f1 + f2
++ | ^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:47:5
++ |
++LL | f1 + f2
++ | ^^^^^^^
++
++error: floating-point arithmetic detected
++ --> $DIR/float_arithmetic.rs:51:5
++ |
++LL | (&f1 + &f2)
++ | ^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::float_cmp)]
++#![allow(
++ unused,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::cast_lossless,
++ clippy::many_single_char_names
++)]
++
++use std::ops::Add;
++
++const ZERO: f32 = 0.0;
++const ONE: f32 = ZERO + 1.0;
++
++fn twice<T>(x: T) -> T
++where
++ T: Add<T, Output = T> + Copy,
++{
++ x + x
++}
++
++fn eq_fl(x: f32, y: f32) -> bool {
++ if x.is_nan() {
++ y.is_nan()
++ } else {
++ x == y
++ } // no error, inside "eq" fn
++}
++
++fn fl_eq(x: f32, y: f32) -> bool {
++ if x.is_nan() {
++ y.is_nan()
++ } else {
++ x == y
++ } // no error, inside "eq" fn
++}
++
++struct X {
++ val: f32,
++}
++
++impl PartialEq for X {
++ fn eq(&self, o: &X) -> bool {
++ if self.val.is_nan() {
++ o.val.is_nan()
++ } else {
++ self.val == o.val // no error, inside "eq" fn
++ }
++ }
++}
++
++fn main() {
++ ZERO == 0f32; //no error, comparison with zero is ok
++ 1.0f32 != f32::INFINITY; // also comparison with infinity
++ 1.0f32 != f32::NEG_INFINITY; // and negative infinity
++ ZERO == 0.0; //no error, comparison with zero is ok
++ ZERO + ZERO != 1.0; //no error, comparison with zero is ok
++
++ ONE == 1f32;
++ ONE == 1.0 + 0.0;
++ ONE + ONE == ZERO + ONE + ONE;
++ ONE != 2.0;
++ ONE != 0.0; // no error, comparison with zero is ok
++ twice(ONE) != ONE;
++ ONE as f64 != 2.0;
++ ONE as f64 != 0.0; // no error, comparison with zero is ok
++
++ let x: f64 = 1.0;
++
++ x == 1.0;
++ x != 0f64; // no error, comparison with zero is ok
++
++ twice(x) != twice(ONE as f64);
++
++ x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
++ x > 0.0;
++ x <= 0.0;
++ x >= 0.0;
++
++ let xs: [f32; 1] = [0.0];
++ let a: *const f32 = xs.as_ptr();
++ let b: *const f32 = xs.as_ptr();
++
++ assert_eq!(a, b); // no errors
++
++ const ZERO_ARRAY: [f32; 2] = [0.0, 0.0];
++ const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1];
++
++ let i = 0;
++ let j = 1;
++
++ ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i
++ NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
++
++ let a1: [f32; 1] = [0.0];
++ let a2: [f32; 1] = [1.1];
++
++ a1 == a2;
++ a1[0] == a2[0];
++
++ // no errors - comparing signums is ok
++ let x32 = 3.21f32;
++ 1.23f32.signum() == x32.signum();
++ 1.23f32.signum() == -(x32.signum());
++ 1.23f32.signum() == 3.21f32.signum();
++
++ 1.23f32.signum() != x32.signum();
++ 1.23f32.signum() != -(x32.signum());
++ 1.23f32.signum() != 3.21f32.signum();
++
++ let x64 = 3.21f64;
++ 1.23f64.signum() == x64.signum();
++ 1.23f64.signum() == -(x64.signum());
++ 1.23f64.signum() == 3.21f64.signum();
++
++ 1.23f64.signum() != x64.signum();
++ 1.23f64.signum() != -(x64.signum());
++ 1.23f64.signum() != 3.21f64.signum();
++}
--- /dev/null
--- /dev/null
++error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:65:5
++ |
++LL | ONE as f64 != 2.0;
++ | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error`
++ |
++ = note: `-D clippy::float-cmp` implied by `-D warnings`
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:70:5
++ |
++LL | x == 1.0;
++ | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:73:5
++ |
++LL | twice(x) != twice(ONE as f64);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:93:5
++ |
++LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` arrays
++ --> $DIR/float_cmp.rs:98:5
++ |
++LL | a1 == a2;
++ | ^^^^^^^^
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++ --> $DIR/float_cmp.rs:99:5
++ |
++LL | a1[0] == a2[0];
++ | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// does not test any rustfixable lints
++
++#![warn(clippy::float_cmp_const)]
++#![allow(clippy::float_cmp)]
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
++
++const ONE: f32 = 1.0;
++const TWO: f32 = 2.0;
++
++fn eq_one(x: f32) -> bool {
++ if x.is_nan() {
++ false
++ } else {
++ x == ONE
++ } // no error, inside "eq" fn
++}
++
++fn main() {
++ // has errors
++ 1f32 == ONE;
++ TWO == ONE;
++ TWO != ONE;
++ ONE + ONE == TWO;
++ let x = 1;
++ x as f32 == ONE;
++
++ let v = 0.9;
++ v == ONE;
++ v != ONE;
++
++ // no errors, lower than or greater than comparisons
++ v < ONE;
++ v > ONE;
++ v <= ONE;
++ v >= ONE;
++
++ // no errors, zero and infinity values
++ ONE != 0f32;
++ TWO == 0f32;
++ ONE != f32::INFINITY;
++ ONE == f32::NEG_INFINITY;
++
++ // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed
++ let w = 1.1;
++ v == w;
++ v != w;
++ v == 1.0;
++ v != 1.0;
++
++ const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0];
++ const ZERO_INF_ARRAY: [f32; 3] = [0.0, ::std::f32::INFINITY, ::std::f32::NEG_INFINITY];
++ const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2];
++ const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0];
++
++ // no errors, zero and infinity values
++ NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0
++ ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros
++ ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities
++
++ // has errors
++ NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
++}
--- /dev/null
--- /dev/null
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:20:5
++ |
++LL | 1f32 == ONE;
++ | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error`
++ |
++ = note: `-D clippy::float-cmp-const` implied by `-D warnings`
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:21:5
++ |
++LL | TWO == ONE;
++ | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:22:5
++ |
++LL | TWO != ONE;
++ | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:23:5
++ |
++LL | ONE + ONE == TWO;
++ | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:25:5
++ |
++LL | x as f32 == ONE;
++ | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:28:5
++ |
++LL | v == ONE;
++ | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++ --> $DIR/float_cmp_const.rs:29:5
++ |
++LL | v != ONE;
++ | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error`
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant arrays
++ --> $DIR/float_cmp_const.rs:61:5
++ |
++LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++struct A {
++ a: f64,
++ b: f64,
++}
++
++fn fake_abs1(num: f64) -> f64 {
++ num.abs()
++}
++
++fn fake_abs2(num: f64) -> f64 {
++ num.abs()
++}
++
++fn fake_abs3(a: A) -> f64 {
++ a.a.abs()
++}
++
++fn fake_abs4(num: f64) -> f64 {
++ num.abs()
++}
++
++fn fake_abs5(a: A) -> f64 {
++ a.a.abs()
++}
++
++fn fake_nabs1(num: f64) -> f64 {
++ -num.abs()
++}
++
++fn fake_nabs2(num: f64) -> f64 {
++ -num.abs()
++}
++
++fn fake_nabs3(a: A) -> A {
++ A {
++ a: -a.a.abs(),
++ b: a.b,
++ }
++}
++
++fn not_fake_abs1(num: f64) -> f64 {
++ if num > 0.0 {
++ num
++ } else {
++ -num - 1f64
++ }
++}
++
++fn not_fake_abs2(num: f64) -> f64 {
++ if num > 0.0 {
++ num + 1.0
++ } else {
++ -(num + 1.0)
++ }
++}
++
++fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
++ if num1 > 0.0 {
++ num2
++ } else {
++ -num2
++ }
++}
++
++fn not_fake_abs4(a: A) -> f64 {
++ if a.a > 0.0 {
++ a.b
++ } else {
++ -a.b
++ }
++}
++
++fn not_fake_abs5(a: A) -> f64 {
++ if a.a > 0.0 {
++ a.a
++ } else {
++ -a.b
++ }
++}
++
++fn main() {
++ fake_abs1(5.0);
++ fake_abs2(5.0);
++ fake_abs3(A { a: 5.0, b: 5.0 });
++ fake_abs4(5.0);
++ fake_abs5(A { a: 5.0, b: 5.0 });
++ fake_nabs1(5.0);
++ fake_nabs2(5.0);
++ fake_nabs3(A { a: 5.0, b: 5.0 });
++ not_fake_abs1(5.0);
++ not_fake_abs2(5.0);
++ not_fake_abs3(5.0, 5.0);
++ not_fake_abs4(A { a: 5.0, b: 5.0 });
++ not_fake_abs5(A { a: 5.0, b: 5.0 });
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++struct A {
++ a: f64,
++ b: f64,
++}
++
++fn fake_abs1(num: f64) -> f64 {
++ if num >= 0.0 {
++ num
++ } else {
++ -num
++ }
++}
++
++fn fake_abs2(num: f64) -> f64 {
++ if 0.0 < num {
++ num
++ } else {
++ -num
++ }
++}
++
++fn fake_abs3(a: A) -> f64 {
++ if a.a > 0.0 {
++ a.a
++ } else {
++ -a.a
++ }
++}
++
++fn fake_abs4(num: f64) -> f64 {
++ if 0.0 >= num {
++ -num
++ } else {
++ num
++ }
++}
++
++fn fake_abs5(a: A) -> f64 {
++ if a.a < 0.0 {
++ -a.a
++ } else {
++ a.a
++ }
++}
++
++fn fake_nabs1(num: f64) -> f64 {
++ if num < 0.0 {
++ num
++ } else {
++ -num
++ }
++}
++
++fn fake_nabs2(num: f64) -> f64 {
++ if 0.0 >= num {
++ num
++ } else {
++ -num
++ }
++}
++
++fn fake_nabs3(a: A) -> A {
++ A {
++ a: if a.a >= 0.0 { -a.a } else { a.a },
++ b: a.b,
++ }
++}
++
++fn not_fake_abs1(num: f64) -> f64 {
++ if num > 0.0 {
++ num
++ } else {
++ -num - 1f64
++ }
++}
++
++fn not_fake_abs2(num: f64) -> f64 {
++ if num > 0.0 {
++ num + 1.0
++ } else {
++ -(num + 1.0)
++ }
++}
++
++fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
++ if num1 > 0.0 {
++ num2
++ } else {
++ -num2
++ }
++}
++
++fn not_fake_abs4(a: A) -> f64 {
++ if a.a > 0.0 {
++ a.b
++ } else {
++ -a.b
++ }
++}
++
++fn not_fake_abs5(a: A) -> f64 {
++ if a.a > 0.0 {
++ a.a
++ } else {
++ -a.b
++ }
++}
++
++fn main() {
++ fake_abs1(5.0);
++ fake_abs2(5.0);
++ fake_abs3(A { a: 5.0, b: 5.0 });
++ fake_abs4(5.0);
++ fake_abs5(A { a: 5.0, b: 5.0 });
++ fake_nabs1(5.0);
++ fake_nabs2(5.0);
++ fake_nabs3(A { a: 5.0, b: 5.0 });
++ not_fake_abs1(5.0);
++ not_fake_abs2(5.0);
++ not_fake_abs3(5.0, 5.0);
++ not_fake_abs4(A { a: 5.0, b: 5.0 });
++ not_fake_abs5(A { a: 5.0, b: 5.0 });
++}
--- /dev/null
--- /dev/null
++error: manual implementation of `abs` method
++ --> $DIR/floating_point_abs.rs:10:5
++ |
++LL | / if num >= 0.0 {
++LL | | num
++LL | | } else {
++LL | | -num
++LL | | }
++ | |_____^ help: try: `num.abs()`
++ |
++ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: manual implementation of `abs` method
++ --> $DIR/floating_point_abs.rs:18:5
++ |
++LL | / if 0.0 < num {
++LL | | num
++LL | | } else {
++LL | | -num
++LL | | }
++ | |_____^ help: try: `num.abs()`
++
++error: manual implementation of `abs` method
++ --> $DIR/floating_point_abs.rs:26:5
++ |
++LL | / if a.a > 0.0 {
++LL | | a.a
++LL | | } else {
++LL | | -a.a
++LL | | }
++ | |_____^ help: try: `a.a.abs()`
++
++error: manual implementation of `abs` method
++ --> $DIR/floating_point_abs.rs:34:5
++ |
++LL | / if 0.0 >= num {
++LL | | -num
++LL | | } else {
++LL | | num
++LL | | }
++ | |_____^ help: try: `num.abs()`
++
++error: manual implementation of `abs` method
++ --> $DIR/floating_point_abs.rs:42:5
++ |
++LL | / if a.a < 0.0 {
++LL | | -a.a
++LL | | } else {
++LL | | a.a
++LL | | }
++ | |_____^ help: try: `a.a.abs()`
++
++error: manual implementation of negation of `abs` method
++ --> $DIR/floating_point_abs.rs:50:5
++ |
++LL | / if num < 0.0 {
++LL | | num
++LL | | } else {
++LL | | -num
++LL | | }
++ | |_____^ help: try: `-num.abs()`
++
++error: manual implementation of negation of `abs` method
++ --> $DIR/floating_point_abs.rs:58:5
++ |
++LL | / if 0.0 >= num {
++LL | | num
++LL | | } else {
++LL | | -num
++LL | | }
++ | |_____^ help: try: `-num.abs()`
++
++error: manual implementation of negation of `abs` method
++ --> $DIR/floating_point_abs.rs:67:12
++ |
++LL | a: if a.a >= 0.0 { -a.a } else { a.a },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::imprecise_flops)]
++
++fn main() {
++ let x = 2f32;
++ let _ = x.exp_m1();
++ let _ = x.exp_m1() + 2.0;
++ // Cases where the lint shouldn't be applied
++ let _ = x.exp() - 2.0;
++ let _ = x.exp() - 1.0 * 2.0;
++
++ let x = 2f64;
++ let _ = x.exp_m1();
++ let _ = x.exp_m1() + 2.0;
++ // Cases where the lint shouldn't be applied
++ let _ = x.exp() - 2.0;
++ let _ = x.exp() - 1.0 * 2.0;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::imprecise_flops)]
++
++fn main() {
++ let x = 2f32;
++ let _ = x.exp() - 1.0;
++ let _ = x.exp() - 1.0 + 2.0;
++ // Cases where the lint shouldn't be applied
++ let _ = x.exp() - 2.0;
++ let _ = x.exp() - 1.0 * 2.0;
++
++ let x = 2f64;
++ let _ = x.exp() - 1.0;
++ let _ = x.exp() - 1.0 + 2.0;
++ // Cases where the lint shouldn't be applied
++ let _ = x.exp() - 2.0;
++ let _ = x.exp() - 1.0 * 2.0;
++}
--- /dev/null
--- /dev/null
++error: (e.pow(x) - 1) can be computed more accurately
++ --> $DIR/floating_point_exp.rs:6:13
++ |
++LL | let _ = x.exp() - 1.0;
++ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++ |
++ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: (e.pow(x) - 1) can be computed more accurately
++ --> $DIR/floating_point_exp.rs:7:13
++ |
++LL | let _ = x.exp() - 1.0 + 2.0;
++ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: (e.pow(x) - 1) can be computed more accurately
++ --> $DIR/floating_point_exp.rs:13:13
++ |
++LL | let _ = x.exp() - 1.0;
++ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: (e.pow(x) - 1) can be computed more accurately
++ --> $DIR/floating_point_exp.rs:14:13
++ |
++LL | let _ = x.exp() - 1.0 + 2.0;
++ | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code, clippy::double_parens)]
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++const TWO: f32 = 2.0;
++const E: f32 = std::f32::consts::E;
++
++fn check_log_base() {
++ let x = 1f32;
++ let _ = x.log2();
++ let _ = x.log10();
++ let _ = x.ln();
++ let _ = x.log2();
++ let _ = x.ln();
++
++ let x = 1f64;
++ let _ = x.log2();
++ let _ = x.log10();
++ let _ = x.ln();
++}
++
++fn check_ln1p() {
++ let x = 1f32;
++ let _ = 2.0f32.ln_1p();
++ let _ = 2.0f32.ln_1p();
++ let _ = x.ln_1p();
++ let _ = (x / 2.0).ln_1p();
++ let _ = x.powi(2).ln_1p();
++ let _ = (x.powi(2) / 2.0).ln_1p();
++ let _ = ((std::f32::consts::E - 1.0)).ln_1p();
++ let _ = x.ln_1p();
++ let _ = x.powi(2).ln_1p();
++ let _ = (x + 2.0).ln_1p();
++ let _ = (x / 2.0).ln_1p();
++ // Cases where the lint shouldn't be applied
++ let _ = (1.0 + x + 2.0).ln();
++ let _ = (x + 1.0 + 2.0).ln();
++ let _ = (x + 1.0 / 2.0).ln();
++ let _ = (1.0 + x - 2.0).ln();
++
++ let x = 1f64;
++ let _ = 2.0f64.ln_1p();
++ let _ = 2.0f64.ln_1p();
++ let _ = x.ln_1p();
++ let _ = (x / 2.0).ln_1p();
++ let _ = x.powi(2).ln_1p();
++ let _ = x.ln_1p();
++ let _ = x.powi(2).ln_1p();
++ let _ = (x + 2.0).ln_1p();
++ let _ = (x / 2.0).ln_1p();
++ // Cases where the lint shouldn't be applied
++ let _ = (1.0 + x + 2.0).ln();
++ let _ = (x + 1.0 + 2.0).ln();
++ let _ = (x + 1.0 / 2.0).ln();
++ let _ = (1.0 + x - 2.0).ln();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code, clippy::double_parens)]
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++const TWO: f32 = 2.0;
++const E: f32 = std::f32::consts::E;
++
++fn check_log_base() {
++ let x = 1f32;
++ let _ = x.log(2f32);
++ let _ = x.log(10f32);
++ let _ = x.log(std::f32::consts::E);
++ let _ = x.log(TWO);
++ let _ = x.log(E);
++
++ let x = 1f64;
++ let _ = x.log(2f64);
++ let _ = x.log(10f64);
++ let _ = x.log(std::f64::consts::E);
++}
++
++fn check_ln1p() {
++ let x = 1f32;
++ let _ = (1f32 + 2.).ln();
++ let _ = (1f32 + 2.0).ln();
++ let _ = (1.0 + x).ln();
++ let _ = (1.0 + x / 2.0).ln();
++ let _ = (1.0 + x.powi(2)).ln();
++ let _ = (1.0 + x.powi(2) / 2.0).ln();
++ let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
++ let _ = (x + 1.0).ln();
++ let _ = (x.powi(2) + 1.0).ln();
++ let _ = (x + 2.0 + 1.0).ln();
++ let _ = (x / 2.0 + 1.0).ln();
++ // Cases where the lint shouldn't be applied
++ let _ = (1.0 + x + 2.0).ln();
++ let _ = (x + 1.0 + 2.0).ln();
++ let _ = (x + 1.0 / 2.0).ln();
++ let _ = (1.0 + x - 2.0).ln();
++
++ let x = 1f64;
++ let _ = (1f64 + 2.).ln();
++ let _ = (1f64 + 2.0).ln();
++ let _ = (1.0 + x).ln();
++ let _ = (1.0 + x / 2.0).ln();
++ let _ = (1.0 + x.powi(2)).ln();
++ let _ = (x + 1.0).ln();
++ let _ = (x.powi(2) + 1.0).ln();
++ let _ = (x + 2.0 + 1.0).ln();
++ let _ = (x / 2.0 + 1.0).ln();
++ // Cases where the lint shouldn't be applied
++ let _ = (1.0 + x + 2.0).ln();
++ let _ = (x + 1.0 + 2.0).ln();
++ let _ = (x + 1.0 / 2.0).ln();
++ let _ = (1.0 + x - 2.0).ln();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:10:13
++ |
++LL | let _ = x.log(2f32);
++ | ^^^^^^^^^^^ help: consider using: `x.log2()`
++ |
++ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:11:13
++ |
++LL | let _ = x.log(10f32);
++ | ^^^^^^^^^^^^ help: consider using: `x.log10()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:12:13
++ |
++LL | let _ = x.log(std::f32::consts::E);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:13:13
++ |
++LL | let _ = x.log(TWO);
++ | ^^^^^^^^^^ help: consider using: `x.log2()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:14:13
++ |
++LL | let _ = x.log(E);
++ | ^^^^^^^^ help: consider using: `x.ln()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:17:13
++ |
++LL | let _ = x.log(2f64);
++ | ^^^^^^^^^^^ help: consider using: `x.log2()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:18:13
++ |
++LL | let _ = x.log(10f64);
++ | ^^^^^^^^^^^^ help: consider using: `x.log10()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++ --> $DIR/floating_point_log.rs:19:13
++ |
++LL | let _ = x.log(std::f64::consts::E);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:24:13
++ |
++LL | let _ = (1f32 + 2.).ln();
++ | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
++ |
++ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:25:13
++ |
++LL | let _ = (1f32 + 2.0).ln();
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:26:13
++ |
++LL | let _ = (1.0 + x).ln();
++ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:27:13
++ |
++LL | let _ = (1.0 + x / 2.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:28:13
++ |
++LL | let _ = (1.0 + x.powi(2)).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:29:13
++ |
++LL | let _ = (1.0 + x.powi(2) / 2.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:30:13
++ |
++LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:31:13
++ |
++LL | let _ = (x + 1.0).ln();
++ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:32:13
++ |
++LL | let _ = (x.powi(2) + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:33:13
++ |
++LL | let _ = (x + 2.0 + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:34:13
++ |
++LL | let _ = (x / 2.0 + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:42:13
++ |
++LL | let _ = (1f64 + 2.).ln();
++ | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:43:13
++ |
++LL | let _ = (1f64 + 2.0).ln();
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:44:13
++ |
++LL | let _ = (1.0 + x).ln();
++ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:45:13
++ |
++LL | let _ = (1.0 + x / 2.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:46:13
++ |
++LL | let _ = (1.0 + x.powi(2)).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:47:13
++ |
++LL | let _ = (x + 1.0).ln();
++ | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:48:13
++ |
++LL | let _ = (x.powi(2) + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:49:13
++ |
++LL | let _ = (x + 2.0 + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++ --> $DIR/floating_point_log.rs:50:13
++ |
++LL | let _ = (x / 2.0 + 1.0).ln();
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: aborting due to 28 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++fn main() {
++ let a: f64 = 1234.567;
++ let b: f64 = 45.67834;
++ let c: f64 = 0.0004;
++ let d: f64 = 0.0001;
++
++ let _ = a.mul_add(b, c);
++ let _ = a.mul_add(b, c);
++ let _ = 2.0f64.mul_add(4.0, a);
++ let _ = 2.0f64.mul_add(4., a);
++
++ let _ = a.mul_add(b, c);
++ let _ = a.mul_add(b, c);
++ let _ = (a * b).mul_add(c, d);
++
++ let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
++ let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++fn main() {
++ let a: f64 = 1234.567;
++ let b: f64 = 45.67834;
++ let c: f64 = 0.0004;
++ let d: f64 = 0.0001;
++
++ let _ = a * b + c;
++ let _ = c + a * b;
++ let _ = a + 2.0 * 4.0;
++ let _ = a + 2. * 4.;
++
++ let _ = (a * b) + c;
++ let _ = c + (a * b);
++ let _ = a * b * c + d;
++
++ let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
++ let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
++}
--- /dev/null
--- /dev/null
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:10:13
++ |
++LL | let _ = a * b + c;
++ | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++ |
++ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:11:13
++ |
++LL | let _ = c + a * b;
++ | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:12:13
++ |
++LL | let _ = a + 2.0 * 4.0;
++ | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:13:13
++ |
++LL | let _ = a + 2. * 4.;
++ | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:15:13
++ |
++LL | let _ = (a * b) + c;
++ | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:16:13
++ |
++LL | let _ = c + (a * b);
++ | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:17:13
++ |
++LL | let _ = a * b * c + d;
++ | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:19:13
++ |
++LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++ --> $DIR/floating_point_mul_add.rs:20:13
++ |
++LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++fn main() {
++ let x = 3f32;
++ let _ = x.exp2();
++ let _ = 3.1f32.exp2();
++ let _ = (-3.1f32).exp2();
++ let _ = x.exp();
++ let _ = 3.1f32.exp();
++ let _ = (-3.1f32).exp();
++ let _ = x.sqrt();
++ let _ = x.cbrt();
++ let _ = x.powi(2);
++ let _ = x.powi(-2);
++ let _ = x.powi(16_777_215);
++ let _ = x.powi(-16_777_215);
++ // Cases where the lint shouldn't be applied
++ let _ = x.powf(2.1);
++ let _ = x.powf(-2.1);
++ let _ = x.powf(16_777_216.0);
++ let _ = x.powf(-16_777_216.0);
++
++ let x = 3f64;
++ let _ = x.exp2();
++ let _ = 3.1f64.exp2();
++ let _ = (-3.1f64).exp2();
++ let _ = x.exp();
++ let _ = 3.1f64.exp();
++ let _ = (-3.1f64).exp();
++ let _ = x.sqrt();
++ let _ = x.cbrt();
++ let _ = x.powi(2);
++ let _ = x.powi(-2);
++ let _ = x.powi(-2_147_483_648);
++ let _ = x.powi(2_147_483_647);
++ // Cases where the lint shouldn't be applied
++ let _ = x.powf(2.1);
++ let _ = x.powf(-2.1);
++ let _ = x.powf(-2_147_483_649.0);
++ let _ = x.powf(2_147_483_648.0);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++fn main() {
++ let x = 3f32;
++ let _ = 2f32.powf(x);
++ let _ = 2f32.powf(3.1);
++ let _ = 2f32.powf(-3.1);
++ let _ = std::f32::consts::E.powf(x);
++ let _ = std::f32::consts::E.powf(3.1);
++ let _ = std::f32::consts::E.powf(-3.1);
++ let _ = x.powf(1.0 / 2.0);
++ let _ = x.powf(1.0 / 3.0);
++ let _ = x.powf(2.0);
++ let _ = x.powf(-2.0);
++ let _ = x.powf(16_777_215.0);
++ let _ = x.powf(-16_777_215.0);
++ // Cases where the lint shouldn't be applied
++ let _ = x.powf(2.1);
++ let _ = x.powf(-2.1);
++ let _ = x.powf(16_777_216.0);
++ let _ = x.powf(-16_777_216.0);
++
++ let x = 3f64;
++ let _ = 2f64.powf(x);
++ let _ = 2f64.powf(3.1);
++ let _ = 2f64.powf(-3.1);
++ let _ = std::f64::consts::E.powf(x);
++ let _ = std::f64::consts::E.powf(3.1);
++ let _ = std::f64::consts::E.powf(-3.1);
++ let _ = x.powf(1.0 / 2.0);
++ let _ = x.powf(1.0 / 3.0);
++ let _ = x.powf(2.0);
++ let _ = x.powf(-2.0);
++ let _ = x.powf(-2_147_483_648.0);
++ let _ = x.powf(2_147_483_647.0);
++ // Cases where the lint shouldn't be applied
++ let _ = x.powf(2.1);
++ let _ = x.powf(-2.1);
++ let _ = x.powf(-2_147_483_649.0);
++ let _ = x.powf(2_147_483_648.0);
++}
--- /dev/null
--- /dev/null
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:6:13
++ |
++LL | let _ = 2f32.powf(x);
++ | ^^^^^^^^^^^^ help: consider using: `x.exp2()`
++ |
++ = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:7:13
++ |
++LL | let _ = 2f32.powf(3.1);
++ | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:8:13
++ |
++LL | let _ = 2f32.powf(-3.1);
++ | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:9:13
++ |
++LL | let _ = std::f32::consts::E.powf(x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:10:13
++ |
++LL | let _ = std::f32::consts::E.powf(3.1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:11:13
++ |
++LL | let _ = std::f32::consts::E.powf(-3.1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
++
++error: square-root of a number can be computed more efficiently and accurately
++ --> $DIR/floating_point_powf.rs:12:13
++ |
++LL | let _ = x.powf(1.0 / 2.0);
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
++
++error: cube-root of a number can be computed more accurately
++ --> $DIR/floating_point_powf.rs:13:13
++ |
++LL | let _ = x.powf(1.0 / 3.0);
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
++ |
++ = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:14:13
++ |
++LL | let _ = x.powf(2.0);
++ | ^^^^^^^^^^^ help: consider using: `x.powi(2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:15:13
++ |
++LL | let _ = x.powf(-2.0);
++ | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:16:13
++ |
++LL | let _ = x.powf(16_777_215.0);
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:17:13
++ |
++LL | let _ = x.powf(-16_777_215.0);
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:25:13
++ |
++LL | let _ = 2f64.powf(x);
++ | ^^^^^^^^^^^^ help: consider using: `x.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:26:13
++ |
++LL | let _ = 2f64.powf(3.1);
++ | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:27:13
++ |
++LL | let _ = 2f64.powf(-3.1);
++ | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:28:13
++ |
++LL | let _ = std::f64::consts::E.powf(x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:29:13
++ |
++LL | let _ = std::f64::consts::E.powf(3.1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++ --> $DIR/floating_point_powf.rs:30:13
++ |
++LL | let _ = std::f64::consts::E.powf(-3.1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
++
++error: square-root of a number can be computed more efficiently and accurately
++ --> $DIR/floating_point_powf.rs:31:13
++ |
++LL | let _ = x.powf(1.0 / 2.0);
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
++
++error: cube-root of a number can be computed more accurately
++ --> $DIR/floating_point_powf.rs:32:13
++ |
++LL | let _ = x.powf(1.0 / 3.0);
++ | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:33:13
++ |
++LL | let _ = x.powf(2.0);
++ | ^^^^^^^^^^^ help: consider using: `x.powi(2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:34:13
++ |
++LL | let _ = x.powf(-2.0);
++ | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:35:13
++ |
++LL | let _ = x.powf(-2_147_483_648.0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
++
++error: exponentiation with integer powers can be computed more efficiently
++ --> $DIR/floating_point_powf.rs:36:13
++ |
++LL | let _ = x.powf(2_147_483_647.0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
++
++error: aborting due to 24 previous errors
++
--- /dev/null
--- /dev/null
++use std::fmt::Debug;
++use std::ptr;
++use std::rc::Rc;
++use std::sync::Arc;
++
++fn a() {}
++
++#[warn(clippy::fn_address_comparisons)]
++fn main() {
++ type F = fn();
++ let f: F = a;
++ let g: F = f;
++
++ // These should fail:
++ let _ = f == a;
++ let _ = f != a;
++
++ // These should be fine:
++ let _ = f == g;
++}
--- /dev/null
--- /dev/null
++error: comparing with a non-unique address of a function item
++ --> $DIR/fn_address_comparisons.rs:15:13
++ |
++LL | let _ = f == a;
++ | ^^^^^^
++ |
++ = note: `-D clippy::fn-address-comparisons` implied by `-D warnings`
++
++error: comparing with a non-unique address of a function item
++ --> $DIR/fn_address_comparisons.rs:16:13
++ |
++LL | let _ = f != a;
++ | ^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::fn_params_excessive_bools)]
++
++extern "C" {
++ fn f(_: bool, _: bool, _: bool, _: bool);
++}
++
++macro_rules! foo {
++ () => {
++ fn fff(_: bool, _: bool, _: bool, _: bool) {}
++ };
++}
++
++foo!();
++
++#[no_mangle]
++extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {}
++fn g(_: bool, _: bool, _: bool, _: bool) {}
++fn h(_: bool, _: bool, _: bool) {}
++fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
++fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
++
++struct S {}
++trait Trait {
++ fn f(_: bool, _: bool, _: bool, _: bool);
++ fn g(_: bool, _: bool, _: bool, _: Vec<u32>);
++}
++
++impl S {
++ fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
++ fn g(&self, _: bool, _: bool, _: bool) {}
++ #[no_mangle]
++ extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {}
++}
++
++impl Trait for S {
++ fn f(_: bool, _: bool, _: bool, _: bool) {}
++ fn g(_: bool, _: bool, _: bool, _: Vec<u32>) {}
++}
++
++fn main() {
++ fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
++ fn nn(_: bool, _: bool, _: bool, _: bool) {}
++ }
++}
--- /dev/null
--- /dev/null
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:17:1
++ |
++LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
++ = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:20:1
++ |
++LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:24:5
++ |
++LL | fn f(_: bool, _: bool, _: bool, _: bool);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:29:5
++ |
++LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:41:5
++ |
++LL | / fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
++LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {}
++LL | | }
++ | |_____^
++ |
++ = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++ --> $DIR/fn_params_excessive_bools.rs:42:9
++ |
++LL | fn nn(_: bool, _: bool, _: bool, _: bool) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider refactoring bools into two-variant enums
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-32bit
++
++#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
++
++fn foo() -> String {
++ String::new()
++}
++
++fn test_function_to_numeric_cast() {
++ let _ = foo as i8;
++ let _ = foo as i16;
++ let _ = foo as i32;
++ let _ = foo as i64;
++ let _ = foo as i128;
++ let _ = foo as isize;
++
++ let _ = foo as u8;
++ let _ = foo as u16;
++ let _ = foo as u32;
++ let _ = foo as u64;
++ let _ = foo as u128;
++
++ // Casting to usize is OK and should not warn
++ let _ = foo as usize;
++
++ // Cast `f` (a `FnDef`) to `fn()` should not warn
++ fn f() {}
++ let _ = f as fn();
++}
++
++fn test_function_var_to_numeric_cast() {
++ let abc: fn() -> String = foo;
++
++ let _ = abc as i8;
++ let _ = abc as i16;
++ let _ = abc as i32;
++ let _ = abc as i64;
++ let _ = abc as i128;
++ let _ = abc as isize;
++
++ let _ = abc as u8;
++ let _ = abc as u16;
++ let _ = abc as u32;
++ let _ = abc as u64;
++ let _ = abc as u128;
++
++ // Casting to usize is OK and should not warn
++ let _ = abc as usize;
++}
++
++fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
++ f as i32
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: casting function pointer `foo` to `i8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:10:13
++ |
++LL | let _ = foo as i8;
++ | ^^^^^^^^^ help: try: `foo as usize`
++ |
++ = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:11:13
++ |
++LL | let _ = foo as i16;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i32`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:12:13
++ |
++LL | let _ = foo as i32;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i64`
++ --> $DIR/fn_to_numeric_cast.rs:13:13
++ |
++LL | let _ = foo as i64;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++ |
++ = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i128`
++ --> $DIR/fn_to_numeric_cast.rs:14:13
++ |
++LL | let _ = foo as i128;
++ | ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `isize`
++ --> $DIR/fn_to_numeric_cast.rs:15:13
++ |
++LL | let _ = foo as isize;
++ | ^^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:17:13
++ |
++LL | let _ = foo as u8;
++ | ^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:18:13
++ |
++LL | let _ = foo as u16;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u32`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:19:13
++ |
++LL | let _ = foo as u32;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u64`
++ --> $DIR/fn_to_numeric_cast.rs:20:13
++ |
++LL | let _ = foo as u64;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u128`
++ --> $DIR/fn_to_numeric_cast.rs:21:13
++ |
++LL | let _ = foo as u128;
++ | ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `abc` to `i8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:34:13
++ |
++LL | let _ = abc as i8;
++ | ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:35:13
++ |
++LL | let _ = abc as i16;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i32`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:36:13
++ |
++LL | let _ = abc as i32;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i64`
++ --> $DIR/fn_to_numeric_cast.rs:37:13
++ |
++LL | let _ = abc as i64;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i128`
++ --> $DIR/fn_to_numeric_cast.rs:38:13
++ |
++LL | let _ = abc as i128;
++ | ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `isize`
++ --> $DIR/fn_to_numeric_cast.rs:39:13
++ |
++LL | let _ = abc as isize;
++ | ^^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:41:13
++ |
++LL | let _ = abc as u8;
++ | ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:42:13
++ |
++LL | let _ = abc as u16;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u32`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:43:13
++ |
++LL | let _ = abc as u32;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u64`
++ --> $DIR/fn_to_numeric_cast.rs:44:13
++ |
++LL | let _ = abc as u64;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u128`
++ --> $DIR/fn_to_numeric_cast.rs:45:13
++ |
++LL | let _ = abc as u128;
++ | ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `f` to `i32`, which truncates the value
++ --> $DIR/fn_to_numeric_cast.rs:52:5
++ |
++LL | f as i32
++ | ^^^^^^^^ help: try: `f as usize`
++
++error: aborting due to 23 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-64bit
++
++#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
++
++fn foo() -> String {
++ String::new()
++}
++
++fn test_function_to_numeric_cast() {
++ let _ = foo as i8;
++ let _ = foo as i16;
++ let _ = foo as i32;
++ let _ = foo as i64;
++ let _ = foo as i128;
++ let _ = foo as isize;
++
++ let _ = foo as u8;
++ let _ = foo as u16;
++ let _ = foo as u32;
++ let _ = foo as u64;
++ let _ = foo as u128;
++
++ // Casting to usize is OK and should not warn
++ let _ = foo as usize;
++
++ // Cast `f` (a `FnDef`) to `fn()` should not warn
++ fn f() {}
++ let _ = f as fn();
++}
++
++fn test_function_var_to_numeric_cast() {
++ let abc: fn() -> String = foo;
++
++ let _ = abc as i8;
++ let _ = abc as i16;
++ let _ = abc as i32;
++ let _ = abc as i64;
++ let _ = abc as i128;
++ let _ = abc as isize;
++
++ let _ = abc as u8;
++ let _ = abc as u16;
++ let _ = abc as u32;
++ let _ = abc as u64;
++ let _ = abc as u128;
++
++ // Casting to usize is OK and should not warn
++ let _ = abc as usize;
++}
++
++fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
++ f as i32
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: casting function pointer `foo` to `i8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:10:13
++ |
++LL | let _ = foo as i8;
++ | ^^^^^^^^^ help: try: `foo as usize`
++ |
++ = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:11:13
++ |
++LL | let _ = foo as i16;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i32`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:12:13
++ |
++LL | let _ = foo as i32;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++ |
++ = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i64`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:13:13
++ |
++LL | let _ = foo as i64;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i128`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:14:13
++ |
++LL | let _ = foo as i128;
++ | ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `isize`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:15:13
++ |
++LL | let _ = foo as isize;
++ | ^^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:17:13
++ |
++LL | let _ = foo as u8;
++ | ^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:18:13
++ |
++LL | let _ = foo as u16;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u32`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:19:13
++ |
++LL | let _ = foo as u32;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u64`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:20:13
++ |
++LL | let _ = foo as u64;
++ | ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u128`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:21:13
++ |
++LL | let _ = foo as u128;
++ | ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `abc` to `i8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:34:13
++ |
++LL | let _ = abc as i8;
++ | ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:35:13
++ |
++LL | let _ = abc as i16;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i32`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:36:13
++ |
++LL | let _ = abc as i32;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i64`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:37:13
++ |
++LL | let _ = abc as i64;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i128`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:38:13
++ |
++LL | let _ = abc as i128;
++ | ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `isize`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:39:13
++ |
++LL | let _ = abc as isize;
++ | ^^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u8`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:41:13
++ |
++LL | let _ = abc as u8;
++ | ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u16`, which truncates the value
++ --> $DIR/fn_to_numeric_cast_32bit.rs:42:13
++ |
++LL | let _ = abc as u16;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u32`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:43:13
++ |
++LL | let _ = abc as u32;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u64`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:44:13
++ |
++LL | let _ = abc as u64;
++ | ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u128`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:45:13
++ |
++LL | let _ = abc as u128;
++ | ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `f` to `i32`
++ --> $DIR/fn_to_numeric_cast_32bit.rs:52:5
++ |
++LL | f as i32
++ | ^^^^^^^^ help: try: `f as usize`
++
++error: aborting due to 23 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::for_kv_map)]
++#![allow(clippy::used_underscore_binding)]
++
++use std::collections::*;
++use std::rc::Rc;
++
++fn main() {
++ let m: HashMap<u64, u64> = HashMap::new();
++ for (_, v) in &m {
++ let _v = v;
++ }
++
++ let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
++ for (_, v) in &*m {
++ let _v = v;
++ // Here the `*` is not actually necessary, but the test tests that we don't
++ // suggest
++ // `in *m.values()` as we used to
++ }
++
++ let mut m: HashMap<u64, u64> = HashMap::new();
++ for (_, v) in &mut m {
++ let _v = v;
++ }
++
++ let m: &mut HashMap<u64, u64> = &mut HashMap::new();
++ for (_, v) in &mut *m {
++ let _v = v;
++ }
++
++ let m: HashMap<u64, u64> = HashMap::new();
++ let rm = &m;
++ for (k, _value) in rm {
++ let _k = k;
++ }
++
++ // The following should not produce warnings.
++
++ let m: HashMap<u64, u64> = HashMap::new();
++ // No error, _value is actually used
++ for (k, _value) in &m {
++ let _ = _value;
++ let _k = k;
++ }
++
++ let m: HashMap<u64, String> = Default::default();
++ for (_, v) in m {
++ let _v = v;
++ }
++}
--- /dev/null
--- /dev/null
++error: you seem to want to iterate on a map's values
++ --> $DIR/for_kv_map.rs:9:19
++ |
++LL | for (_, v) in &m {
++ | ^^
++ |
++ = note: `-D clippy::for-kv-map` implied by `-D warnings`
++help: use the corresponding method
++ |
++LL | for v in m.values() {
++ | ^ ^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++ --> $DIR/for_kv_map.rs:14:19
++ |
++LL | for (_, v) in &*m {
++ | ^^^
++ |
++help: use the corresponding method
++ |
++LL | for v in (*m).values() {
++ | ^ ^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++ --> $DIR/for_kv_map.rs:22:19
++ |
++LL | for (_, v) in &mut m {
++ | ^^^^^^
++ |
++help: use the corresponding method
++ |
++LL | for v in m.values_mut() {
++ | ^ ^^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++ --> $DIR/for_kv_map.rs:27:19
++ |
++LL | for (_, v) in &mut *m {
++ | ^^^^^^^
++ |
++help: use the corresponding method
++ |
++LL | for v in (*m).values_mut() {
++ | ^ ^^^^^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's keys
++ --> $DIR/for_kv_map.rs:33:24
++ |
++LL | for (k, _value) in rm {
++ | ^^
++ |
++help: use the corresponding method
++ |
++LL | for k in rm.keys() {
++ | ^ ^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused)]
++
++use std::collections::*;
++
++#[warn(clippy::all)]
++struct Unrelated(Vec<u8>);
++impl Unrelated {
++ fn next(&self) -> std::slice::Iter<u8> {
++ self.0.iter()
++ }
++
++ fn iter(&self) -> std::slice::Iter<u8> {
++ self.0.iter()
++ }
++}
++
++#[warn(
++ clippy::needless_range_loop,
++ clippy::explicit_iter_loop,
++ clippy::explicit_into_iter_loop,
++ clippy::iter_next_loop,
++ clippy::reverse_range_loop,
++ clippy::for_kv_map
++)]
++#[allow(
++ clippy::linkedlist,
++ clippy::shadow_unrelated,
++ clippy::unnecessary_mut_passed,
++ clippy::similar_names
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++ const MAX_LEN: usize = 42;
++ let mut vec = vec![1, 2, 3, 4];
++
++ for i in (0..10).rev() {
++ println!("{}", i);
++ }
++
++ for i in (0..=10).rev() {
++ println!("{}", i);
++ }
++
++ for i in (0..MAX_LEN).rev() {
++ println!("{}", i);
++ }
++
++ for i in 5..=5 {
++ // not an error, this is the range with only one element “5”
++ println!("{}", i);
++ }
++
++ for i in 0..10 {
++ // not an error, the start index is less than the end index
++ println!("{}", i);
++ }
++
++ for i in -10..0 {
++ // not an error
++ println!("{}", i);
++ }
++
++ for i in (10..0).map(|x| x * 2) {
++ // not an error, it can't be known what arbitrary methods do to a range
++ println!("{}", i);
++ }
++
++ // testing that the empty range lint folds constants
++ for i in (5 + 4..10).rev() {
++ println!("{}", i);
++ }
++
++ for i in ((3 - 1)..(5 + 2)).rev() {
++ println!("{}", i);
++ }
++
++ for i in (2 * 2)..(2 * 3) {
++ // no error, 4..6 is fine
++ println!("{}", i);
++ }
++
++ let x = 42;
++ for i in x..10 {
++ // no error, not constant-foldable
++ println!("{}", i);
++ }
++
++ // See #601
++ for i in 0..10 {
++ // no error, id_col does not exist outside the loop
++ let mut id_col = vec![0f64; 10];
++ id_col[i] = 1f64;
++ }
++
++ for _v in &vec {}
++
++ for _v in &mut vec {}
++
++ let out_vec = vec![1, 2, 3];
++ for _v in out_vec {}
++
++ for _v in &vec {} // these are fine
++ for _v in &mut vec {} // these are fine
++
++ for _v in &[1, 2, 3] {}
++
++ for _v in (&mut [1, 2, 3]).iter() {} // no error
++
++ for _v in &[0; 32] {}
++
++ for _v in [0; 33].iter() {} // no error
++
++ let ll: LinkedList<()> = LinkedList::new();
++ for _v in &ll {}
++
++ let vd: VecDeque<()> = VecDeque::new();
++ for _v in &vd {}
++
++ let bh: BinaryHeap<()> = BinaryHeap::new();
++ for _v in &bh {}
++
++ let hm: HashMap<(), ()> = HashMap::new();
++ for _v in &hm {}
++
++ let bt: BTreeMap<(), ()> = BTreeMap::new();
++ for _v in &bt {}
++
++ let hs: HashSet<()> = HashSet::new();
++ for _v in &hs {}
++
++ let bs: BTreeSet<()> = BTreeSet::new();
++ for _v in &bs {}
++
++ let u = Unrelated(vec![]);
++ for _v in u.next() {} // no error
++ for _v in u.iter() {} // no error
++
++ let mut out = vec![];
++ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
++ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
++
++ // Loop with explicit counter variable
++
++ // Potential false positives
++ let mut _index = 0;
++ _index = 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ _index += 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ if true {
++ _index = 1
++ }
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ let mut _index = 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index += 1;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index *= 2;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index = 1;
++ _index += 1
++ }
++
++ let mut _index = 0;
++
++ for _v in &vec {
++ let mut _index = 0;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index += 1;
++ _index = 0;
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ for _x in 0..1 {
++ _index += 1;
++ }
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for x in &vec {
++ if *x == 1 {
++ _index += 1
++ }
++ }
++
++ let mut _index = 0;
++ if true {
++ _index = 1
++ };
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 1;
++ if false {
++ _index = 0
++ };
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut index = 0;
++ {
++ let mut _x = &mut index;
++ }
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut index = 0;
++ for _v in &vec {
++ index += 1
++ }
++ println!("index: {}", index);
++
++ fn f<T>(_: &T, _: &T) -> bool {
++ unimplemented!()
++ }
++ fn g<T>(_: &mut [T], _: usize, _: usize) {
++ unimplemented!()
++ }
++ for i in 1..vec.len() {
++ if f(&vec[i - 1], &vec[i]) {
++ g(&mut vec, i - 1, i);
++ }
++ }
++
++ for mid in 1..vec.len() {
++ let (_, _) = vec.split_at(mid);
++ }
++}
++
++fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
++ let pivot = v.len() - 1;
++ let mut i = 0;
++ for j in 0..pivot {
++ if v[j] <= v[pivot] {
++ v.swap(i, j);
++ i += 1;
++ }
++ }
++ v.swap(i, pivot);
++ i
++}
++
++#[warn(clippy::needless_range_loop)]
++pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
++ // Same source and destination - don't trigger lint
++ for i in 0..dst.len() {
++ dst[d + i] = dst[s + i];
++ }
++}
++
++mod issue_2496 {
++ pub trait Handle {
++ fn new_for_index(index: usize) -> Self;
++ fn index(&self) -> usize;
++ }
++
++ pub fn test<H: Handle>() -> H {
++ for x in 0..5 {
++ let next_handle = H::new_for_index(x);
++ println!("{}", next_handle.index());
++ }
++ unimplemented!()
++ }
++}
++
++// explicit_into_iter_loop bad suggestions
++#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
++mod issue_4958 {
++ fn takes_iterator<T>(iterator: &T)
++ where
++ for<'a> &'a T: IntoIterator<Item = &'a String>,
++ {
++ for i in iterator {
++ println!("{}", i);
++ }
++ }
++
++ struct T;
++ impl IntoIterator for &T {
++ type Item = ();
++ type IntoIter = std::vec::IntoIter<Self::Item>;
++ fn into_iter(self) -> Self::IntoIter {
++ vec![].into_iter()
++ }
++ }
++
++ fn more_tests() {
++ let t = T;
++ let r = &t;
++ let rr = &&t;
++
++ // This case is handled by `explicit_iter_loop`. No idea why.
++ for _ in &t {}
++
++ for _ in r {}
++
++ // No suggestion for this.
++ // We'd have to suggest `for _ in *rr {}` which is less clear.
++ for _ in rr.into_iter() {}
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused)]
++
++use std::collections::*;
++
++#[warn(clippy::all)]
++struct Unrelated(Vec<u8>);
++impl Unrelated {
++ fn next(&self) -> std::slice::Iter<u8> {
++ self.0.iter()
++ }
++
++ fn iter(&self) -> std::slice::Iter<u8> {
++ self.0.iter()
++ }
++}
++
++#[warn(
++ clippy::needless_range_loop,
++ clippy::explicit_iter_loop,
++ clippy::explicit_into_iter_loop,
++ clippy::iter_next_loop,
++ clippy::reverse_range_loop,
++ clippy::for_kv_map
++)]
++#[allow(
++ clippy::linkedlist,
++ clippy::shadow_unrelated,
++ clippy::unnecessary_mut_passed,
++ clippy::similar_names
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++ const MAX_LEN: usize = 42;
++ let mut vec = vec![1, 2, 3, 4];
++
++ for i in 10..0 {
++ println!("{}", i);
++ }
++
++ for i in 10..=0 {
++ println!("{}", i);
++ }
++
++ for i in MAX_LEN..0 {
++ println!("{}", i);
++ }
++
++ for i in 5..=5 {
++ // not an error, this is the range with only one element “5”
++ println!("{}", i);
++ }
++
++ for i in 0..10 {
++ // not an error, the start index is less than the end index
++ println!("{}", i);
++ }
++
++ for i in -10..0 {
++ // not an error
++ println!("{}", i);
++ }
++
++ for i in (10..0).map(|x| x * 2) {
++ // not an error, it can't be known what arbitrary methods do to a range
++ println!("{}", i);
++ }
++
++ // testing that the empty range lint folds constants
++ for i in 10..5 + 4 {
++ println!("{}", i);
++ }
++
++ for i in (5 + 2)..(3 - 1) {
++ println!("{}", i);
++ }
++
++ for i in (2 * 2)..(2 * 3) {
++ // no error, 4..6 is fine
++ println!("{}", i);
++ }
++
++ let x = 42;
++ for i in x..10 {
++ // no error, not constant-foldable
++ println!("{}", i);
++ }
++
++ // See #601
++ for i in 0..10 {
++ // no error, id_col does not exist outside the loop
++ let mut id_col = vec![0f64; 10];
++ id_col[i] = 1f64;
++ }
++
++ for _v in vec.iter() {}
++
++ for _v in vec.iter_mut() {}
++
++ let out_vec = vec![1, 2, 3];
++ for _v in out_vec.into_iter() {}
++
++ for _v in &vec {} // these are fine
++ for _v in &mut vec {} // these are fine
++
++ for _v in [1, 2, 3].iter() {}
++
++ for _v in (&mut [1, 2, 3]).iter() {} // no error
++
++ for _v in [0; 32].iter() {}
++
++ for _v in [0; 33].iter() {} // no error
++
++ let ll: LinkedList<()> = LinkedList::new();
++ for _v in ll.iter() {}
++
++ let vd: VecDeque<()> = VecDeque::new();
++ for _v in vd.iter() {}
++
++ let bh: BinaryHeap<()> = BinaryHeap::new();
++ for _v in bh.iter() {}
++
++ let hm: HashMap<(), ()> = HashMap::new();
++ for _v in hm.iter() {}
++
++ let bt: BTreeMap<(), ()> = BTreeMap::new();
++ for _v in bt.iter() {}
++
++ let hs: HashSet<()> = HashSet::new();
++ for _v in hs.iter() {}
++
++ let bs: BTreeSet<()> = BTreeSet::new();
++ for _v in bs.iter() {}
++
++ let u = Unrelated(vec![]);
++ for _v in u.next() {} // no error
++ for _v in u.iter() {} // no error
++
++ let mut out = vec![];
++ vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
++ let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
++
++ // Loop with explicit counter variable
++
++ // Potential false positives
++ let mut _index = 0;
++ _index = 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ _index += 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ if true {
++ _index = 1
++ }
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ let mut _index = 1;
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index += 1;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index *= 2;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index = 1;
++ _index += 1
++ }
++
++ let mut _index = 0;
++
++ for _v in &vec {
++ let mut _index = 0;
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ _index += 1;
++ _index = 0;
++ }
++
++ let mut _index = 0;
++ for _v in &vec {
++ for _x in 0..1 {
++ _index += 1;
++ }
++ _index += 1
++ }
++
++ let mut _index = 0;
++ for x in &vec {
++ if *x == 1 {
++ _index += 1
++ }
++ }
++
++ let mut _index = 0;
++ if true {
++ _index = 1
++ };
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut _index = 1;
++ if false {
++ _index = 0
++ };
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut index = 0;
++ {
++ let mut _x = &mut index;
++ }
++ for _v in &vec {
++ _index += 1
++ }
++
++ let mut index = 0;
++ for _v in &vec {
++ index += 1
++ }
++ println!("index: {}", index);
++
++ fn f<T>(_: &T, _: &T) -> bool {
++ unimplemented!()
++ }
++ fn g<T>(_: &mut [T], _: usize, _: usize) {
++ unimplemented!()
++ }
++ for i in 1..vec.len() {
++ if f(&vec[i - 1], &vec[i]) {
++ g(&mut vec, i - 1, i);
++ }
++ }
++
++ for mid in 1..vec.len() {
++ let (_, _) = vec.split_at(mid);
++ }
++}
++
++fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
++ let pivot = v.len() - 1;
++ let mut i = 0;
++ for j in 0..pivot {
++ if v[j] <= v[pivot] {
++ v.swap(i, j);
++ i += 1;
++ }
++ }
++ v.swap(i, pivot);
++ i
++}
++
++#[warn(clippy::needless_range_loop)]
++pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
++ // Same source and destination - don't trigger lint
++ for i in 0..dst.len() {
++ dst[d + i] = dst[s + i];
++ }
++}
++
++mod issue_2496 {
++ pub trait Handle {
++ fn new_for_index(index: usize) -> Self;
++ fn index(&self) -> usize;
++ }
++
++ pub fn test<H: Handle>() -> H {
++ for x in 0..5 {
++ let next_handle = H::new_for_index(x);
++ println!("{}", next_handle.index());
++ }
++ unimplemented!()
++ }
++}
++
++// explicit_into_iter_loop bad suggestions
++#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
++mod issue_4958 {
++ fn takes_iterator<T>(iterator: &T)
++ where
++ for<'a> &'a T: IntoIterator<Item = &'a String>,
++ {
++ for i in iterator.into_iter() {
++ println!("{}", i);
++ }
++ }
++
++ struct T;
++ impl IntoIterator for &T {
++ type Item = ();
++ type IntoIter = std::vec::IntoIter<Self::Item>;
++ fn into_iter(self) -> Self::IntoIter {
++ vec![].into_iter()
++ }
++ }
++
++ fn more_tests() {
++ let t = T;
++ let r = &t;
++ let rr = &&t;
++
++ // This case is handled by `explicit_iter_loop`. No idea why.
++ for _ in t.into_iter() {}
++
++ for _ in r.into_iter() {}
++
++ // No suggestion for this.
++ // We'd have to suggest `for _ in *rr {}` which is less clear.
++ for _ in rr.into_iter() {}
++ }
++}
--- /dev/null
--- /dev/null
++error: this range is empty so this for loop will never run
++ --> $DIR/for_loop_fixable.rs:38:14
++ |
++LL | for i in 10..0 {
++ | ^^^^^
++ |
++ = note: `-D clippy::reverse-range-loop` implied by `-D warnings`
++help: consider using the following if you are attempting to iterate over this range in reverse
++ |
++LL | for i in (0..10).rev() {
++ | ^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++ --> $DIR/for_loop_fixable.rs:42:14
++ |
++LL | for i in 10..=0 {
++ | ^^^^^^
++ |
++help: consider using the following if you are attempting to iterate over this range in reverse
++ |
++LL | for i in (0..=10).rev() {
++ | ^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++ --> $DIR/for_loop_fixable.rs:46:14
++ |
++LL | for i in MAX_LEN..0 {
++ | ^^^^^^^^^^
++ |
++help: consider using the following if you are attempting to iterate over this range in reverse
++ |
++LL | for i in (0..MAX_LEN).rev() {
++ | ^^^^^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++ --> $DIR/for_loop_fixable.rs:71:14
++ |
++LL | for i in 10..5 + 4 {
++ | ^^^^^^^^^
++ |
++help: consider using the following if you are attempting to iterate over this range in reverse
++ |
++LL | for i in (5 + 4..10).rev() {
++ | ^^^^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++ --> $DIR/for_loop_fixable.rs:75:14
++ |
++LL | for i in (5 + 2)..(3 - 1) {
++ | ^^^^^^^^^^^^^^^^
++ |
++help: consider using the following if you are attempting to iterate over this range in reverse
++ |
++LL | for i in ((3 - 1)..(5 + 2)).rev() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:97:15
++ |
++LL | for _v in vec.iter() {}
++ | ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
++ |
++ = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:99:15
++ |
++LL | for _v in vec.iter_mut() {}
++ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:102:15
++ |
++LL | for _v in out_vec.into_iter() {}
++ | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
++ |
++ = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:107:15
++ |
++LL | for _v in [1, 2, 3].iter() {}
++ | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:111:15
++ |
++LL | for _v in [0; 32].iter() {}
++ | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:116:15
++ |
++LL | for _v in ll.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&ll`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:119:15
++ |
++LL | for _v in vd.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&vd`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:122:15
++ |
++LL | for _v in bh.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&bh`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:125:15
++ |
++LL | for _v in hm.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&hm`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:128:15
++ |
++LL | for _v in bt.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&bt`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:131:15
++ |
++LL | for _v in hs.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&hs`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:134:15
++ |
++LL | for _v in bs.iter() {}
++ | ^^^^^^^^^ help: to write this more concisely, try: `&bs`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:309:18
++ |
++LL | for i in iterator.into_iter() {
++ | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:329:18
++ |
++LL | for _ in t.into_iter() {}
++ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++ --> $DIR/for_loop_fixable.rs:331:18
++ |
++LL | for _ in r.into_iter() {}
++ | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)]
++
++/// Tests for_loop_over_result and for_loop_over_option
++
++fn for_loop_over_option_and_result() {
++ let option = Some(1);
++ let result = option.ok_or("x not found");
++ let v = vec![0, 1, 2];
++
++ // check FOR_LOOP_OVER_OPTION lint
++ for x in option {
++ println!("{}", x);
++ }
++
++ // check FOR_LOOP_OVER_RESULT lint
++ for x in result {
++ println!("{}", x);
++ }
++
++ for x in option.ok_or("x not found") {
++ println!("{}", x);
++ }
++
++ // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call
++ // in the chain
++ for x in v.iter().next() {
++ println!("{}", x);
++ }
++
++ // make sure we lint when next() is not the last call in the chain
++ for x in v.iter().next().and(Some(0)) {
++ println!("{}", x);
++ }
++
++ for x in v.iter().next().ok_or("x not found") {
++ println!("{}", x);
++ }
++
++ // check for false positives
++
++ // for loop false positive
++ for x in v {
++ println!("{}", x);
++ }
++
++ // while let false positive for Option
++ while let Some(x) = option {
++ println!("{}", x);
++ break;
++ }
++
++ // while let false positive for Result
++ while let Ok(x) = result {
++ println!("{}", x);
++ break;
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement.
++ --> $DIR/for_loop_over_option_result.rs:11:14
++ |
++LL | for x in option {
++ | ^^^^^^
++ |
++ = note: `-D clippy::for-loop-over-option` implied by `-D warnings`
++ = help: consider replacing `for x in option` with `if let Some(x) = option`
++
++error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement.
++ --> $DIR/for_loop_over_option_result.rs:16:14
++ |
++LL | for x in result {
++ | ^^^^^^
++ |
++ = note: `-D clippy::for-loop-over-result` implied by `-D warnings`
++ = help: consider replacing `for x in result` with `if let Ok(x) = result`
++
++error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement.
++ --> $DIR/for_loop_over_option_result.rs:20:14
++ |
++LL | for x in option.ok_or("x not found") {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
++
++error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
++ --> $DIR/for_loop_over_option_result.rs:26:14
++ |
++LL | for x in v.iter().next() {
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: `#[deny(clippy::iter_next_loop)]` on by default
++
++error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement.
++ --> $DIR/for_loop_over_option_result.rs:31:14
++ |
++LL | for x in v.iter().next().and(Some(0)) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
++
++error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement.
++ --> $DIR/for_loop_over_option_result.rs:35:14
++ |
++LL | for x in v.iter().next().ok_or("x not found") {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
++
++error: this loop never actually loops
++ --> $DIR/for_loop_over_option_result.rs:47:5
++ |
++LL | / while let Some(x) = option {
++LL | | println!("{}", x);
++LL | | break;
++LL | | }
++ | |_____^
++ |
++ = note: `#[deny(clippy::never_loop)]` on by default
++
++error: this loop never actually loops
++ --> $DIR/for_loop_over_option_result.rs:53:5
++ |
++LL | / while let Ok(x) = result {
++LL | | println!("{}", x);
++LL | | break;
++LL | | }
++ | |_____^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// Tests from for_loop.rs that don't have suggestions
++
++#[warn(
++ clippy::needless_range_loop,
++ clippy::explicit_iter_loop,
++ clippy::explicit_into_iter_loop,
++ clippy::iter_next_loop,
++ clippy::reverse_range_loop,
++ clippy::for_kv_map
++)]
++#[allow(
++ clippy::linkedlist,
++ clippy::shadow_unrelated,
++ clippy::unnecessary_mut_passed,
++ clippy::similar_names,
++ unused,
++ dead_code
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++ for i in 5..5 {
++ println!("{}", i);
++ }
++
++ let vec = vec![1, 2, 3, 4];
++
++ for _v in vec.iter().next() {}
++
++ for i in (5 + 2)..(8 - 1) {
++ println!("{}", i);
++ }
++
++ const ZERO: usize = 0;
++
++ for i in ZERO..vec.len() {
++ if f(&vec[i], &vec[i]) {
++ panic!("at the disco");
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error[E0425]: cannot find function `f` in this scope
++ --> $DIR/for_loop_unfixable.rs:36:12
++ |
++LL | if f(&vec[i], &vec[i]) {
++ | ^ help: a local variable with a similar name exists: `i`
++
++error: aborting due to previous error
++
++For more information about this error, try `rustc --explain E0425`.
--- /dev/null
--- /dev/null
++#![warn(clippy::forget_ref)]
++#![allow(clippy::toplevel_ref_arg)]
++
++use std::mem::forget;
++
++struct SomeStruct;
++
++fn main() {
++ forget(&SomeStruct);
++
++ let mut owned = SomeStruct;
++ forget(&owned);
++ forget(&&owned);
++ forget(&mut owned);
++ forget(owned); //OK
++
++ let reference1 = &SomeStruct;
++ forget(&*reference1);
++
++ let reference2 = &mut SomeStruct;
++ forget(reference2);
++
++ let ref reference3 = SomeStruct;
++ forget(reference3);
++}
++
++#[allow(dead_code)]
++fn test_generic_fn_forget<T>(val: T) {
++ forget(&val);
++ forget(val); //OK
++}
++
++#[allow(dead_code)]
++fn test_similarly_named_function() {
++ fn forget<T>(_val: T) {}
++ forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name
++ std::mem::forget(&SomeStruct);
++}
++
++#[derive(Copy, Clone)]
++pub struct Error;
++fn produce_half_owl_error() -> Result<(), Error> {
++ Ok(())
++}
++
++fn produce_half_owl_ok() -> Result<bool, ()> {
++ Ok(true)
++}
--- /dev/null
--- /dev/null
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:9:5
++ |
++LL | forget(&SomeStruct);
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::forget-ref` implied by `-D warnings`
++note: argument has type `&SomeStruct`
++ --> $DIR/forget_ref.rs:9:12
++ |
++LL | forget(&SomeStruct);
++ | ^^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:12:5
++ |
++LL | forget(&owned);
++ | ^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/forget_ref.rs:12:12
++ |
++LL | forget(&owned);
++ | ^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:13:5
++ |
++LL | forget(&&owned);
++ | ^^^^^^^^^^^^^^^
++ |
++note: argument has type `&&SomeStruct`
++ --> $DIR/forget_ref.rs:13:12
++ |
++LL | forget(&&owned);
++ | ^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:14:5
++ |
++LL | forget(&mut owned);
++ | ^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&mut SomeStruct`
++ --> $DIR/forget_ref.rs:14:12
++ |
++LL | forget(&mut owned);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:18:5
++ |
++LL | forget(&*reference1);
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/forget_ref.rs:18:12
++ |
++LL | forget(&*reference1);
++ | ^^^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:21:5
++ |
++LL | forget(reference2);
++ | ^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&mut SomeStruct`
++ --> $DIR/forget_ref.rs:21:12
++ |
++LL | forget(reference2);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:24:5
++ |
++LL | forget(reference3);
++ | ^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/forget_ref.rs:24:12
++ |
++LL | forget(reference3);
++ | ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:29:5
++ |
++LL | forget(&val);
++ | ^^^^^^^^^^^^
++ |
++note: argument has type `&T`
++ --> $DIR/forget_ref.rs:29:12
++ |
++LL | forget(&val);
++ | ^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++ --> $DIR/forget_ref.rs:37:5
++ |
++LL | std::mem::forget(&SomeStruct);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: argument has type `&SomeStruct`
++ --> $DIR/forget_ref.rs:37:22
++ |
++LL | std::mem::forget(&SomeStruct);
++ | ^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::print_literal, clippy::redundant_clone)]
++#![warn(clippy::useless_format)]
++
++struct Foo(pub String);
++
++macro_rules! foo {
++ ($($t:tt)*) => (Foo(format!($($t)*)))
++}
++
++fn main() {
++ "foo".to_string();
++ "{}".to_string();
++ "{} abc {}".to_string();
++ "foo {}\n\" bar".to_string();
++
++ "foo".to_string();
++ format!("{:?}", "foo"); // Don't warn about `Debug`.
++ format!("{:8}", "foo");
++ format!("{:width$}", "foo", width = 8);
++ "foo".to_string(); // Warn when the format makes no difference.
++ "foo".to_string(); // Warn when the format makes no difference.
++ format!("foo {}", "bar");
++ format!("{} bar", "foo");
++
++ let arg: String = "".to_owned();
++ arg.to_string();
++ format!("{:?}", arg); // Don't warn about debug.
++ format!("{:8}", arg);
++ format!("{:width$}", arg, width = 8);
++ arg.to_string(); // Warn when the format makes no difference.
++ arg.to_string(); // Warn when the format makes no difference.
++ format!("foo {}", arg);
++ format!("{} bar", arg);
++
++ // We don’t want to warn for non-string args; see issue #697.
++ format!("{}", 42);
++ format!("{:?}", 42);
++ format!("{:+}", 42);
++ format!("foo {}", 42);
++ format!("{} bar", 42);
++
++ // We only want to warn about `format!` itself.
++ println!("foo");
++ println!("{}", "foo");
++ println!("foo {}", "foo");
++ println!("{}", 42);
++ println!("foo {}", 42);
++
++ // A `format!` inside a macro should not trigger a warning.
++ foo!("should not warn");
++
++ // Precision on string means slicing without panicking on size.
++ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
++ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
++ format!("{:.prec$}", "foo", prec = 1);
++ format!("{:.prec$}", "foo", prec = 10);
++
++ 42.to_string();
++ let x = std::path::PathBuf::from("/bar/foo/qux");
++ x.display().to_string();
++
++ // False positive
++ let a = "foo".to_string();
++ let _ = Some(a + "bar");
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::print_literal, clippy::redundant_clone)]
++#![warn(clippy::useless_format)]
++
++struct Foo(pub String);
++
++macro_rules! foo {
++ ($($t:tt)*) => (Foo(format!($($t)*)))
++}
++
++fn main() {
++ format!("foo");
++ format!("{{}}");
++ format!("{{}} abc {{}}");
++ format!(
++ r##"foo {{}}
++" bar"##
++ );
++
++ format!("{}", "foo");
++ format!("{:?}", "foo"); // Don't warn about `Debug`.
++ format!("{:8}", "foo");
++ format!("{:width$}", "foo", width = 8);
++ format!("{:+}", "foo"); // Warn when the format makes no difference.
++ format!("{:<}", "foo"); // Warn when the format makes no difference.
++ format!("foo {}", "bar");
++ format!("{} bar", "foo");
++
++ let arg: String = "".to_owned();
++ format!("{}", arg);
++ format!("{:?}", arg); // Don't warn about debug.
++ format!("{:8}", arg);
++ format!("{:width$}", arg, width = 8);
++ format!("{:+}", arg); // Warn when the format makes no difference.
++ format!("{:<}", arg); // Warn when the format makes no difference.
++ format!("foo {}", arg);
++ format!("{} bar", arg);
++
++ // We don’t want to warn for non-string args; see issue #697.
++ format!("{}", 42);
++ format!("{:?}", 42);
++ format!("{:+}", 42);
++ format!("foo {}", 42);
++ format!("{} bar", 42);
++
++ // We only want to warn about `format!` itself.
++ println!("foo");
++ println!("{}", "foo");
++ println!("foo {}", "foo");
++ println!("{}", 42);
++ println!("foo {}", 42);
++
++ // A `format!` inside a macro should not trigger a warning.
++ foo!("should not warn");
++
++ // Precision on string means slicing without panicking on size.
++ format!("{:.1}", "foo"); // Could be `"foo"[..1]`
++ format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
++ format!("{:.prec$}", "foo", prec = 1);
++ format!("{:.prec$}", "foo", prec = 10);
++
++ format!("{}", 42.to_string());
++ let x = std::path::PathBuf::from("/bar/foo/qux");
++ format!("{}", x.display().to_string());
++
++ // False positive
++ let a = "foo".to_string();
++ let _ = Some(format!("{}", a + "bar"));
++}
--- /dev/null
--- /dev/null
++error: useless use of `format!`
++ --> $DIR/format.rs:13:5
++ |
++LL | format!("foo");
++ | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++ |
++ = note: `-D clippy::useless-format` implied by `-D warnings`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:14:5
++ |
++LL | format!("{{}}");
++ | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:15:5
++ |
++LL | format!("{{}} abc {{}}");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:16:5
++ |
++LL | / format!(
++LL | | r##"foo {{}}
++LL | | " bar"##
++LL | | );
++ | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:21:5
++ |
++LL | format!("{}", "foo");
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:25:5
++ |
++LL | format!("{:+}", "foo"); // Warn when the format makes no difference.
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:26:5
++ |
++LL | format!("{:<}", "foo"); // Warn when the format makes no difference.
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:31:5
++ |
++LL | format!("{}", arg);
++ | ^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:35:5
++ |
++LL | format!("{:+}", arg); // Warn when the format makes no difference.
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:36:5
++ |
++LL | format!("{:<}", arg); // Warn when the format makes no difference.
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:63:5
++ |
++LL | format!("{}", 42.to_string());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:65:5
++ |
++LL | format!("{}", x.display().to_string());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string();`
++
++error: useless use of `format!`
++ --> $DIR/format.rs:69:18
++ |
++LL | let _ = Some(format!("{}", a + "bar"));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(unused_variables)]
++#![allow(unused_assignments)]
++#![allow(clippy::if_same_then_else)]
++#![allow(clippy::deref_addrof)]
++
++fn foo() -> bool {
++ true
++}
++
++#[rustfmt::skip]
++fn main() {
++ // weird `else` formatting:
++ if foo() {
++ } {
++ }
++
++ if foo() {
++ } if foo() {
++ }
++
++ let _ = { // if as the last expression
++ let _ = 0;
++
++ if foo() {
++ } if foo() {
++ }
++ else {
++ }
++ };
++
++ let _ = { // if in the middle of a block
++ if foo() {
++ } if foo() {
++ }
++ else {
++ }
++
++ let _ = 0;
++ };
++
++ if foo() {
++ } else
++ {
++ }
++
++ if foo() {
++ }
++ else
++ {
++ }
++
++ if foo() {
++ } else
++ if foo() { // the span of the above error should continue here
++ }
++
++ if foo() {
++ }
++ else
++ if foo() { // the span of the above error should continue here
++ }
++
++ // those are ok:
++ if foo() {
++ }
++ {
++ }
++
++ if foo() {
++ } else {
++ }
++
++ if foo() {
++ }
++ else {
++ }
++
++ if foo() {
++ }
++ if foo() {
++ }
++
++ if foo() {
++ } else if foo() {
++ }
++
++ if foo() {
++ }
++ else if foo() {
++ }
++
++ if foo() {
++ }
++ else if
++ foo() {}
++
++ // weird op_eq formatting:
++ let mut a = 42;
++ a =- 35;
++ a =* &191;
++
++ let mut b = true;
++ b =! false;
++
++ // those are ok:
++ a = -35;
++ a = *&191;
++ b = !false;
++
++ // possible missing comma in an array
++ let _ = &[
++ -1, -2, -3 // <= no comma here
++ -4, -5, -6
++ ];
++ let _ = &[
++ -1, -2, -3 // <= no comma here
++ *4, -5, -6
++ ];
++
++ // those are ok:
++ let _ = &[
++ -1, -2, -3,
++ -4, -5, -6
++ ];
++ let _ = &[
++ -1, -2, -3,
++ -4, -5, -6,
++ ];
++ let _ = &[
++ 1 + 2, 3 +
++ 4, 5 + 6,
++ ];
++
++ // don't lint for bin op without unary equiv
++ // issue 3244
++ vec![
++ 1
++ / 2,
++ ];
++ // issue 3396
++ vec![
++ true
++ | false,
++ ];
++
++ // don't lint if the indentation suggests not to
++ let _ = &[
++ 1 + 2, 3
++ - 4, 5
++ ];
++ // lint if it doesnt
++ let _ = &[
++ -1
++ -4,
++ ];
++}
--- /dev/null
--- /dev/null
++error: this looks like an `else {..}` but the `else` is missing
++ --> $DIR/formatting.rs:15:6
++ |
++LL | } {
++ | ^
++ |
++ = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
++ = note: to remove this lint, add the missing `else` or add a new line before the next block
++
++error: this looks like an `else if` but the `else` is missing
++ --> $DIR/formatting.rs:19:6
++ |
++LL | } if foo() {
++ | ^
++ |
++ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this looks like an `else if` but the `else` is missing
++ --> $DIR/formatting.rs:26:10
++ |
++LL | } if foo() {
++ | ^
++ |
++ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this looks like an `else if` but the `else` is missing
++ --> $DIR/formatting.rs:34:10
++ |
++LL | } if foo() {
++ | ^
++ |
++ = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this is an `else {..}` but the formatting might hide it
++ --> $DIR/formatting.rs:43:6
++ |
++LL | } else
++ | ______^
++LL | | {
++ | |____^
++ |
++ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
++
++error: this is an `else {..}` but the formatting might hide it
++ --> $DIR/formatting.rs:48:6
++ |
++LL | }
++ | ______^
++LL | | else
++LL | | {
++ | |____^
++ |
++ = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
++
++error: this is an `else if` but the formatting might hide it
++ --> $DIR/formatting.rs:54:6
++ |
++LL | } else
++ | ______^
++LL | | if foo() { // the span of the above error should continue here
++ | |____^
++ |
++ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
++
++error: this is an `else if` but the formatting might hide it
++ --> $DIR/formatting.rs:59:6
++ |
++LL | }
++ | ______^
++LL | | else
++LL | | if foo() { // the span of the above error should continue here
++ | |____^
++ |
++ = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
++
++error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
++ --> $DIR/formatting.rs:100:6
++ |
++LL | a =- 35;
++ | ^^^^
++ |
++ = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
++ = note: to remove this lint, use either `-=` or `= -`
++
++error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
++ --> $DIR/formatting.rs:101:6
++ |
++LL | a =* &191;
++ | ^^^^
++ |
++ = note: to remove this lint, use either `*=` or `= *`
++
++error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
++ --> $DIR/formatting.rs:104:6
++ |
++LL | b =! false;
++ | ^^^^
++ |
++ = note: to remove this lint, use either `!=` or `= !`
++
++error: possibly missing a comma here
++ --> $DIR/formatting.rs:113:19
++ |
++LL | -1, -2, -3 // <= no comma here
++ | ^
++ |
++ = note: `-D clippy::possible-missing-comma` implied by `-D warnings`
++ = note: to remove this lint, add a comma or write the expr in a single line
++
++error: possibly missing a comma here
++ --> $DIR/formatting.rs:117:19
++ |
++LL | -1, -2, -3 // <= no comma here
++ | ^
++ |
++ = note: to remove this lint, add a comma or write the expr in a single line
++
++error: possibly missing a comma here
++ --> $DIR/formatting.rs:154:11
++ |
++LL | -1
++ | ^
++ |
++ = note: to remove this lint, add a comma or write the expr in a single line
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(dead_code)]
++#![allow(unused_unsafe, clippy::missing_safety_doc)]
++
++// TOO_MANY_ARGUMENTS
++fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++
++fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++
++#[rustfmt::skip]
++fn bad_multiline(
++ one: u32,
++ two: u32,
++ three: &str,
++ four: bool,
++ five: f32,
++ six: f32,
++ seven: bool,
++ eight: ()
++) {
++ let _one = one;
++ let _two = two;
++ let _three = three;
++ let _four = four;
++ let _five = five;
++ let _six = six;
++ let _seven = seven;
++}
++
++// don't lint extern fns
++extern "C" fn extern_fn(
++ _one: u32,
++ _two: u32,
++ _three: *const u8,
++ _four: bool,
++ _five: f32,
++ _six: f32,
++ _seven: bool,
++ _eight: *const std::ffi::c_void,
++) {
++}
++
++pub trait Foo {
++ fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool);
++ fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
++
++ fn ptr(p: *const u8);
++}
++
++pub struct Bar;
++
++impl Bar {
++ fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++ fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++}
++
++// ok, we don’t want to warn implementations
++impl Foo for Bar {
++ fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++ fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++
++ fn ptr(p: *const u8) {
++ println!("{}", unsafe { *p });
++ println!("{:?}", unsafe { p.as_ref() });
++ unsafe { std::ptr::read(p) };
++ }
++}
++
++// NOT_UNSAFE_PTR_ARG_DEREF
++
++fn private(p: *const u8) {
++ println!("{}", unsafe { *p });
++}
++
++pub fn public(p: *const u8) {
++ println!("{}", unsafe { *p });
++ println!("{:?}", unsafe { p.as_ref() });
++ unsafe { std::ptr::read(p) };
++}
++
++impl Bar {
++ fn private(self, p: *const u8) {
++ println!("{}", unsafe { *p });
++ }
++
++ pub fn public(self, p: *const u8) {
++ println!("{}", unsafe { *p });
++ println!("{:?}", unsafe { p.as_ref() });
++ unsafe { std::ptr::read(p) };
++ }
++
++ pub fn public_ok(self, p: *const u8) {
++ if !p.is_null() {
++ println!("{:p}", p);
++ }
++ }
++
++ pub unsafe fn public_unsafe(self, p: *const u8) {
++ println!("{}", unsafe { *p });
++ println!("{:?}", unsafe { p.as_ref() });
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this function has too many arguments (8/7)
++ --> $DIR/functions.rs:8:1
++ |
++LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::too-many-arguments` implied by `-D warnings`
++
++error: this function has too many arguments (8/7)
++ --> $DIR/functions.rs:11:1
++ |
++LL | / fn bad_multiline(
++LL | | one: u32,
++LL | | two: u32,
++LL | | three: &str,
++... |
++LL | | eight: ()
++LL | | ) {
++ | |__^
++
++error: this function has too many arguments (8/7)
++ --> $DIR/functions.rs:45:5
++ |
++LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this function has too many arguments (8/7)
++ --> $DIR/functions.rs:54:5
++ |
++LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:63:34
++ |
++LL | println!("{}", unsafe { *p });
++ | ^
++ |
++ = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings`
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:64:35
++ |
++LL | println!("{:?}", unsafe { p.as_ref() });
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:65:33
++ |
++LL | unsafe { std::ptr::read(p) };
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:76:30
++ |
++LL | println!("{}", unsafe { *p });
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:77:31
++ |
++LL | println!("{:?}", unsafe { p.as_ref() });
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:78:29
++ |
++LL | unsafe { std::ptr::read(p) };
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:87:34
++ |
++LL | println!("{}", unsafe { *p });
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:88:35
++ |
++LL | println!("{:?}", unsafe { p.as_ref() });
++ | ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++ --> $DIR/functions.rs:89:33
++ |
++LL | unsafe { std::ptr::read(p) };
++ | ^
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::too_many_lines)]
++
++fn good_lines() {
++ /* println!("This is good."); */
++ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* */ // println!("This is good.");
++ /* println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good."); */
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++ println!("This is good.");
++}
++
++fn bad_lines() {
++ println!("Dont get confused by braces: {{}}");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++ println!("This is bad.");
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: This function has a large number of lines.
++ --> $DIR/functions_maxlines.rs:58:1
++ |
++LL | / fn bad_lines() {
++LL | | println!("Dont get confused by braces: {{}}");
++LL | | println!("This is bad.");
++LL | | println!("This is bad.");
++... |
++LL | | println!("This is bad.");
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::too-many-lines` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// edition:2018
++#![warn(clippy::future_not_send)]
++
++use std::cell::Cell;
++use std::rc::Rc;
++use std::sync::Arc;
++
++async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ async { true }.await
++}
++
++pub async fn public_future(rc: Rc<[u8]>) {
++ async { true }.await;
++}
++
++pub async fn public_send(arc: Arc<[u8]>) -> bool {
++ async { false }.await
++}
++
++async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ true
++}
++
++pub async fn public_future2(rc: Rc<[u8]>) {}
++
++pub async fn public_send2(arc: Arc<[u8]>) -> bool {
++ false
++}
++
++struct Dummy {
++ rc: Rc<[u8]>,
++}
++
++impl Dummy {
++ async fn private_future(&self) -> usize {
++ async { true }.await;
++ self.rc.len()
++ }
++
++ pub async fn public_future(&self) {
++ self.private_future().await;
++ }
++
++ pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
++ async { false }
++ }
++}
++
++async fn generic_future<T>(t: T) -> T
++where
++ T: Send,
++{
++ let rt = &t;
++ async { true }.await;
++ t
++}
++
++async fn generic_future_send<T>(t: T)
++where
++ T: Send,
++{
++ async { true }.await;
++}
++
++async fn unclear_future<T>(t: T) {}
++
++fn main() {
++ let rc = Rc::new([1, 2, 3]);
++ private_future(rc.clone(), &Cell::new(42));
++ public_future(rc.clone());
++ let arc = Arc::new([4, 5, 6]);
++ public_send(arc);
++ generic_future(42);
++ generic_future_send(42);
++
++ let dummy = Dummy { rc };
++ dummy.public_future();
++ dummy.public_send();
++}
--- /dev/null
--- /dev/null
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:8:62
++ |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ | ^^^^ future returned by `private_future` is not `Send`
++ |
++ = note: `-D clippy::future-not-send` implied by `-D warnings`
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:9:5
++ |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
++LL | async { true }.await
++ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
++LL | }
++ | - `rc` is later dropped here
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:9:5
++ |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ | ---- has type `&std::cell::Cell<usize>` which is not `Send`
++LL | async { true }.await
++ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later
++LL | }
++ | - `cell` is later dropped here
++ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:12:42
++ |
++LL | pub async fn public_future(rc: Rc<[u8]>) {
++ | ^ future returned by `public_future` is not `Send`
++ |
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:13:5
++ |
++LL | pub async fn public_future(rc: Rc<[u8]>) {
++ | -- has type `std::rc::Rc<[u8]>` which is not `Send`
++LL | async { true }.await;
++ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
++LL | }
++ | - `rc` is later dropped here
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:20:63
++ |
++LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++ | ^^^^
++ |
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++ = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:24:43
++ |
++LL | pub async fn public_future2(rc: Rc<[u8]>) {}
++ | ^
++ |
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:35:39
++ |
++LL | async fn private_future(&self) -> usize {
++ | ^^^^^ future returned by `private_future` is not `Send`
++ |
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:36:9
++ |
++LL | async fn private_future(&self) -> usize {
++ | ----- has type `&Dummy` which is not `Send`
++LL | async { true }.await;
++ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
++LL | self.rc.len()
++LL | }
++ | - `&self` is later dropped here
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:40:39
++ |
++LL | pub async fn public_future(&self) {
++ | ^ future returned by `public_future` is not `Send`
++ |
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:41:9
++ |
++LL | pub async fn public_future(&self) {
++ | ----- has type `&Dummy` which is not `Send`
++LL | self.private_future().await;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
++LL | }
++ | - `&self` is later dropped here
++ = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:49:37
++ |
++LL | async fn generic_future<T>(t: T) -> T
++ | ^ future returned by `generic_future` is not `Send`
++ |
++note: future is not `Send` as this value is used across an await
++ --> $DIR/future_not_send.rs:54:5
++ |
++LL | let rt = &t;
++ | -- has type `&T` which is not `Send`
++LL | async { true }.await;
++ | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later
++LL | t
++LL | }
++ | - `rt` is later dropped here
++ = note: `T` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++ --> $DIR/future_not_send.rs:65:34
++ |
++LL | async fn unclear_future<T>(t: T) {}
++ | ^
++ |
++ = note: `T` doesn't implement `std::marker::Send`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::get_last_with_len)]
++
++fn dont_use_last() {
++ let x = vec![2, 3, 5];
++ let _ = x.last(); // ~ERROR Use x.last()
++}
++
++fn indexing_two_from_end() {
++ let x = vec![2, 3, 5];
++ let _ = x.get(x.len() - 2);
++}
++
++fn index_into_last() {
++ let x = vec![2, 3, 5];
++ let _ = x[x.len() - 1];
++}
++
++fn use_last_with_different_vec_length() {
++ let x = vec![2, 3, 5];
++ let y = vec!['a', 'b', 'c'];
++ let _ = x.get(y.len() - 1);
++}
++
++fn main() {
++ dont_use_last();
++ indexing_two_from_end();
++ index_into_last();
++ use_last_with_different_vec_length();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::get_last_with_len)]
++
++fn dont_use_last() {
++ let x = vec![2, 3, 5];
++ let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
++}
++
++fn indexing_two_from_end() {
++ let x = vec![2, 3, 5];
++ let _ = x.get(x.len() - 2);
++}
++
++fn index_into_last() {
++ let x = vec![2, 3, 5];
++ let _ = x[x.len() - 1];
++}
++
++fn use_last_with_different_vec_length() {
++ let x = vec![2, 3, 5];
++ let y = vec!['a', 'b', 'c'];
++ let _ = x.get(y.len() - 1);
++}
++
++fn main() {
++ dont_use_last();
++ indexing_two_from_end();
++ index_into_last();
++ use_last_with_different_vec_length();
++}
--- /dev/null
--- /dev/null
++error: accessing last element with `x.get(x.len() - 1)`
++ --> $DIR/get_last_with_len.rs:7:13
++ |
++LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
++ | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()`
++ |
++ = note: `-D clippy::get-last-with-len` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_mut)]
++#![deny(clippy::get_unwrap)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++
++struct GetFalsePositive {
++ arr: [u32; 3],
++}
++
++impl GetFalsePositive {
++ fn get(&self, pos: usize) -> Option<&u32> {
++ self.arr.get(pos)
++ }
++ fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
++ self.arr.get_mut(pos)
++ }
++}
++
++fn main() {
++ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++ let mut some_slice = &mut [0, 1, 2, 3];
++ let mut some_vec = vec![0, 1, 2, 3];
++ let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
++ let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++ let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++ let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
++
++ {
++ // Test `get().unwrap()`
++ let _ = &boxed_slice[1];
++ let _ = &some_slice[0];
++ let _ = &some_vec[0];
++ let _ = &some_vecdeque[0];
++ let _ = &some_hashmap[&1];
++ let _ = &some_btreemap[&1];
++ let _ = false_positive.get(0).unwrap();
++ // Test with deref
++ let _: u8 = boxed_slice[1];
++ }
++
++ {
++ // Test `get_mut().unwrap()`
++ boxed_slice[0] = 1;
++ some_slice[0] = 1;
++ some_vec[0] = 1;
++ some_vecdeque[0] = 1;
++ // Check false positives
++ *some_hashmap.get_mut(&1).unwrap() = 'b';
++ *some_btreemap.get_mut(&1).unwrap() = 'b';
++ *false_positive.get_mut(0).unwrap() = 1;
++ }
++
++ {
++ // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
++ let _ = some_vec[0..1].to_vec();
++ let _ = some_vec[0..1].to_vec();
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_mut)]
++#![deny(clippy::get_unwrap)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++
++struct GetFalsePositive {
++ arr: [u32; 3],
++}
++
++impl GetFalsePositive {
++ fn get(&self, pos: usize) -> Option<&u32> {
++ self.arr.get(pos)
++ }
++ fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
++ self.arr.get_mut(pos)
++ }
++}
++
++fn main() {
++ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++ let mut some_slice = &mut [0, 1, 2, 3];
++ let mut some_vec = vec![0, 1, 2, 3];
++ let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
++ let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++ let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++ let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
++
++ {
++ // Test `get().unwrap()`
++ let _ = boxed_slice.get(1).unwrap();
++ let _ = some_slice.get(0).unwrap();
++ let _ = some_vec.get(0).unwrap();
++ let _ = some_vecdeque.get(0).unwrap();
++ let _ = some_hashmap.get(&1).unwrap();
++ let _ = some_btreemap.get(&1).unwrap();
++ let _ = false_positive.get(0).unwrap();
++ // Test with deref
++ let _: u8 = *boxed_slice.get(1).unwrap();
++ }
++
++ {
++ // Test `get_mut().unwrap()`
++ *boxed_slice.get_mut(0).unwrap() = 1;
++ *some_slice.get_mut(0).unwrap() = 1;
++ *some_vec.get_mut(0).unwrap() = 1;
++ *some_vecdeque.get_mut(0).unwrap() = 1;
++ // Check false positives
++ *some_hashmap.get_mut(&1).unwrap() = 'b';
++ *some_btreemap.get_mut(&1).unwrap() = 'b';
++ *false_positive.get_mut(0).unwrap() = 1;
++ }
++
++ {
++ // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
++ let _ = some_vec.get(0..1).unwrap().to_vec();
++ let _ = some_vec.get_mut(0..1).unwrap().to_vec();
++ }
++}
--- /dev/null
--- /dev/null
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:34:17
++ |
++LL | let _ = boxed_slice.get(1).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
++ |
++note: the lint level is defined here
++ --> $DIR/get_unwrap.rs:3:9
++ |
++LL | #![deny(clippy::get_unwrap)]
++ | ^^^^^^^^^^^^^^^^^^
++
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:35:17
++ |
++LL | let _ = some_slice.get(0).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
++
++error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:36:17
++ |
++LL | let _ = some_vec.get(0).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
++
++error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:37:17
++ |
++LL | let _ = some_vecdeque.get(0).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
++
++error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:38:17
++ |
++LL | let _ = some_hashmap.get(&1).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
++
++error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:39:17
++ |
++LL | let _ = some_btreemap.get(&1).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
++
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:42:21
++ |
++LL | let _: u8 = *boxed_slice.get(1).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
++
++error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:47:9
++ |
++LL | *boxed_slice.get_mut(0).unwrap() = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
++
++error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:48:9
++ |
++LL | *some_slice.get_mut(0).unwrap() = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
++
++error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:49:9
++ |
++LL | *some_vec.get_mut(0).unwrap() = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
++
++error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:50:9
++ |
++LL | *some_vecdeque.get_mut(0).unwrap() = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
++
++error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:59:17
++ |
++LL | let _ = some_vec.get(0..1).unwrap().to_vec();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
++
++error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
++ --> $DIR/get_unwrap.rs:60:17
++ |
++LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::identity_conversion)]
++
++fn test_generic<T: Copy>(val: T) -> T {
++ let _ = val;
++ val
++}
++
++fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
++ // ok
++ let _: i32 = val.into();
++ let _: U = val.into();
++ let _ = U::from(val);
++}
++
++fn test_questionmark() -> Result<(), ()> {
++ {
++ let _: i32 = 0i32;
++ Ok(Ok(()))
++ }??;
++ Ok(())
++}
++
++fn test_issue_3913() -> Result<(), std::io::Error> {
++ use std::fs;
++ use std::path::Path;
++
++ let path = Path::new(".");
++ for _ in fs::read_dir(path)? {}
++
++ Ok(())
++}
++
++fn main() {
++ test_generic(10i32);
++ test_generic2::<i32, i32>(10i32);
++ test_questionmark().unwrap();
++ test_issue_3913().unwrap();
++
++ let _: String = "foo".into();
++ let _: String = From::from("foo");
++ let _ = String::from("foo");
++ #[allow(clippy::identity_conversion)]
++ {
++ let _: String = "foo".into();
++ let _ = String::from("foo");
++ let _ = "".lines().into_iter();
++ }
++
++ let _: String = "foo".to_string();
++ let _: String = "foo".to_string();
++ let _ = "foo".to_string();
++ let _ = format!("A: {:04}", 123);
++ let _ = "".lines();
++ let _ = vec![1, 2, 3].into_iter();
++ let _: String = format!("Hello {}", "world");
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::identity_conversion)]
++
++fn test_generic<T: Copy>(val: T) -> T {
++ let _ = T::from(val);
++ val.into()
++}
++
++fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
++ // ok
++ let _: i32 = val.into();
++ let _: U = val.into();
++ let _ = U::from(val);
++}
++
++fn test_questionmark() -> Result<(), ()> {
++ {
++ let _: i32 = 0i32.into();
++ Ok(Ok(()))
++ }??;
++ Ok(())
++}
++
++fn test_issue_3913() -> Result<(), std::io::Error> {
++ use std::fs;
++ use std::path::Path;
++
++ let path = Path::new(".");
++ for _ in fs::read_dir(path)? {}
++
++ Ok(())
++}
++
++fn main() {
++ test_generic(10i32);
++ test_generic2::<i32, i32>(10i32);
++ test_questionmark().unwrap();
++ test_issue_3913().unwrap();
++
++ let _: String = "foo".into();
++ let _: String = From::from("foo");
++ let _ = String::from("foo");
++ #[allow(clippy::identity_conversion)]
++ {
++ let _: String = "foo".into();
++ let _ = String::from("foo");
++ let _ = "".lines().into_iter();
++ }
++
++ let _: String = "foo".to_string().into();
++ let _: String = From::from("foo".to_string());
++ let _ = String::from("foo".to_string());
++ let _ = String::from(format!("A: {:04}", 123));
++ let _ = "".lines().into_iter();
++ let _ = vec![1, 2, 3].into_iter().into_iter();
++ let _: String = format!("Hello {}", "world").into();
++}
--- /dev/null
--- /dev/null
++error: identical conversion
++ --> $DIR/identity_conversion.rs:6:13
++ |
++LL | let _ = T::from(val);
++ | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val`
++ |
++note: the lint level is defined here
++ --> $DIR/identity_conversion.rs:3:9
++ |
++LL | #![deny(clippy::identity_conversion)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:7:5
++ |
++LL | val.into()
++ | ^^^^^^^^^^ help: consider removing `.into()`: `val`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:19:22
++ |
++LL | let _: i32 = 0i32.into();
++ | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:51:21
++ |
++LL | let _: String = "foo".to_string().into();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:52:21
++ |
++LL | let _: String = From::from("foo".to_string());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:53:13
++ |
++LL | let _ = String::from("foo".to_string());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:54:13
++ |
++LL | let _ = String::from(format!("A: {:04}", 123));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:55:13
++ |
++LL | let _ = "".lines().into_iter();
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:56:13
++ |
++LL | let _ = vec![1, 2, 3].into_iter().into_iter();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()`
++
++error: identical conversion
++ --> $DIR/identity_conversion.rs:57:21
++ |
++LL | let _: String = format!("Hello {}", "world").into();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++const ONE: i64 = 1;
++const NEG_ONE: i64 = -1;
++const ZERO: i64 = 0;
++
++#[allow(
++ clippy::eq_op,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::double_parens
++)]
++#[warn(clippy::identity_op)]
++#[rustfmt::skip]
++fn main() {
++ let x = 0;
++
++ x + 0;
++ x + (1 - 1);
++ x + 1;
++ 0 + x;
++ 1 + x;
++ x - ZERO; //no error, as we skip lookups (for now)
++ x | (0);
++ ((ZERO)) | x; //no error, as we skip lookups (for now)
++
++ x * 1;
++ 1 * x;
++ x / ONE; //no error, as we skip lookups (for now)
++
++ x / 2; //no false positive
++
++ x & NEG_ONE; //no error, as we skip lookups (for now)
++ -1 & x;
++
++ let u: u8 = 0;
++ u & 255;
++}
--- /dev/null
--- /dev/null
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:16:5
++ |
++LL | x + 0;
++ | ^^^^^
++ |
++ = note: `-D clippy::identity-op` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:17:5
++ |
++LL | x + (1 - 1);
++ | ^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:19:5
++ |
++LL | 0 + x;
++ | ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:22:5
++ |
++LL | x | (0);
++ | ^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:25:5
++ |
++LL | x * 1;
++ | ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:26:5
++ |
++LL | 1 * x;
++ | ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++ --> $DIR/identity_op.rs:32:5
++ |
++LL | -1 & x;
++ | ^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `u`
++ --> $DIR/identity_op.rs:35:5
++ |
++LL | u & 255;
++ | ^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::if_let_mutex)]
++
++use std::ops::Deref;
++use std::sync::Mutex;
++
++fn do_stuff<T>(_: T) {}
++
++fn if_let() {
++ let m = Mutex::new(1_u8);
++ if let Err(locked) = m.lock() {
++ do_stuff(locked);
++ } else {
++ let lock = m.lock().unwrap();
++ do_stuff(lock);
++ };
++}
++
++// This is the most common case as the above case is pretty
++// contrived.
++fn if_let_option() {
++ let m = Mutex::new(Some(0_u8));
++ if let Some(locked) = m.lock().unwrap().deref() {
++ do_stuff(locked);
++ } else {
++ let lock = m.lock().unwrap();
++ do_stuff(lock);
++ };
++}
++
++// When mutexs are different don't warn
++fn if_let_different_mutex() {
++ let m = Mutex::new(Some(0_u8));
++ let other = Mutex::new(None::<u8>);
++ if let Some(locked) = m.lock().unwrap().deref() {
++ do_stuff(locked);
++ } else {
++ let lock = other.lock().unwrap();
++ do_stuff(lock);
++ };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
++ --> $DIR/if_let_mutex.rs:10:5
++ |
++LL | / if let Err(locked) = m.lock() {
++LL | | do_stuff(locked);
++LL | | } else {
++LL | | let lock = m.lock().unwrap();
++LL | | do_stuff(lock);
++LL | | };
++ | |_____^
++ |
++ = note: `-D clippy::if-let-mutex` implied by `-D warnings`
++ = help: move the lock call outside of the `if let ...` expression
++
++error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
++ --> $DIR/if_let_mutex.rs:22:5
++ |
++LL | / if let Some(locked) = m.lock().unwrap().deref() {
++LL | | do_stuff(locked);
++LL | | } else {
++LL | | let lock = m.lock().unwrap();
++LL | | do_stuff(lock);
++LL | | };
++ | |_____^
++ |
++ = help: move the lock call outside of the `if let ...` expression
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::if_let_some_result)]
++
++fn str_to_int(x: &str) -> i32 {
++ if let Ok(y) = x.parse() {
++ y
++ } else {
++ 0
++ }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++ if let Ok(y) = x.parse() {
++ y
++ } else {
++ 0
++ }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++ {
++ if let Ok(y) = x . parse() {
++ return y;
++ };
++ 0
++ }
++}
++
++fn main() {
++ let _ = str_to_int("1");
++ let _ = str_to_int_ok("2");
++ let _ = strange_some_no_else("3");
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::if_let_some_result)]
++
++fn str_to_int(x: &str) -> i32 {
++ if let Some(y) = x.parse().ok() {
++ y
++ } else {
++ 0
++ }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++ if let Ok(y) = x.parse() {
++ y
++ } else {
++ 0
++ }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++ {
++ if let Some(y) = x . parse() . ok () {
++ return y;
++ };
++ 0
++ }
++}
++
++fn main() {
++ let _ = str_to_int("1");
++ let _ = str_to_int_ok("2");
++ let _ = strange_some_no_else("3");
++}
--- /dev/null
--- /dev/null
++error: Matching on `Some` with `ok()` is redundant
++ --> $DIR/if_let_some_result.rs:6:5
++ |
++LL | if let Some(y) = x.parse().ok() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::if-let-some-result` implied by `-D warnings`
++help: Consider matching on `Ok(y)` and removing the call to `ok` instead
++ |
++LL | if let Ok(y) = x.parse() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Matching on `Some` with `ok()` is redundant
++ --> $DIR/if_let_some_result.rs:24:9
++ |
++LL | if let Some(y) = x . parse() . ok () {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: Consider matching on `Ok(y)` and removing the call to `ok` instead
++ |
++LL | if let Ok(y) = x . parse() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![warn(clippy::if_not_else)]
++
++fn bla() -> bool {
++ unimplemented!()
++}
++
++fn main() {
++ if !bla() {
++ println!("Bugs");
++ } else {
++ println!("Bunny");
++ }
++ if 4 != 5 {
++ println!("Bugs");
++ } else {
++ println!("Bunny");
++ }
++}
--- /dev/null
--- /dev/null
++error: Unnecessary boolean `not` operation
++ --> $DIR/if_not_else.rs:9:5
++ |
++LL | / if !bla() {
++LL | | println!("Bugs");
++LL | | } else {
++LL | | println!("Bunny");
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::if-not-else` implied by `-D warnings`
++ = help: remove the `!` and swap the blocks of the `if`/`else`
++
++error: Unnecessary `!=` operation
++ --> $DIR/if_not_else.rs:14:5
++ |
++LL | / if 4 != 5 {
++LL | | println!("Bugs");
++LL | | } else {
++LL | | println!("Bunny");
++LL | | }
++ | |_____^
++ |
++ = help: change to `==` and swap the blocks of the `if`/`else`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::if_same_then_else)]
++#![allow(
++ clippy::blacklisted_name,
++ clippy::eq_op,
++ clippy::never_loop,
++ clippy::no_effect,
++ clippy::unused_unit,
++ clippy::zero_divided_by_zero
++)]
++
++struct Foo {
++ bar: u8,
++}
++
++fn foo() -> bool {
++ unimplemented!()
++}
++
++fn if_same_then_else() {
++ if true {
++ Foo { bar: 42 };
++ 0..10;
++ ..;
++ 0..;
++ ..10;
++ 0..=10;
++ foo();
++ } else {
++ //~ ERROR same body as `if` block
++ Foo { bar: 42 };
++ 0..10;
++ ..;
++ 0..;
++ ..10;
++ 0..=10;
++ foo();
++ }
++
++ if true {
++ Foo { bar: 42 };
++ } else {
++ Foo { bar: 43 };
++ }
++
++ if true {
++ ();
++ } else {
++ ()
++ }
++
++ if true {
++ 0..10;
++ } else {
++ 0..=10;
++ }
++
++ if true {
++ foo();
++ foo();
++ } else {
++ foo();
++ }
++
++ let _ = if true {
++ 0.0
++ } else {
++ //~ ERROR same body as `if` block
++ 0.0
++ };
++
++ let _ = if true {
++ -0.0
++ } else {
++ //~ ERROR same body as `if` block
++ -0.0
++ };
++
++ let _ = if true { 0.0 } else { -0.0 };
++
++ // Different NaNs
++ let _ = if true { 0.0 / 0.0 } else { f32::NAN };
++
++ if true {
++ foo();
++ }
++
++ let _ = if true {
++ 42
++ } else {
++ //~ ERROR same body as `if` block
++ 42
++ };
++
++ if true {
++ let bar = if true { 42 } else { 43 };
++
++ while foo() {
++ break;
++ }
++ bar + 1;
++ } else {
++ //~ ERROR same body as `if` block
++ let bar = if true { 42 } else { 43 };
++
++ while foo() {
++ break;
++ }
++ bar + 1;
++ }
++
++ if true {
++ let _ = match 42 {
++ 42 => 1,
++ a if a > 0 => 2,
++ 10..=15 => 3,
++ _ => 4,
++ };
++ } else if false {
++ foo();
++ } else if foo() {
++ let _ = match 42 {
++ 42 => 1,
++ a if a > 0 => 2,
++ 10..=15 => 3,
++ _ => 4,
++ };
++ }
++}
++
++// Issue #2423. This was causing an ICE.
++fn func() {
++ if true {
++ f(&[0; 62]);
++ f(&[0; 4]);
++ f(&[0; 3]);
++ } else {
++ f(&[0; 62]);
++ f(&[0; 6]);
++ f(&[0; 6]);
++ }
++}
++
++fn f(val: &[u8]) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else.rs:28:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | Foo { bar: 42 };
++LL | | 0..10;
++... |
++LL | | foo();
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::if-same-then-else` implied by `-D warnings`
++note: same as this
++ --> $DIR/if_same_then_else.rs:20:13
++ |
++LL | if true {
++ | _____________^
++LL | | Foo { bar: 42 };
++LL | | 0..10;
++LL | | ..;
++... |
++LL | | foo();
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else.rs:66:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | 0.0
++LL | | };
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else.rs:64:21
++ |
++LL | let _ = if true {
++ | _____________________^
++LL | | 0.0
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else.rs:73:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | -0.0
++LL | | };
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else.rs:71:21
++ |
++LL | let _ = if true {
++ | _____________________^
++LL | | -0.0
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else.rs:89:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | 42
++LL | | };
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else.rs:87:21
++ |
++LL | let _ = if true {
++ | _____________________^
++LL | | 42
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else.rs:101:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | let bar = if true { 42 } else { 43 };
++LL | |
++... |
++LL | | bar + 1;
++LL | | }
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else.rs:94:13
++ |
++LL | if true {
++ | _____________^
++LL | | let bar = if true { 42 } else { 43 };
++LL | |
++LL | | while foo() {
++... |
++LL | | bar + 1;
++LL | | } else {
++ | |_____^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::if_same_then_else)]
++#![allow(
++ clippy::blacklisted_name,
++ clippy::collapsible_if,
++ clippy::ifs_same_cond,
++ clippy::needless_return
++)]
++
++fn if_same_then_else2() -> Result<&'static str, ()> {
++ if true {
++ for _ in &[42] {
++ let foo: &Option<_> = &Some::<u8>(42);
++ if true {
++ break;
++ } else {
++ continue;
++ }
++ }
++ } else {
++ //~ ERROR same body as `if` block
++ for _ in &[42] {
++ let foo: &Option<_> = &Some::<u8>(42);
++ if true {
++ break;
++ } else {
++ continue;
++ }
++ }
++ }
++
++ if true {
++ if let Some(a) = Some(42) {}
++ } else {
++ //~ ERROR same body as `if` block
++ if let Some(a) = Some(42) {}
++ }
++
++ if true {
++ if let (1, .., 3) = (1, 2, 3) {}
++ } else {
++ //~ ERROR same body as `if` block
++ if let (1, .., 3) = (1, 2, 3) {}
++ }
++
++ if true {
++ if let (1, .., 3) = (1, 2, 3) {}
++ } else {
++ if let (.., 3) = (1, 2, 3) {}
++ }
++
++ if true {
++ if let (1, .., 3) = (1, 2, 3) {}
++ } else {
++ if let (.., 4) = (1, 2, 3) {}
++ }
++
++ if true {
++ if let (1, .., 3) = (1, 2, 3) {}
++ } else {
++ if let (.., 1, 3) = (1, 2, 3) {}
++ }
++
++ if true {
++ if let Some(42) = None {}
++ } else {
++ if let Option::Some(42) = None {}
++ }
++
++ if true {
++ if let Some(42) = None::<u8> {}
++ } else {
++ if let Some(42) = None {}
++ }
++
++ if true {
++ if let Some(42) = None::<u8> {}
++ } else {
++ if let Some(42) = None::<u32> {}
++ }
++
++ if true {
++ if let Some(a) = Some(42) {}
++ } else {
++ if let Some(a) = Some(43) {}
++ }
++
++ // Same NaNs
++ let _ = if true {
++ f32::NAN
++ } else {
++ //~ ERROR same body as `if` block
++ f32::NAN
++ };
++
++ if true {
++ Ok("foo")?;
++ } else {
++ //~ ERROR same body as `if` block
++ Ok("foo")?;
++ }
++
++ if true {
++ let foo = "";
++ return Ok(&foo[0..]);
++ } else if false {
++ let foo = "bar";
++ return Ok(&foo[0..]);
++ } else {
++ let foo = "";
++ return Ok(&foo[0..]);
++ }
++
++ if true {
++ let foo = "";
++ return Ok(&foo[0..]);
++ } else if false {
++ let foo = "bar";
++ return Ok(&foo[0..]);
++ } else if true {
++ let foo = "";
++ return Ok(&foo[0..]);
++ } else {
++ let foo = "";
++ return Ok(&foo[0..]);
++ }
++
++ // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559.
++ if true {
++ let foo = "";
++ let (x, y) = (1, 2);
++ return Ok(&foo[x..y]);
++ } else {
++ let foo = "";
++ let (y, x) = (1, 2);
++ return Ok(&foo[x..y]);
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:19:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | for _ in &[42] {
++LL | | let foo: &Option<_> = &Some::<u8>(42);
++... |
++LL | | }
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::if-same-then-else` implied by `-D warnings`
++note: same as this
++ --> $DIR/if_same_then_else2.rs:10:13
++ |
++LL | if true {
++ | _____________^
++LL | | for _ in &[42] {
++LL | | let foo: &Option<_> = &Some::<u8>(42);
++LL | | if true {
++... |
++LL | | }
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:33:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | if let Some(a) = Some(42) {}
++LL | | }
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else2.rs:31:13
++ |
++LL | if true {
++ | _____________^
++LL | | if let Some(a) = Some(42) {}
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:40:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | if let (1, .., 3) = (1, 2, 3) {}
++LL | | }
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else2.rs:38:13
++ |
++LL | if true {
++ | _____________^
++LL | | if let (1, .., 3) = (1, 2, 3) {}
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:90:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | f32::NAN
++LL | | };
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else2.rs:88:21
++ |
++LL | let _ = if true {
++ | _____________________^
++LL | | f32::NAN
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:97:12
++ |
++LL | } else {
++ | ____________^
++LL | | //~ ERROR same body as `if` block
++LL | | Ok("foo")?;
++LL | | }
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else2.rs:95:13
++ |
++LL | if true {
++ | _____________^
++LL | | Ok("foo")?;
++LL | | } else {
++ | |_____^
++
++error: this `if` has identical blocks
++ --> $DIR/if_same_then_else2.rs:122:12
++ |
++LL | } else {
++ | ____________^
++LL | | let foo = "";
++LL | | return Ok(&foo[0..]);
++LL | | }
++ | |_____^
++ |
++note: same as this
++ --> $DIR/if_same_then_else2.rs:119:20
++ |
++LL | } else if true {
++ | ____________________^
++LL | | let foo = "";
++LL | | return Ok(&foo[0..]);
++LL | | } else {
++ | |_____^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::ifs_same_cond)]
++#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
++
++fn ifs_same_cond() {
++ let a = 0;
++ let b = false;
++
++ if b {
++ } else if b {
++ //~ ERROR ifs same condition
++ }
++
++ if a == 1 {
++ } else if a == 1 {
++ //~ ERROR ifs same condition
++ }
++
++ if 2 * a == 1 {
++ } else if 2 * a == 2 {
++ } else if 2 * a == 1 {
++ //~ ERROR ifs same condition
++ } else if a == 1 {
++ }
++
++ // See #659
++ if cfg!(feature = "feature1-659") {
++ 1
++ } else if cfg!(feature = "feature2-659") {
++ 2
++ } else {
++ 3
++ };
++
++ let mut v = vec![1];
++ if v.pop() == None {
++ // ok, functions
++ } else if v.pop() == None {
++ }
++
++ if v.len() == 42 {
++ // ok, functions
++ } else if v.len() == 42 {
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `if` has the same condition as a previous `if`
++ --> $DIR/ifs_same_cond.rs:9:15
++ |
++LL | } else if b {
++ | ^
++ |
++ = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
++note: same as this
++ --> $DIR/ifs_same_cond.rs:8:8
++ |
++LL | if b {
++ | ^
++
++error: this `if` has the same condition as a previous `if`
++ --> $DIR/ifs_same_cond.rs:14:15
++ |
++LL | } else if a == 1 {
++ | ^^^^^^
++ |
++note: same as this
++ --> $DIR/ifs_same_cond.rs:13:8
++ |
++LL | if a == 1 {
++ | ^^^^^^
++
++error: this `if` has the same condition as a previous `if`
++ --> $DIR/ifs_same_cond.rs:20:15
++ |
++LL | } else if 2 * a == 1 {
++ | ^^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/ifs_same_cond.rs:18:8
++ |
++LL | if 2 * a == 1 {
++ | ^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![warn(clippy::multiple_inherent_impl)]
++
++struct MyStruct;
++
++impl MyStruct {
++ fn first() {}
++}
++
++impl MyStruct {
++ fn second() {}
++}
++
++impl<'a> MyStruct {
++ fn lifetimed() {}
++}
++
++mod submod {
++ struct MyStruct;
++ impl MyStruct {
++ fn other() {}
++ }
++
++ impl super::MyStruct {
++ fn third() {}
++ }
++}
++
++use std::fmt;
++impl fmt::Debug for MyStruct {
++ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++ write!(f, "MyStruct {{ }}")
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Multiple implementations of this structure
++ --> $DIR/impl.rs:10:1
++ |
++LL | / impl MyStruct {
++LL | | fn second() {}
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings`
++note: First implementation here
++ --> $DIR/impl.rs:6:1
++ |
++LL | / impl MyStruct {
++LL | | fn first() {}
++LL | | }
++ | |_^
++
++error: Multiple implementations of this structure
++ --> $DIR/impl.rs:24:5
++ |
++LL | / impl super::MyStruct {
++LL | | fn third() {}
++LL | | }
++ | |_____^
++ |
++note: First implementation here
++ --> $DIR/impl.rs:6:1
++ |
++LL | / impl MyStruct {
++LL | | fn first() {}
++LL | | }
++ | |_^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:implicit_hasher_macros.rs
++#![deny(clippy::implicit_hasher)]
++#![allow(unused)]
++
++#[macro_use]
++extern crate implicit_hasher_macros;
++
++use std::cmp::Eq;
++use std::collections::{HashMap, HashSet};
++use std::hash::{BuildHasher, Hash};
++
++pub trait Foo<T>: Sized {
++ fn make() -> (Self, Self);
++}
++
++impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
++ fn make() -> (Self, Self) {
++ // OK, don't suggest to modify these
++ let _: HashMap<i32, i32> = HashMap::new();
++ let _: HashSet<i32> = HashSet::new();
++
++ (HashMap::new(), HashMap::with_capacity(10))
++ }
++}
++impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
++ fn make() -> (Self, Self) {
++ ((HashMap::new(),), (HashMap::with_capacity(10),))
++ }
++}
++impl Foo<i16> for HashMap<String, String> {
++ fn make() -> (Self, Self) {
++ (HashMap::new(), HashMap::with_capacity(10))
++ }
++}
++
++impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
++ fn make() -> (Self, Self) {
++ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
++ }
++}
++impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
++ fn make() -> (Self, Self) {
++ (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
++ }
++}
++
++impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
++ fn make() -> (Self, Self) {
++ (HashSet::new(), HashSet::with_capacity(10))
++ }
++}
++impl Foo<i16> for HashSet<String> {
++ fn make() -> (Self, Self) {
++ (HashSet::new(), HashSet::with_capacity(10))
++ }
++}
++
++impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
++ fn make() -> (Self, Self) {
++ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
++ }
++}
++impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
++ fn make() -> (Self, Self) {
++ (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
++ }
++}
++
++pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++
++macro_rules! gen {
++ (impl) => {
++ impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
++ fn make() -> (Self, Self) {
++ (HashMap::new(), HashMap::with_capacity(10))
++ }
++ }
++ };
++
++ (fn $name:ident) => {
++ pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++ };
++}
++#[rustfmt::skip]
++gen!(impl);
++gen!(fn bar);
++
++// When the macro is in a different file, the suggestion spans can't be combined properly
++// and should not cause an ICE
++// See #2707
++#[macro_use]
++#[path = "../auxiliary/test_macro.rs"]
++pub mod test_macro;
++__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
++
++// #4260
++implicit_hasher_fn!();
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: impl for `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:16:35
++ |
++LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
++ | ^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/implicit_hasher.rs:2:9
++ |
++LL | #![deny(clippy::implicit_hasher)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++help: consider adding a type parameter
++ |
++LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:25:36
++ |
++LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
++ | ^^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:30:19
++ |
++LL | impl Foo<i16> for HashMap<String, String> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashSet` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:47:32
++ |
++LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
++ | ^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashSet` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:52:19
++ |
++LL | impl Foo<i16> for HashSet<String> {
++ | ^^^^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:69:23
++ |
++LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashSet` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:69:53
++ |
++LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^
++ |
++help: consider adding a type parameter
++ |
++LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:73:43
++ |
++LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
++ | ^^^^^^^^^^^^^
++...
++LL | gen!(impl);
++ | ----------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++ |
++LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++ |
++LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++ | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashMap` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:81:33
++ |
++LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^^^^^^
++...
++LL | gen!(fn bar);
++ | ------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++ |
++LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashSet` should be generalized over different hashers
++ --> $DIR/implicit_hasher.rs:81:63
++ |
++LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++ | ^^^^^^^^^^^^
++...
++LL | gen!(fn bar);
++ | ------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++ |
++LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::implicit_return)]
++#![allow(clippy::needless_return, unused)]
++
++fn test_end_of_fn() -> bool {
++ if true {
++ // no error!
++ return true;
++ }
++
++ return true
++}
++
++#[allow(clippy::needless_bool)]
++fn test_if_block() -> bool {
++ if true {
++ return true
++ } else {
++ return false
++ }
++}
++
++#[rustfmt::skip]
++fn test_match(x: bool) -> bool {
++ match x {
++ true => return false,
++ false => { return true },
++ }
++}
++
++#[allow(clippy::needless_return)]
++fn test_match_with_unreachable(x: bool) -> bool {
++ match x {
++ true => return false,
++ false => unreachable!(),
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop() -> bool {
++ loop {
++ return true;
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_block() -> bool {
++ loop {
++ {
++ return true;
++ }
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_nests() -> bool {
++ loop {
++ if true {
++ return true;
++ } else {
++ let _ = true;
++ }
++ }
++}
++
++#[allow(clippy::redundant_pattern_matching)]
++fn test_loop_with_if_let() -> bool {
++ loop {
++ if let Some(x) = Some(true) {
++ return x;
++ }
++ }
++}
++
++fn test_closure() {
++ #[rustfmt::skip]
++ let _ = || { return true };
++ let _ = || return true;
++}
++
++fn test_panic() -> bool {
++ panic!()
++}
++
++fn test_return_macro() -> String {
++ return format!("test {}", "test")
++}
++
++fn main() {
++ let _ = test_end_of_fn();
++ let _ = test_if_block();
++ let _ = test_match(true);
++ let _ = test_match_with_unreachable(true);
++ let _ = test_loop();
++ let _ = test_loop_with_block();
++ let _ = test_loop_with_nests();
++ let _ = test_loop_with_if_let();
++ test_closure();
++ let _ = test_return_macro();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::implicit_return)]
++#![allow(clippy::needless_return, unused)]
++
++fn test_end_of_fn() -> bool {
++ if true {
++ // no error!
++ return true;
++ }
++
++ true
++}
++
++#[allow(clippy::needless_bool)]
++fn test_if_block() -> bool {
++ if true {
++ true
++ } else {
++ false
++ }
++}
++
++#[rustfmt::skip]
++fn test_match(x: bool) -> bool {
++ match x {
++ true => false,
++ false => { true },
++ }
++}
++
++#[allow(clippy::needless_return)]
++fn test_match_with_unreachable(x: bool) -> bool {
++ match x {
++ true => return false,
++ false => unreachable!(),
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop() -> bool {
++ loop {
++ break true;
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_block() -> bool {
++ loop {
++ {
++ break true;
++ }
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_nests() -> bool {
++ loop {
++ if true {
++ break true;
++ } else {
++ let _ = true;
++ }
++ }
++}
++
++#[allow(clippy::redundant_pattern_matching)]
++fn test_loop_with_if_let() -> bool {
++ loop {
++ if let Some(x) = Some(true) {
++ return x;
++ }
++ }
++}
++
++fn test_closure() {
++ #[rustfmt::skip]
++ let _ = || { true };
++ let _ = || true;
++}
++
++fn test_panic() -> bool {
++ panic!()
++}
++
++fn test_return_macro() -> String {
++ format!("test {}", "test")
++}
++
++fn main() {
++ let _ = test_end_of_fn();
++ let _ = test_if_block();
++ let _ = test_match(true);
++ let _ = test_match_with_unreachable(true);
++ let _ = test_loop();
++ let _ = test_loop_with_block();
++ let _ = test_loop_with_nests();
++ let _ = test_loop_with_if_let();
++ test_closure();
++ let _ = test_return_macro();
++}
--- /dev/null
--- /dev/null
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:12:5
++ |
++LL | true
++ | ^^^^ help: add `return` as shown: `return true`
++ |
++ = note: `-D clippy::implicit-return` implied by `-D warnings`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:18:9
++ |
++LL | true
++ | ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:20:9
++ |
++LL | false
++ | ^^^^^ help: add `return` as shown: `return false`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:27:17
++ |
++LL | true => false,
++ | ^^^^^ help: add `return` as shown: `return false`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:28:20
++ |
++LL | false => { true },
++ | ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:43:9
++ |
++LL | break true;
++ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:51:13
++ |
++LL | break true;
++ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:60:13
++ |
++LL | break true;
++ | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:78:18
++ |
++LL | let _ = || { true };
++ | ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:79:16
++ |
++LL | let _ = || true;
++ | ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++ --> $DIR/implicit_return.rs:87:5
++ |
++LL | format!("test {}", "test")
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
++#![warn(clippy::implicit_saturating_sub)]
++
++fn main() {
++ // Tests for unsigned integers
++
++ let end_8: u8 = 10;
++ let start_8: u8 = 5;
++ let mut u_8: u8 = end_8 - start_8;
++
++ // Lint
++ u_8 = u_8.saturating_sub(1);
++
++ match end_8 {
++ 10 => {
++ // Lint
++ u_8 = u_8.saturating_sub(1);
++ },
++ 11 => u_8 += 1,
++ _ => u_8 = 0,
++ }
++
++ let end_16: u16 = 40;
++ let start_16: u16 = 35;
++
++ let mut u_16: u16 = end_16 - start_16;
++
++ // Lint
++ u_16 = u_16.saturating_sub(1);
++
++ let mut end_32: u32 = 7000;
++ let mut start_32: u32 = 7010;
++
++ let mut u_32: u32 = end_32 - start_32;
++
++ // Lint
++ u_32 = u_32.saturating_sub(1);
++
++ // No Lint
++ if u_32 > 0 {
++ u_16 += 1;
++ }
++
++ // No Lint
++ if u_32 != 0 {
++ end_32 -= 1;
++ start_32 += 1;
++ }
++
++ let mut end_64: u64 = 75001;
++ let mut start_64: u64 = 75000;
++
++ let mut u_64: u64 = end_64 - start_64;
++
++ // Lint
++ u_64 = u_64.saturating_sub(1);
++
++ // Lint
++ u_64 = u_64.saturating_sub(1);
++
++ // Lint
++ u_64 = u_64.saturating_sub(1);
++
++ // No Lint
++ if u_64 >= 1 {
++ u_64 -= 1;
++ }
++
++ // No Lint
++ if u_64 > 0 {
++ end_64 -= 1;
++ }
++
++ // Tests for usize
++ let end_usize: usize = 8054;
++ let start_usize: usize = 8050;
++
++ let mut u_usize: usize = end_usize - start_usize;
++
++ // Lint
++ u_usize = u_usize.saturating_sub(1);
++
++ // Tests for signed integers
++
++ let endi_8: i8 = 10;
++ let starti_8: i8 = 50;
++
++ let mut i_8: i8 = endi_8 - starti_8;
++
++ // Lint
++ i_8 = i_8.saturating_sub(1);
++
++ // Lint
++ i_8 = i_8.saturating_sub(1);
++
++ // Lint
++ i_8 = i_8.saturating_sub(1);
++
++ // Lint
++ i_8 = i_8.saturating_sub(1);
++
++ let endi_16: i16 = 45;
++ let starti_16: i16 = 44;
++
++ let mut i_16: i16 = endi_16 - starti_16;
++
++ // Lint
++ i_16 = i_16.saturating_sub(1);
++
++ // Lint
++ i_16 = i_16.saturating_sub(1);
++
++ // Lint
++ i_16 = i_16.saturating_sub(1);
++
++ // Lint
++ i_16 = i_16.saturating_sub(1);
++
++ let endi_32: i32 = 45;
++ let starti_32: i32 = 44;
++
++ let mut i_32: i32 = endi_32 - starti_32;
++
++ // Lint
++ i_32 = i_32.saturating_sub(1);
++
++ // Lint
++ i_32 = i_32.saturating_sub(1);
++
++ // Lint
++ i_32 = i_32.saturating_sub(1);
++
++ // Lint
++ i_32 = i_32.saturating_sub(1);
++
++ let endi_64: i64 = 45;
++ let starti_64: i64 = 44;
++
++ let mut i_64: i64 = endi_64 - starti_64;
++
++ // Lint
++ i_64 = i_64.saturating_sub(1);
++
++ // Lint
++ i_64 = i_64.saturating_sub(1);
++
++ // Lint
++ i_64 = i_64.saturating_sub(1);
++
++ // No Lint
++ if i_64 > 0 {
++ i_64 -= 1;
++ }
++
++ // No Lint
++ if i_64 != 0 {
++ i_64 -= 1;
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
++#![warn(clippy::implicit_saturating_sub)]
++
++fn main() {
++ // Tests for unsigned integers
++
++ let end_8: u8 = 10;
++ let start_8: u8 = 5;
++ let mut u_8: u8 = end_8 - start_8;
++
++ // Lint
++ if u_8 > 0 {
++ u_8 = u_8 - 1;
++ }
++
++ match end_8 {
++ 10 => {
++ // Lint
++ if u_8 > 0 {
++ u_8 -= 1;
++ }
++ },
++ 11 => u_8 += 1,
++ _ => u_8 = 0,
++ }
++
++ let end_16: u16 = 40;
++ let start_16: u16 = 35;
++
++ let mut u_16: u16 = end_16 - start_16;
++
++ // Lint
++ if u_16 > 0 {
++ u_16 -= 1;
++ }
++
++ let mut end_32: u32 = 7000;
++ let mut start_32: u32 = 7010;
++
++ let mut u_32: u32 = end_32 - start_32;
++
++ // Lint
++ if u_32 != 0 {
++ u_32 -= 1;
++ }
++
++ // No Lint
++ if u_32 > 0 {
++ u_16 += 1;
++ }
++
++ // No Lint
++ if u_32 != 0 {
++ end_32 -= 1;
++ start_32 += 1;
++ }
++
++ let mut end_64: u64 = 75001;
++ let mut start_64: u64 = 75000;
++
++ let mut u_64: u64 = end_64 - start_64;
++
++ // Lint
++ if u_64 > 0 {
++ u_64 -= 1;
++ }
++
++ // Lint
++ if 0 < u_64 {
++ u_64 -= 1;
++ }
++
++ // Lint
++ if 0 != u_64 {
++ u_64 -= 1;
++ }
++
++ // No Lint
++ if u_64 >= 1 {
++ u_64 -= 1;
++ }
++
++ // No Lint
++ if u_64 > 0 {
++ end_64 -= 1;
++ }
++
++ // Tests for usize
++ let end_usize: usize = 8054;
++ let start_usize: usize = 8050;
++
++ let mut u_usize: usize = end_usize - start_usize;
++
++ // Lint
++ if u_usize > 0 {
++ u_usize -= 1;
++ }
++
++ // Tests for signed integers
++
++ let endi_8: i8 = 10;
++ let starti_8: i8 = 50;
++
++ let mut i_8: i8 = endi_8 - starti_8;
++
++ // Lint
++ if i_8 > i8::MIN {
++ i_8 -= 1;
++ }
++
++ // Lint
++ if i_8 > i8::min_value() {
++ i_8 -= 1;
++ }
++
++ // Lint
++ if i_8 != i8::MIN {
++ i_8 -= 1;
++ }
++
++ // Lint
++ if i_8 != i8::min_value() {
++ i_8 -= 1;
++ }
++
++ let endi_16: i16 = 45;
++ let starti_16: i16 = 44;
++
++ let mut i_16: i16 = endi_16 - starti_16;
++
++ // Lint
++ if i_16 > i16::MIN {
++ i_16 -= 1;
++ }
++
++ // Lint
++ if i_16 > i16::min_value() {
++ i_16 -= 1;
++ }
++
++ // Lint
++ if i_16 != i16::MIN {
++ i_16 -= 1;
++ }
++
++ // Lint
++ if i_16 != i16::min_value() {
++ i_16 -= 1;
++ }
++
++ let endi_32: i32 = 45;
++ let starti_32: i32 = 44;
++
++ let mut i_32: i32 = endi_32 - starti_32;
++
++ // Lint
++ if i_32 > i32::MIN {
++ i_32 -= 1;
++ }
++
++ // Lint
++ if i_32 > i32::min_value() {
++ i_32 -= 1;
++ }
++
++ // Lint
++ if i_32 != i32::MIN {
++ i_32 -= 1;
++ }
++
++ // Lint
++ if i_32 != i32::min_value() {
++ i_32 -= 1;
++ }
++
++ let endi_64: i64 = 45;
++ let starti_64: i64 = 44;
++
++ let mut i_64: i64 = endi_64 - starti_64;
++
++ // Lint
++ if i64::min_value() < i_64 {
++ i_64 -= 1;
++ }
++
++ // Lint
++ if i64::MIN != i_64 {
++ i_64 -= 1;
++ }
++
++ // Lint
++ if i64::MIN < i_64 {
++ i_64 -= 1;
++ }
++
++ // No Lint
++ if i_64 > 0 {
++ i_64 -= 1;
++ }
++
++ // No Lint
++ if i_64 != 0 {
++ i_64 -= 1;
++ }
++}
--- /dev/null
--- /dev/null
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:13:5
++ |
++LL | / if u_8 > 0 {
++LL | | u_8 = u_8 - 1;
++LL | | }
++ | |_____^ help: try: `u_8 = u_8.saturating_sub(1);`
++ |
++ = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:20:13
++ |
++LL | / if u_8 > 0 {
++LL | | u_8 -= 1;
++LL | | }
++ | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:34:5
++ |
++LL | / if u_16 > 0 {
++LL | | u_16 -= 1;
++LL | | }
++ | |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:44:5
++ |
++LL | / if u_32 != 0 {
++LL | | u_32 -= 1;
++LL | | }
++ | |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:65:5
++ |
++LL | / if u_64 > 0 {
++LL | | u_64 -= 1;
++LL | | }
++ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:70:5
++ |
++LL | / if 0 < u_64 {
++LL | | u_64 -= 1;
++LL | | }
++ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:75:5
++ |
++LL | / if 0 != u_64 {
++LL | | u_64 -= 1;
++LL | | }
++ | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:96:5
++ |
++LL | / if u_usize > 0 {
++LL | | u_usize -= 1;
++LL | | }
++ | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:108:5
++ |
++LL | / if i_8 > i8::MIN {
++LL | | i_8 -= 1;
++LL | | }
++ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:113:5
++ |
++LL | / if i_8 > i8::min_value() {
++LL | | i_8 -= 1;
++LL | | }
++ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:118:5
++ |
++LL | / if i_8 != i8::MIN {
++LL | | i_8 -= 1;
++LL | | }
++ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:123:5
++ |
++LL | / if i_8 != i8::min_value() {
++LL | | i_8 -= 1;
++LL | | }
++ | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:133:5
++ |
++LL | / if i_16 > i16::MIN {
++LL | | i_16 -= 1;
++LL | | }
++ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:138:5
++ |
++LL | / if i_16 > i16::min_value() {
++LL | | i_16 -= 1;
++LL | | }
++ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:143:5
++ |
++LL | / if i_16 != i16::MIN {
++LL | | i_16 -= 1;
++LL | | }
++ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:148:5
++ |
++LL | / if i_16 != i16::min_value() {
++LL | | i_16 -= 1;
++LL | | }
++ | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:158:5
++ |
++LL | / if i_32 > i32::MIN {
++LL | | i_32 -= 1;
++LL | | }
++ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:163:5
++ |
++LL | / if i_32 > i32::min_value() {
++LL | | i_32 -= 1;
++LL | | }
++ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:168:5
++ |
++LL | / if i_32 != i32::MIN {
++LL | | i_32 -= 1;
++LL | | }
++ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:173:5
++ |
++LL | / if i_32 != i32::min_value() {
++LL | | i_32 -= 1;
++LL | | }
++ | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:183:5
++ |
++LL | / if i64::min_value() < i_64 {
++LL | | i_64 -= 1;
++LL | | }
++ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:188:5
++ |
++LL | / if i64::MIN != i_64 {
++LL | | i_64 -= 1;
++LL | | }
++ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++ --> $DIR/implicit_saturating_sub.rs:193:5
++ |
++LL | / if i64::MIN < i_64 {
++LL | | i_64 -= 1;
++LL | | }
++ | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: aborting due to 23 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#[warn(clippy::inconsistent_digit_grouping)]
++#[deny(clippy::unreadable_literal)]
++#[allow(unused_variables, clippy::excessive_precision)]
++fn main() {
++ macro_rules! mac1 {
++ () => {
++ 1_23_456
++ };
++ }
++ macro_rules! mac2 {
++ () => {
++ 1_234.5678_f32
++ };
++ }
++
++ let good = (
++ 123,
++ 1_234,
++ 1_2345_6789,
++ 123_f32,
++ 1_234.12_f32,
++ 1_234.123_4_f32,
++ 1.123_456_7_f32,
++ );
++ let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32);
++
++ // Test padding
++ let _ = 0x0010_0000;
++ let _ = 0x0100_0000;
++ let _ = 0x1000_0000;
++ let _ = 0x0001_0000_0000_u64;
++
++ // Test suggestion when fraction has no digits
++ let _: f32 = 123_456.;
++
++ // Test UUID formatted literal
++ let _: u128 = 0x12345678_1234_1234_1234_123456789012;
++
++ // Ignore literals in macros
++ let _ = mac1!();
++ let _ = mac2!();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#[warn(clippy::inconsistent_digit_grouping)]
++#[deny(clippy::unreadable_literal)]
++#[allow(unused_variables, clippy::excessive_precision)]
++fn main() {
++ macro_rules! mac1 {
++ () => {
++ 1_23_456
++ };
++ }
++ macro_rules! mac2 {
++ () => {
++ 1_234.5678_f32
++ };
++ }
++
++ let good = (
++ 123,
++ 1_234,
++ 1_2345_6789,
++ 123_f32,
++ 1_234.12_f32,
++ 1_234.123_4_f32,
++ 1.123_456_7_f32,
++ );
++ let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++
++ // Test padding
++ let _ = 0x100000;
++ let _ = 0x1000000;
++ let _ = 0x10000000;
++ let _ = 0x100000000_u64;
++
++ // Test suggestion when fraction has no digits
++ let _: f32 = 1_23_456.;
++
++ // Test UUID formatted literal
++ let _: u128 = 0x12345678_1234_1234_1234_123456789012;
++
++ // Ignore literals in macros
++ let _ = mac1!();
++ let _ = mac2!();
++}
--- /dev/null
--- /dev/null
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:26:16
++ |
++LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++ | ^^^^^^^^ help: consider: `123_456`
++ |
++ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:26:26
++ |
++LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++ | ^^^^^^^^^^ help: consider: `12_345_678`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:26:38
++ |
++LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++ | ^^^^^^^^ help: consider: `1_234_567`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:26:48
++ |
++LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++ | ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:26:64
++ |
++LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++ | ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32`
++
++error: long literal lacking separators
++ --> $DIR/inconsistent_digit_grouping.rs:29:13
++ |
++LL | let _ = 0x100000;
++ | ^^^^^^^^ help: consider: `0x0010_0000`
++ |
++note: the lint level is defined here
++ --> $DIR/inconsistent_digit_grouping.rs:3:8
++ |
++LL | #[deny(clippy::unreadable_literal)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: long literal lacking separators
++ --> $DIR/inconsistent_digit_grouping.rs:30:13
++ |
++LL | let _ = 0x1000000;
++ | ^^^^^^^^^ help: consider: `0x0100_0000`
++
++error: long literal lacking separators
++ --> $DIR/inconsistent_digit_grouping.rs:31:13
++ |
++LL | let _ = 0x10000000;
++ | ^^^^^^^^^^ help: consider: `0x1000_0000`
++
++error: long literal lacking separators
++ --> $DIR/inconsistent_digit_grouping.rs:32:13
++ |
++LL | let _ = 0x100000000_u64;
++ | ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/inconsistent_digit_grouping.rs:35:18
++ |
++LL | let _: f32 = 1_23_456.;
++ | ^^^^^^^^^ help: consider: `123_456.`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::indexing_slicing)]
++// We also check the out_of_bounds_indexing lint here, because it lints similar things and
++// we want to avoid false positives.
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++ let x = [1, 2, 3, 4];
++ let index: usize = 1;
++ x[index];
++ x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++ x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++
++ x[0]; // Ok, should not produce stderr.
++ x[3]; // Ok, should not produce stderr.
++
++ let y = &x;
++ y[0];
++
++ let v = vec![0; 5];
++ v[0];
++ v[10];
++ v[1 << 3];
++
++ const N: usize = 15; // Out of bounds
++ const M: usize = 3; // In bounds
++ x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++ x[M]; // Ok, should not produce stderr.
++ v[N];
++ v[M];
++}
--- /dev/null
--- /dev/null
++error: this operation will panic at runtime
++ --> $DIR/indexing_slicing_index.rs:11:5
++ |
++LL | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++ | ^^^^ index out of bounds: the len is 4 but the index is 4
++ |
++ = note: `#[deny(unconditional_panic)]` on by default
++
++error: this operation will panic at runtime
++ --> $DIR/indexing_slicing_index.rs:12:5
++ |
++LL | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++ | ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8
++
++error: this operation will panic at runtime
++ --> $DIR/indexing_slicing_index.rs:27:5
++ |
++LL | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++ | ^^^^ index out of bounds: the len is 4 but the index is 15
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:10:5
++ |
++LL | x[index];
++ | ^^^^^^^^
++ |
++ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:18:5
++ |
++LL | y[0];
++ | ^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:21:5
++ |
++LL | v[0];
++ | ^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:22:5
++ |
++LL | v[10];
++ | ^^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:23:5
++ |
++LL | v[1 << 3];
++ | ^^^^^^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:29:5
++ |
++LL | v[N];
++ | ^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++ --> $DIR/indexing_slicing_index.rs:30:5
++ |
++LL | v[M];
++ | ^^^^
++ |
++ = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::indexing_slicing)]
++// We also check the out_of_bounds_indexing lint here, because it lints similar things and
++// we want to avoid false positives.
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++ let x = [1, 2, 3, 4];
++ let index: usize = 1;
++ let index_from: usize = 2;
++ let index_to: usize = 3;
++ &x[index..];
++ &x[..index];
++ &x[index_from..index_to];
++ &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++ &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++ &x[0..][..3];
++ &x[1..][..5];
++
++ &x[0..].get(..3); // Ok, should not produce stderr.
++ &x[0..3]; // Ok, should not produce stderr.
++
++ let y = &x;
++ &y[1..2];
++ &y[0..=4];
++ &y[..=4];
++
++ &y[..]; // Ok, should not produce stderr.
++
++ let v = vec![0; 5];
++ &v[10..100];
++ &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++ &v[10..];
++ &v[..100];
++
++ &v[..]; // Ok, should not produce stderr.
++}
--- /dev/null
--- /dev/null
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:12:6
++ |
++LL | &x[index..];
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
++ = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:13:6
++ |
++LL | &x[..index];
++ | ^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:14:6
++ |
++LL | &x[index_from..index_to];
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:15:6
++ |
++LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:15:6
++ |
++LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:16:6
++ |
++LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++ | ^^^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: range is out of bounds
++ --> $DIR/indexing_slicing_slice.rs:16:8
++ |
++LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++ | ^
++ |
++ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:17:6
++ |
++LL | &x[0..][..3];
++ | ^^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:18:6
++ |
++LL | &x[1..][..5];
++ | ^^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:24:6
++ |
++LL | &y[1..2];
++ | ^^^^^^^
++ |
++ = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:25:6
++ |
++LL | &y[0..=4];
++ | ^^^^^^^^
++ |
++ = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:26:6
++ |
++LL | &y[..=4];
++ | ^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:31:6
++ |
++LL | &v[10..100];
++ | ^^^^^^^^^^
++ |
++ = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:32:6
++ |
++LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++ | ^^^^^^^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: range is out of bounds
++ --> $DIR/indexing_slicing_slice.rs:32:8
++ |
++LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++ | ^^
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:33:6
++ |
++LL | &v[10..];
++ | ^^^^^^^
++ |
++ = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++ --> $DIR/indexing_slicing_slice.rs:34:6
++ |
++LL | &v[..100];
++ | ^^^^^^^^
++ |
++ = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::inefficient_to_string)]
++
++use std::borrow::Cow;
++
++fn main() {
++ let rstr: &str = "hello";
++ let rrstr: &&str = &rstr;
++ let rrrstr: &&&str = &rrstr;
++ let _: String = rstr.to_string();
++ let _: String = (*rrstr).to_string();
++ let _: String = (**rrrstr).to_string();
++
++ let string: String = String::from("hello");
++ let rstring: &String = &string;
++ let rrstring: &&String = &rstring;
++ let rrrstring: &&&String = &rrstring;
++ let _: String = string.to_string();
++ let _: String = rstring.to_string();
++ let _: String = (*rrstring).to_string();
++ let _: String = (**rrrstring).to_string();
++
++ let cow: Cow<'_, str> = Cow::Borrowed("hello");
++ let rcow: &Cow<'_, str> = &cow;
++ let rrcow: &&Cow<'_, str> = &rcow;
++ let rrrcow: &&&Cow<'_, str> = &rrcow;
++ let _: String = cow.to_string();
++ let _: String = rcow.to_string();
++ let _: String = (*rrcow).to_string();
++ let _: String = (**rrrcow).to_string();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::inefficient_to_string)]
++
++use std::borrow::Cow;
++
++fn main() {
++ let rstr: &str = "hello";
++ let rrstr: &&str = &rstr;
++ let rrrstr: &&&str = &rrstr;
++ let _: String = rstr.to_string();
++ let _: String = rrstr.to_string();
++ let _: String = rrrstr.to_string();
++
++ let string: String = String::from("hello");
++ let rstring: &String = &string;
++ let rrstring: &&String = &rstring;
++ let rrrstring: &&&String = &rrstring;
++ let _: String = string.to_string();
++ let _: String = rstring.to_string();
++ let _: String = rrstring.to_string();
++ let _: String = rrrstring.to_string();
++
++ let cow: Cow<'_, str> = Cow::Borrowed("hello");
++ let rcow: &Cow<'_, str> = &cow;
++ let rrcow: &&Cow<'_, str> = &rcow;
++ let rrrcow: &&&Cow<'_, str> = &rrcow;
++ let _: String = cow.to_string();
++ let _: String = rcow.to_string();
++ let _: String = rrcow.to_string();
++ let _: String = rrrcow.to_string();
++}
--- /dev/null
--- /dev/null
++error: calling `to_string` on `&&str`
++ --> $DIR/inefficient_to_string.rs:11:21
++ |
++LL | let _: String = rrstr.to_string();
++ | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()`
++ |
++note: the lint level is defined here
++ --> $DIR/inefficient_to_string.rs:2:9
++ |
++LL | #![deny(clippy::inefficient_to_string)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&str`
++ --> $DIR/inefficient_to_string.rs:12:21
++ |
++LL | let _: String = rrrstr.to_string();
++ | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()`
++ |
++ = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&std::string::String`
++ --> $DIR/inefficient_to_string.rs:20:21
++ |
++LL | let _: String = rrstring.to_string();
++ | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()`
++ |
++ = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&std::string::String`
++ --> $DIR/inefficient_to_string.rs:21:21
++ |
++LL | let _: String = rrrstring.to_string();
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()`
++ |
++ = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&std::borrow::Cow<str>`
++ --> $DIR/inefficient_to_string.rs:29:21
++ |
++LL | let _: String = rrcow.to_string();
++ | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()`
++ |
++ = help: `&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&std::borrow::Cow<str>`
++ --> $DIR/inefficient_to_string.rs:30:21
++ |
++LL | let _: String = rrrcow.to_string();
++ | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()`
++ |
++ = help: `&&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(exhaustive_patterns, never_type)]
++#![allow(dead_code, unreachable_code, unused_variables)]
++#![allow(clippy::let_and_return)]
++
++enum SingleVariantEnum {
++ Variant(i32),
++}
++
++struct TupleStruct(i32);
++
++enum EmptyEnum {}
++
++macro_rules! match_enum {
++ ($param:expr) => {
++ let data = match $param {
++ SingleVariantEnum::Variant(i) => i,
++ };
++ };
++}
++
++fn infallible_destructuring_match_enum() {
++ let wrapper = SingleVariantEnum::Variant(0);
++
++ // This should lint!
++ let SingleVariantEnum::Variant(data) = wrapper;
++
++ // This shouldn't (inside macro)
++ match_enum!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ SingleVariantEnum::Variant(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ SingleVariantEnum::Variant(i) => -1,
++ };
++
++ let SingleVariantEnum::Variant(data) = wrapper;
++}
++
++macro_rules! match_struct {
++ ($param:expr) => {
++ let data = match $param {
++ TupleStruct(i) => i,
++ };
++ };
++}
++
++fn infallible_destructuring_match_struct() {
++ let wrapper = TupleStruct(0);
++
++ // This should lint!
++ let TupleStruct(data) = wrapper;
++
++ // This shouldn't (inside macro)
++ match_struct!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ TupleStruct(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ TupleStruct(i) => -1,
++ };
++
++ let TupleStruct(data) = wrapper;
++}
++
++macro_rules! match_never_enum {
++ ($param:expr) => {
++ let data = match $param {
++ Ok(i) => i,
++ };
++ };
++}
++
++fn never_enum() {
++ let wrapper: Result<i32, !> = Ok(23);
++
++ // This should lint!
++ let Ok(data) = wrapper;
++
++ // This shouldn't (inside macro)
++ match_never_enum!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ Ok(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ Ok(i) => -1,
++ };
++
++ let Ok(data) = wrapper;
++}
++
++impl EmptyEnum {
++ fn match_on(&self) -> ! {
++ // The lint shouldn't pick this up, as `let` won't work here!
++ let data = match *self {};
++ data
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(exhaustive_patterns, never_type)]
++#![allow(dead_code, unreachable_code, unused_variables)]
++#![allow(clippy::let_and_return)]
++
++enum SingleVariantEnum {
++ Variant(i32),
++}
++
++struct TupleStruct(i32);
++
++enum EmptyEnum {}
++
++macro_rules! match_enum {
++ ($param:expr) => {
++ let data = match $param {
++ SingleVariantEnum::Variant(i) => i,
++ };
++ };
++}
++
++fn infallible_destructuring_match_enum() {
++ let wrapper = SingleVariantEnum::Variant(0);
++
++ // This should lint!
++ let data = match wrapper {
++ SingleVariantEnum::Variant(i) => i,
++ };
++
++ // This shouldn't (inside macro)
++ match_enum!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ SingleVariantEnum::Variant(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ SingleVariantEnum::Variant(i) => -1,
++ };
++
++ let SingleVariantEnum::Variant(data) = wrapper;
++}
++
++macro_rules! match_struct {
++ ($param:expr) => {
++ let data = match $param {
++ TupleStruct(i) => i,
++ };
++ };
++}
++
++fn infallible_destructuring_match_struct() {
++ let wrapper = TupleStruct(0);
++
++ // This should lint!
++ let data = match wrapper {
++ TupleStruct(i) => i,
++ };
++
++ // This shouldn't (inside macro)
++ match_struct!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ TupleStruct(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ TupleStruct(i) => -1,
++ };
++
++ let TupleStruct(data) = wrapper;
++}
++
++macro_rules! match_never_enum {
++ ($param:expr) => {
++ let data = match $param {
++ Ok(i) => i,
++ };
++ };
++}
++
++fn never_enum() {
++ let wrapper: Result<i32, !> = Ok(23);
++
++ // This should lint!
++ let data = match wrapper {
++ Ok(i) => i,
++ };
++
++ // This shouldn't (inside macro)
++ match_never_enum!(wrapper);
++
++ // This shouldn't!
++ let data = match wrapper {
++ Ok(_) => -1,
++ };
++
++ // Neither should this!
++ let data = match wrapper {
++ Ok(i) => -1,
++ };
++
++ let Ok(data) = wrapper;
++}
++
++impl EmptyEnum {
++ fn match_on(&self) -> ! {
++ // The lint shouldn't pick this up, as `let` won't work here!
++ let data = match *self {};
++ data
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++ --> $DIR/infallible_destructuring_match.rs:26:5
++ |
++LL | / let data = match wrapper {
++LL | | SingleVariantEnum::Variant(i) => i,
++LL | | };
++ | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;`
++ |
++ = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings`
++
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++ --> $DIR/infallible_destructuring_match.rs:58:5
++ |
++LL | / let data = match wrapper {
++LL | | TupleStruct(i) => i,
++LL | | };
++ | |______^ help: try this: `let TupleStruct(data) = wrapper;`
++
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++ --> $DIR/infallible_destructuring_match.rs:90:5
++ |
++LL | / let data = match wrapper {
++LL | | Ok(i) => i,
++LL | | };
++ | |______^ help: try this: `let Ok(data) = wrapper;`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++use std::iter::repeat;
++fn square_is_lower_64(x: &u32) -> bool {
++ x * x < 64
++}
++
++#[allow(clippy::maybe_infinite_iter)]
++#[deny(clippy::infinite_iter)]
++fn infinite_iters() {
++ repeat(0_u8).collect::<Vec<_>>(); // infinite iter
++ (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
++ (0..8_u64).chain(0..).max(); // infinite iter
++ (0_usize..)
++ .chain([0usize, 1, 2].iter().cloned())
++ .skip_while(|x| *x != 42)
++ .min(); // infinite iter
++ (0..8_u32)
++ .rev()
++ .cycle()
++ .map(|x| x + 1_u32)
++ .for_each(|x| println!("{}", x)); // infinite iter
++ (0..3_u32).flat_map(|x| x..).sum::<u32>(); // infinite iter
++ (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
++ (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
++ (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended
++ (0..).next(); // iterator is not exhausted
++}
++
++#[deny(clippy::maybe_infinite_iter)]
++fn potential_infinite_iters() {
++ (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
++ repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
++ (1..)
++ .scan(0, |state, x| {
++ *state += x;
++ Some(*state)
++ })
++ .min(); // maybe infinite iter
++ (0..).find(|x| *x == 24); // maybe infinite iter
++ (0..).position(|x| x == 24); // maybe infinite iter
++ (0..).any(|x| x == 24); // maybe infinite iter
++ (0..).all(|x| x == 24); // maybe infinite iter
++
++ (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite
++ repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted
++}
++
++fn main() {
++ infinite_iters();
++ potential_infinite_iters();
++}
++
++mod finite_collect {
++ use std::collections::HashSet;
++ use std::iter::FromIterator;
++
++ struct C;
++ impl FromIterator<i32> for C {
++ fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self {
++ C
++ }
++ }
++
++ fn check_collect() {
++ let _: HashSet<i32> = (0..).collect(); // Infinite iter
++
++ // Some data structures don't collect infinitely, such as `ArrayVec`
++ let _: C = (0..).collect();
++ }
++}
--- /dev/null
--- /dev/null
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:9:5
++ |
++LL | repeat(0_u8).collect::<Vec<_>>(); // infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/infinite_iter.rs:7:8
++ |
++LL | #[deny(clippy::infinite_iter)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:10:5
++ |
++LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:11:5
++ |
++LL | (0..8_u64).chain(0..).max(); // infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:16:5
++ |
++LL | / (0..8_u32)
++LL | | .rev()
++LL | | .cycle()
++LL | | .map(|x| x + 1_u32)
++LL | | .for_each(|x| println!("{}", x)); // infinite iter
++ | |________________________________________^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:22:5
++ |
++LL | (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:23:5
++ |
++LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:30:5
++ |
++LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/infinite_iter.rs:28:8
++ |
++LL | #[deny(clippy::maybe_infinite_iter)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:31:5
++ |
++LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:32:5
++ |
++LL | / (1..)
++LL | | .scan(0, |state, x| {
++LL | | *state += x;
++LL | | Some(*state)
++LL | | })
++LL | | .min(); // maybe infinite iter
++ | |______________^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:38:5
++ |
++LL | (0..).find(|x| *x == 24); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:39:5
++ |
++LL | (0..).position(|x| x == 24); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:40:5
++ |
++LL | (0..).any(|x| x == 24); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++ --> $DIR/infinite_iter.rs:41:5
++ |
++LL | (0..).all(|x| x == 24); // maybe infinite iter
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++ --> $DIR/infinite_iter.rs:64:31
++ |
++LL | let _: HashSet<i32> = (0..).collect(); // Infinite iter
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: `#[deny(clippy::infinite_iter)]` on by default
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++fn fn_val(i: i32) -> i32 {
++ unimplemented!()
++}
++fn fn_constref(i: &i32) -> i32 {
++ unimplemented!()
++}
++fn fn_mutref(i: &mut i32) {
++ unimplemented!()
++}
++fn fooi() -> i32 {
++ unimplemented!()
++}
++fn foob() -> bool {
++ unimplemented!()
++}
++
++#[allow(clippy::many_single_char_names)]
++fn immutable_condition() {
++ // Should warn when all vars mentioned are immutable
++ let y = 0;
++ while y < 10 {
++ println!("KO - y is immutable");
++ }
++
++ let x = 0;
++ while y < 10 && x < 3 {
++ let mut k = 1;
++ k += 2;
++ println!("KO - x and y immutable");
++ }
++
++ let cond = false;
++ while !cond {
++ println!("KO - cond immutable");
++ }
++
++ let mut i = 0;
++ while y < 10 && i < 3 {
++ i += 1;
++ println!("OK - i is mutable");
++ }
++
++ let mut mut_cond = false;
++ while !mut_cond || cond {
++ mut_cond = true;
++ println!("OK - mut_cond is mutable");
++ }
++
++ while fooi() < x {
++ println!("OK - Fn call results may vary");
++ }
++
++ while foob() {
++ println!("OK - Fn call results may vary");
++ }
++
++ let mut a = 0;
++ let mut c = move || {
++ while a < 5 {
++ a += 1;
++ println!("OK - a is mutable");
++ }
++ };
++ c();
++
++ let mut tup = (0, 0);
++ while tup.0 < 5 {
++ tup.0 += 1;
++ println!("OK - tup.0 gets mutated")
++ }
++}
++
++fn unused_var() {
++ // Should warn when a (mutable) var is not used in while body
++ let (mut i, mut j) = (0, 0);
++
++ while i < 3 {
++ j = 3;
++ println!("KO - i not mentioned");
++ }
++
++ while i < 3 && j > 0 {
++ println!("KO - i and j not mentioned");
++ }
++
++ while i < 3 {
++ let mut i = 5;
++ fn_mutref(&mut i);
++ println!("KO - shadowed");
++ }
++
++ while i < 3 && j > 0 {
++ i = 5;
++ println!("OK - i in cond and mentioned");
++ }
++}
++
++fn used_immutable() {
++ let mut i = 0;
++
++ while i < 3 {
++ fn_constref(&i);
++ println!("KO - const reference");
++ }
++
++ while i < 3 {
++ fn_val(i);
++ println!("KO - passed by value");
++ }
++
++ while i < 3 {
++ println!("OK - passed by mutable reference");
++ fn_mutref(&mut i)
++ }
++
++ while i < 3 {
++ fn_mutref(&mut i);
++ println!("OK - passed by mutable reference");
++ }
++}
++
++const N: i32 = 5;
++const B: bool = false;
++
++fn consts() {
++ while false {
++ println!("Constants are not linted");
++ }
++
++ while B {
++ println!("Constants are not linted");
++ }
++
++ while N > 0 {
++ println!("Constants are not linted");
++ }
++}
++
++use std::cell::Cell;
++
++fn maybe_i_mutate(i: &Cell<bool>) {
++ unimplemented!()
++}
++
++fn internally_mutable() {
++ let b = Cell::new(true);
++
++ while b.get() {
++ // b cannot be silently coerced to `bool`
++ maybe_i_mutate(&b);
++ println!("OK - Method call within condition");
++ }
++}
++
++struct Counter {
++ count: usize,
++}
++
++impl Counter {
++ fn inc(&mut self) {
++ self.count += 1;
++ }
++
++ fn inc_n(&mut self, n: usize) {
++ while self.count < n {
++ self.inc();
++ }
++ println!("OK - self borrowed mutably");
++ }
++
++ fn print_n(&self, n: usize) {
++ while self.count < n {
++ println!("KO - {} is not mutated", self.count);
++ }
++ }
++}
++
++fn while_loop_with_break_and_return() {
++ let y = 0;
++ while y < 10 {
++ if y == 0 {
++ break;
++ }
++ println!("KO - loop contains break");
++ }
++
++ while y < 10 {
++ if y == 0 {
++ return;
++ }
++ println!("KO - loop contains return");
++ }
++}
++
++fn main() {
++ immutable_condition();
++ unused_var();
++ used_immutable();
++ internally_mutable();
++
++ let mut c = Counter { count: 0 };
++ c.inc_n(5);
++ c.print_n(2);
++
++ while_loop_with_break_and_return();
++}
--- /dev/null
--- /dev/null
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:21:11
++ |
++LL | while y < 10 {
++ | ^^^^^^
++ |
++ = note: `#[deny(clippy::while_immutable_condition)]` on by default
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:26:11
++ |
++LL | while y < 10 && x < 3 {
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:33:11
++ |
++LL | while !cond {
++ | ^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:77:11
++ |
++LL | while i < 3 {
++ | ^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:82:11
++ |
++LL | while i < 3 && j > 0 {
++ | ^^^^^^^^^^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:86:11
++ |
++LL | while i < 3 {
++ | ^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:101:11
++ |
++LL | while i < 3 {
++ | ^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:106:11
++ |
++LL | while i < 3 {
++ | ^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:172:15
++ |
++LL | while self.count < n {
++ | ^^^^^^^^^^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:180:11
++ |
++LL | while y < 10 {
++ | ^^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++ = note: this loop contains `return`s or `break`s
++ = help: rewrite it as `if cond { loop { } }`
++
++error: variables in the condition are not mutated in the loop body
++ --> $DIR/infinite_loop.rs:187:11
++ |
++LL | while y < 10 {
++ | ^^^^^^
++ |
++ = note: this may lead to an infinite or to a never running loop
++ = note: this loop contains `return`s or `break`s
++ = help: rewrite it as `if cond { loop { } }`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::inherent_to_string)]
++#![deny(clippy::inherent_to_string_shadow_display)]
++#![allow(clippy::many_single_char_names)]
++
++use std::fmt;
++
++trait FalsePositive {
++ fn to_string(&self) -> String;
++}
++
++struct A;
++struct B;
++struct C;
++struct D;
++struct E;
++struct F;
++
++impl A {
++ // Should be detected; emit warning
++ fn to_string(&self) -> String {
++ "A.to_string()".to_string()
++ }
++
++ // Should not be detected as it does not match the function signature
++ fn to_str(&self) -> String {
++ "A.to_str()".to_string()
++ }
++}
++
++// Should not be detected as it is a free function
++fn to_string() -> String {
++ "free to_string()".to_string()
++}
++
++impl B {
++ // Should not be detected, wrong return type
++ fn to_string(&self) -> i32 {
++ 42
++ }
++}
++
++impl C {
++ // Should be detected and emit error as C also implements Display
++ fn to_string(&self) -> String {
++ "C.to_string()".to_string()
++ }
++}
++
++impl fmt::Display for C {
++ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++ write!(f, "impl Display for C")
++ }
++}
++
++impl FalsePositive for D {
++ // Should not be detected, as it is a trait function
++ fn to_string(&self) -> String {
++ "impl FalsePositive for D".to_string()
++ }
++}
++
++impl E {
++ // Should not be detected, as it is not bound to an instance
++ fn to_string() -> String {
++ "E::to_string()".to_string()
++ }
++}
++
++impl F {
++ // Should not be detected, as it does not match the function signature
++ fn to_string(&self, _i: i32) -> String {
++ "F.to_string()".to_string()
++ }
++}
++
++fn main() {
++ let a = A;
++ a.to_string();
++ a.to_str();
++
++ to_string();
++
++ let b = B;
++ b.to_string();
++
++ let c = C;
++ C.to_string();
++
++ let d = D;
++ d.to_string();
++
++ E::to_string();
++
++ let f = F;
++ f.to_string(1);
++}
--- /dev/null
--- /dev/null
++error: implementation of inherent method `to_string(&self) -> String` for type `A`
++ --> $DIR/inherent_to_string.rs:20:5
++ |
++LL | / fn to_string(&self) -> String {
++LL | | "A.to_string()".to_string()
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::inherent-to-string` implied by `-D warnings`
++ = help: implement trait `Display` for type `A` instead
++
++error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
++ --> $DIR/inherent_to_string.rs:44:5
++ |
++LL | / fn to_string(&self) -> String {
++LL | | "C.to_string()".to_string()
++LL | | }
++ | |_____^
++ |
++note: the lint level is defined here
++ --> $DIR/inherent_to_string.rs:2:9
++ |
++LL | #![deny(clippy::inherent_to_string_shadow_display)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: remove the inherent method from type `C`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::inline_fn_without_body)]
++#![allow(clippy::inline_always)]
++
++trait Foo {
++ fn default_inline();
++
++ fn always_inline();
++
++ fn never_inline();
++
++ #[inline]
++ fn has_body() {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::inline_fn_without_body)]
++#![allow(clippy::inline_always)]
++
++trait Foo {
++ #[inline]
++ fn default_inline();
++
++ #[inline(always)]
++ fn always_inline();
++
++ #[inline(never)]
++ fn never_inline();
++
++ #[inline]
++ fn has_body() {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: use of `#[inline]` on trait method `default_inline` which has no body
++ --> $DIR/inline_fn_without_body.rs:7:5
++ |
++LL | #[inline]
++ | _____-^^^^^^^^
++LL | | fn default_inline();
++ | |____- help: remove
++ |
++ = note: `-D clippy::inline-fn-without-body` implied by `-D warnings`
++
++error: use of `#[inline]` on trait method `always_inline` which has no body
++ --> $DIR/inline_fn_without_body.rs:10:5
++ |
++LL | #[inline(always)]
++ | _____-^^^^^^^^^^^^^^^^
++LL | | fn always_inline();
++ | |____- help: remove
++
++error: use of `#[inline]` on trait method `never_inline` which has no body
++ --> $DIR/inline_fn_without_body.rs:13:5
++ |
++LL | #[inline(never)]
++ | _____-^^^^^^^^^^^^^^^
++LL | | fn never_inline();
++ | |____- help: remove
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++#[warn(clippy::int_plus_one)]
++fn main() {
++ let x = 1i32;
++ let y = 0i32;
++
++ let _ = x > y;
++ let _ = y < x;
++
++ let _ = x > y;
++ let _ = y < x;
++
++ let _ = x > y; // should be ok
++ let _ = y < x; // should be ok
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++#[warn(clippy::int_plus_one)]
++fn main() {
++ let x = 1i32;
++ let y = 0i32;
++
++ let _ = x >= y + 1;
++ let _ = y + 1 <= x;
++
++ let _ = x - 1 >= y;
++ let _ = y <= x - 1;
++
++ let _ = x > y; // should be ok
++ let _ = y < x; // should be ok
++}
--- /dev/null
--- /dev/null
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++ --> $DIR/int_plus_one.rs:9:13
++ |
++LL | let _ = x >= y + 1;
++ | ^^^^^^^^^^ help: change it to: `x > y`
++ |
++ = note: `-D clippy::int-plus-one` implied by `-D warnings`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++ --> $DIR/int_plus_one.rs:10:13
++ |
++LL | let _ = y + 1 <= x;
++ | ^^^^^^^^^^ help: change it to: `y < x`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++ --> $DIR/int_plus_one.rs:12:13
++ |
++LL | let _ = x - 1 >= y;
++ | ^^^^^^^^^^ help: change it to: `x > y`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++ --> $DIR/int_plus_one.rs:13:13
++ |
++LL | let _ = y <= x - 1;
++ | ^^^^^^^^^^ help: change it to: `y < x`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
++#![allow(
++ unused,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::op_ref
++)]
++
++#[rustfmt::skip]
++fn main() {
++ let mut i = 1i32;
++ 1 + i;
++ i * 2;
++ 1 %
++ i / 2; // no error, this is part of the expression in the preceding line
++ i - 2 + 2 - i;
++ -i;
++ i >> 1;
++ i << 1;
++
++ // no error, overflows are checked by `overflowing_literals`
++ -1;
++ -(-1);
++
++ i & 1; // no wrapping
++ i | 1;
++ i ^ 1;
++
++ i += 1;
++ i -= 1;
++ i *= 2;
++ i /= 2;
++ i %= 2;
++ i <<= 3;
++ i >>= 2;
++
++ // no errors
++ i |= 1;
++ i &= 1;
++ i ^= i;
++
++ // No errors for the following items because they are constant expressions
++ enum Foo {
++ Bar = -2,
++ }
++ struct Baz([i32; 1 + 1]);
++ union Qux {
++ field: [i32; 1 + 1],
++ }
++ type Alias = [i32; 1 + 1];
++
++ const FOO: i32 = -2;
++ static BAR: i32 = -2;
++
++ let _: [i32; 1 + 1] = [0, 0];
++
++ let _: [i32; 1 + 1] = {
++ let a: [i32; 1 + 1] = [0, 0];
++ a
++ };
++
++ trait Trait {
++ const ASSOC: i32 = 1 + 1;
++ }
++
++ impl Trait for Foo {
++ const ASSOC: i32 = {
++ let _: [i32; 1 + 1];
++ fn foo() {}
++ 1 + 1
++ };
++ }
++}
++
++// warn on references as well! (#5328)
++pub fn int_arith_ref() {
++ 3 + &1;
++ &3 + 1;
++ &3 + &1;
++}
++
++pub fn foo(x: &i32) -> i32 {
++ let a = 5;
++ a + x
++}
++
++pub fn bar(x: &i32, y: &i32) -> i32 {
++ x + y
++}
++
++pub fn baz(x: i32, y: &i32) -> i32 {
++ x + y
++}
++
++pub fn qux(x: i32, y: i32) -> i32 {
++ (&x + &y)
++}
--- /dev/null
--- /dev/null
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:14:5
++ |
++LL | 1 + i;
++ | ^^^^^
++ |
++ = note: `-D clippy::integer-arithmetic` implied by `-D warnings`
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:15:5
++ |
++LL | i * 2;
++ | ^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:16:5
++ |
++LL | / 1 %
++LL | | i / 2; // no error, this is part of the expression in the preceding line
++ | |_________^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:18:5
++ |
++LL | i - 2 + 2 - i;
++ | ^^^^^^^^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:19:5
++ |
++LL | -i;
++ | ^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:20:5
++ |
++LL | i >> 1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:21:5
++ |
++LL | i << 1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:31:5
++ |
++LL | i += 1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:32:5
++ |
++LL | i -= 1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:33:5
++ |
++LL | i *= 2;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:34:5
++ |
++LL | i /= 2;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:35:5
++ |
++LL | i %= 2;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:36:5
++ |
++LL | i <<= 3;
++ | ^^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:37:5
++ |
++LL | i >>= 2;
++ | ^^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:79:5
++ |
++LL | 3 + &1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:80:5
++ |
++LL | &3 + 1;
++ | ^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:81:5
++ |
++LL | &3 + &1;
++ | ^^^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:86:5
++ |
++LL | a + x
++ | ^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:90:5
++ |
++LL | x + y
++ | ^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:94:5
++ |
++LL | x + y
++ | ^^^^^
++
++error: integer arithmetic detected
++ --> $DIR/integer_arithmetic.rs:98:5
++ |
++LL | (&x + &y)
++ | ^^^^^^^^^
++
++error: aborting due to 21 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::integer_division)]
++
++fn main() {
++ let two = 2;
++ let n = 1 / 2;
++ let o = 1 / two;
++ let p = two / 4;
++ let x = 1. / 2.0;
++}
--- /dev/null
--- /dev/null
++error: integer division
++ --> $DIR/integer_division.rs:5:13
++ |
++LL | let n = 1 / 2;
++ | ^^^^^
++ |
++ = note: `-D clippy::integer-division` implied by `-D warnings`
++ = help: division of integers may cause loss of precision. consider using floats.
++
++error: integer division
++ --> $DIR/integer_division.rs:6:13
++ |
++LL | let o = 1 / two;
++ | ^^^^^^^
++ |
++ = help: division of integers may cause loss of precision. consider using floats.
++
++error: integer division
++ --> $DIR/integer_division.rs:7:13
++ |
++LL | let p = two / 4;
++ | ^^^^^^^
++ |
++ = help: division of integers may cause loss of precision. consider using floats.
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::useless_vec)]
++#![warn(clippy::into_iter_on_ref)]
++
++struct X;
++use std::collections::*;
++
++fn main() {
++ for _ in &[1, 2, 3] {}
++ for _ in vec![X, X] {}
++ for _ in &vec![X, X] {}
++
++ let _ = vec![1, 2, 3].into_iter();
++ let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter()
++ let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter()
++ let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
++ let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
++
++ let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
++ let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
++ let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut()
++
++ let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&Vec::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&VecDeque::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&LinkedList::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++ let _ = (&HashMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++
++ let _ = (&BTreeSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&BinaryHeap::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++ let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter()
++ let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter()
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::useless_vec)]
++#![warn(clippy::into_iter_on_ref)]
++
++struct X;
++use std::collections::*;
++
++fn main() {
++ for _ in &[1, 2, 3] {}
++ for _ in vec![X, X] {}
++ for _ in &vec![X, X] {}
++
++ let _ = vec![1, 2, 3].into_iter();
++ let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
++ let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
++ let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++ let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++
++ let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++ let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++ let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
++
++ let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++
++ let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
++ let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
++}
--- /dev/null
--- /dev/null
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
++ --> $DIR/into_iter_on_ref.rs:14:30
++ |
++LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++ |
++ = note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++ --> $DIR/into_iter_on_ref.rs:15:46
++ |
++LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++ --> $DIR/into_iter_on_ref.rs:16:41
++ |
++LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++ --> $DIR/into_iter_on_ref.rs:17:44
++ |
++LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
++ --> $DIR/into_iter_on_ref.rs:19:32
++ |
++LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
++ --> $DIR/into_iter_on_ref.rs:20:36
++ |
++LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array`
++ --> $DIR/into_iter_on_ref.rs:21:40
++ |
++LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option`
++ --> $DIR/into_iter_on_ref.rs:23:24
++ |
++LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option`
++ --> $DIR/into_iter_on_ref.rs:24:28
++ |
++LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result`
++ --> $DIR/into_iter_on_ref.rs:25:32
++ |
++LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result`
++ --> $DIR/into_iter_on_ref.rs:26:37
++ |
++LL | let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
++ --> $DIR/into_iter_on_ref.rs:27:34
++ |
++LL | let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec`
++ --> $DIR/into_iter_on_ref.rs:28:38
++ |
++LL | let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap`
++ --> $DIR/into_iter_on_ref.rs:29:44
++ |
++LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap`
++ --> $DIR/into_iter_on_ref.rs:30:48
++ |
++LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque`
++ --> $DIR/into_iter_on_ref.rs:31:39
++ |
++LL | let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque`
++ --> $DIR/into_iter_on_ref.rs:32:43
++ |
++LL | let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList`
++ --> $DIR/into_iter_on_ref.rs:33:41
++ |
++LL | let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList`
++ --> $DIR/into_iter_on_ref.rs:34:45
++ |
++LL | let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap`
++ --> $DIR/into_iter_on_ref.rs:35:43
++ |
++LL | let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap`
++ --> $DIR/into_iter_on_ref.rs:36:47
++ |
++LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++ | ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet`
++ --> $DIR/into_iter_on_ref.rs:38:39
++ |
++LL | let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap`
++ --> $DIR/into_iter_on_ref.rs:39:41
++ |
++LL | let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet`
++ --> $DIR/into_iter_on_ref.rs:40:38
++ |
++LL | let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path`
++ --> $DIR/into_iter_on_ref.rs:41:43
++ |
++LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf`
++ --> $DIR/into_iter_on_ref.rs:42:47
++ |
++LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
++ | ^^^^^^^^^ help: call directly: `iter`
++
++error: aborting due to 26 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::invalid_upcast_comparisons)]
++#![allow(
++ unused,
++ clippy::eq_op,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::cast_lossless
++)]
++
++fn mk_value<T>() -> T {
++ unimplemented!()
++}
++
++fn main() {
++ let u32: u32 = mk_value();
++ let u8: u8 = mk_value();
++ let i32: i32 = mk_value();
++ let i8: i8 = mk_value();
++
++ // always false, since no u8 can be > 300
++ (u8 as u32) > 300;
++ (u8 as i32) > 300;
++ (u8 as u32) == 300;
++ (u8 as i32) == 300;
++ 300 < (u8 as u32);
++ 300 < (u8 as i32);
++ 300 == (u8 as u32);
++ 300 == (u8 as i32);
++ // inverted of the above
++ (u8 as u32) <= 300;
++ (u8 as i32) <= 300;
++ (u8 as u32) != 300;
++ (u8 as i32) != 300;
++ 300 >= (u8 as u32);
++ 300 >= (u8 as i32);
++ 300 != (u8 as u32);
++ 300 != (u8 as i32);
++
++ // always false, since u8 -> i32 doesn't wrap
++ (u8 as i32) < 0;
++ -5 != (u8 as i32);
++ // inverted of the above
++ (u8 as i32) >= 0;
++ -5 == (u8 as i32);
++
++ // always false, since no u8 can be 1337
++ 1337 == (u8 as i32);
++ 1337 == (u8 as u32);
++ // inverted of the above
++ 1337 != (u8 as i32);
++ 1337 != (u8 as u32);
++
++ // Those are Ok:
++ (u8 as u32) > 20;
++ 42 == (u8 as i32);
++ 42 != (u8 as i32);
++ 42 > (u8 as i32);
++ (u8 as i32) == 42;
++ (u8 as i32) != 42;
++ (u8 as i32) > 42;
++ (u8 as i32) < 42;
++
++ (u8 as i8) == -1;
++ (u8 as i8) != -1;
++ (u8 as i32) > -1;
++ (u8 as i32) < -1;
++ (u32 as i32) < -5;
++ (u32 as i32) < 10;
++
++ (i8 as u8) == 1;
++ (i8 as u8) != 1;
++ (i8 as u8) < 1;
++ (i8 as u8) > 1;
++ (i32 as u32) < 5;
++ (i32 as u32) < 10;
++
++ -5 < (u32 as i32);
++ 0 <= (u32 as i32);
++ 0 < (u32 as i32);
++
++ -5 > (u32 as i32);
++ -5 >= (u8 as i32);
++
++ -5 == (u32 as i32);
++}
--- /dev/null
--- /dev/null
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:21:5
++ |
++LL | (u8 as u32) > 300;
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::invalid-upcast-comparisons` implied by `-D warnings`
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:22:5
++ |
++LL | (u8 as i32) > 300;
++ | ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:23:5
++ |
++LL | (u8 as u32) == 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:24:5
++ |
++LL | (u8 as i32) == 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:25:5
++ |
++LL | 300 < (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:26:5
++ |
++LL | 300 < (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:27:5
++ |
++LL | 300 == (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:28:5
++ |
++LL | 300 == (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:30:5
++ |
++LL | (u8 as u32) <= 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:31:5
++ |
++LL | (u8 as i32) <= 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:32:5
++ |
++LL | (u8 as u32) != 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:33:5
++ |
++LL | (u8 as i32) != 300;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:34:5
++ |
++LL | 300 >= (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:35:5
++ |
++LL | 300 >= (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:36:5
++ |
++LL | 300 != (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:37:5
++ |
++LL | 300 != (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:40:5
++ |
++LL | (u8 as i32) < 0;
++ | ^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:41:5
++ |
++LL | -5 != (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:43:5
++ |
++LL | (u8 as i32) >= 0;
++ | ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:44:5
++ |
++LL | -5 == (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:47:5
++ |
++LL | 1337 == (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:48:5
++ |
++LL | 1337 == (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:50:5
++ |
++LL | 1337 != (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:51:5
++ |
++LL | 1337 != (u8 as u32);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++ --> $DIR/invalid_upcast_comparisons.rs:65:5
++ |
++LL | (u8 as i32) > -1;
++ | ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:66:5
++ |
++LL | (u8 as i32) < -1;
++ | ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++ --> $DIR/invalid_upcast_comparisons.rs:82:5
++ |
++LL | -5 >= (u8 as i32);
++ | ^^^^^^^^^^^^^^^^^
++
++error: aborting due to 27 previous errors
++
--- /dev/null
--- /dev/null
++fn main() {
++ println!("{}" a); //~ERROR expected token: `,`
++}
--- /dev/null
--- /dev/null
++error: expected token: `,`
++ --> $DIR/issue-3145.rs:2:19
++ |
++LL | println!("{}" a); //~ERROR expected token: `,`
++ | ^ expected `,`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// ignore-macos
++// ignore-windows
++
++#![warn(clippy::empty_loop)]
++#![feature(lang_items, link_args, start, libc)]
++#![link_args = "-nostartfiles"]
++#![no_std]
++
++use core::panic::PanicInfo;
++
++#[start]
++fn main(argc: isize, argv: *const *const u8) -> isize {
++ loop {}
++}
++
++#[panic_handler]
++fn panic(_info: &PanicInfo) -> ! {
++ loop {}
++}
++
++#[lang = "eh_personality"]
++extern "C" fn eh_personality() {}
--- /dev/null
--- /dev/null
++#![deny(clippy::while_let_on_iterator)]
++
++use std::iter::Iterator;
++
++struct Foo;
++
++impl Foo {
++ fn foo1<I: Iterator<Item = usize>>(mut it: I) {
++ while let Some(_) = it.next() {
++ println!("{:?}", it.size_hint());
++ }
++ }
++
++ fn foo2<I: Iterator<Item = usize>>(mut it: I) {
++ while let Some(e) = it.next() {
++ println!("{:?}", e);
++ }
++ }
++}
++
++fn main() {
++ Foo::foo1(vec![].into_iter());
++ Foo::foo2(vec![].into_iter());
++}
--- /dev/null
--- /dev/null
++error: this loop could be written as a `for` loop
++ --> $DIR/issue_2356.rs:15:9
++ |
++LL | while let Some(e) = it.next() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it`
++ |
++note: the lint level is defined here
++ --> $DIR/issue_2356.rs:1:9
++ |
++LL | #![deny(clippy::while_let_on_iterator)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// edition:2018
++#![allow(dead_code)]
++
++async fn sink1<'a>(_: &'a str) {} // lint
++async fn sink1_elided(_: &str) {} // ok
++
++// lint
++async fn one_to_one<'a>(s: &'a str) -> &'a str {
++ s
++}
++
++// ok
++async fn one_to_one_elided(s: &str) -> &str {
++ s
++}
++
++// ok
++async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str {
++ a
++}
++
++// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn
++
++// #3988
++struct Foo;
++impl Foo {
++ // ok
++ pub async fn foo(&mut self) {}
++}
++
++// rust-lang/rust#61115
++// ok
++async fn print(s: &str) {
++ println!("{}", s);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/issue_4266.rs:4:1
++ |
++LL | async fn sink1<'a>(_: &'a str) {} // lint
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/issue_4266.rs:8:1
++ |
++LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::items_after_statements)]
++
++fn ok() {
++ fn foo() {
++ println!("foo");
++ }
++ foo();
++}
++
++fn last() {
++ foo();
++ fn foo() {
++ println!("foo");
++ }
++}
++
++fn main() {
++ foo();
++ fn foo() {
++ println!("foo");
++ }
++ foo();
++}
++
++fn mac() {
++ let mut a = 5;
++ println!("{}", a);
++ // do not lint this, because it needs to be after `a`
++ macro_rules! b {
++ () => {{
++ a = 6
++ }};
++ }
++ b!();
++ println!("{}", a);
++}
--- /dev/null
--- /dev/null
++error: adding items after statements is confusing, since items exist from the start of the scope
++ --> $DIR/item_after_statement.rs:12:5
++ |
++LL | / fn foo() {
++LL | | println!("foo");
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::items-after-statements` implied by `-D warnings`
++
++error: adding items after statements is confusing, since items exist from the start of the scope
++ --> $DIR/item_after_statement.rs:19:5
++ |
++LL | / fn foo() {
++LL | | println!("foo");
++LL | | }
++ | |_____^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++
++use std::collections::HashSet;
++use std::collections::VecDeque;
++
++fn main() {
++ let v = [1, 2, 3, 4, 5];
++ let v2: Vec<isize> = v.to_vec();
++ let v3: HashSet<isize> = v.iter().cloned().collect();
++ let v4: VecDeque<isize> = v.iter().cloned().collect();
++
++ // Handle macro expansion in suggestion
++ let _: Vec<isize> = vec![1, 2, 3].to_vec();
++
++ // Issue #3704
++ unsafe {
++ let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
++ .to_bytes().to_vec();
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++
++use std::collections::HashSet;
++use std::collections::VecDeque;
++
++fn main() {
++ let v = [1, 2, 3, 4, 5];
++ let v2: Vec<isize> = v.iter().cloned().collect();
++ let v3: HashSet<isize> = v.iter().cloned().collect();
++ let v4: VecDeque<isize> = v.iter().cloned().collect();
++
++ // Handle macro expansion in suggestion
++ let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
++
++ // Issue #3704
++ unsafe {
++ let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
++ .to_bytes()
++ .iter()
++ .cloned()
++ .collect();
++ }
++}
--- /dev/null
--- /dev/null
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++ --> $DIR/iter_cloned_collect.rs:10:27
++ |
++LL | let v2: Vec<isize> = v.iter().cloned().collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
++ |
++ = note: `-D clippy::iter-cloned-collect` implied by `-D warnings`
++
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++ --> $DIR/iter_cloned_collect.rs:15:38
++ |
++LL | let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
++
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++ --> $DIR/iter_cloned_collect.rs:20:24
++ |
++LL | .to_bytes()
++ | ________________________^
++LL | | .iter()
++LL | | .cloned()
++LL | | .collect();
++ | |______________________^ help: try: `.to_vec()`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:option_helpers.rs
++
++#![warn(clippy::iter_nth)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use option_helpers::IteratorFalsePositives;
++use std::collections::VecDeque;
++
++/// Struct to generate false positives for things with `.iter()`.
++#[derive(Copy, Clone)]
++struct HasIter;
++
++impl HasIter {
++ fn iter(self) -> IteratorFalsePositives {
++ IteratorFalsePositives { foo: 0 }
++ }
++
++ fn iter_mut(self) -> IteratorFalsePositives {
++ IteratorFalsePositives { foo: 0 }
++ }
++}
++
++/// Checks implementation of `ITER_NTH` lint.
++fn iter_nth() {
++ let mut some_vec = vec![0, 1, 2, 3];
++ let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++ let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect();
++
++ {
++ // Make sure we lint `.iter()` for relevant types.
++ let bad_vec = some_vec.iter().nth(3);
++ let bad_slice = &some_vec[..].iter().nth(3);
++ let bad_boxed_slice = boxed_slice.iter().nth(3);
++ let bad_vec_deque = some_vec_deque.iter().nth(3);
++ }
++
++ {
++ // Make sure we lint `.iter_mut()` for relevant types.
++ let bad_vec = some_vec.iter_mut().nth(3);
++ }
++ {
++ let bad_slice = &some_vec[..].iter_mut().nth(3);
++ }
++ {
++ let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
++ }
++
++ // Make sure we don't lint for non-relevant types.
++ let false_positive = HasIter;
++ let ok = false_positive.iter().nth(3);
++ let ok_mut = false_positive.iter_mut().nth(3);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `.iter().nth()` on a Vec
++ --> $DIR/iter_nth.rs:33:23
++ |
++LL | let bad_vec = some_vec.iter().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::iter-nth` implied by `-D warnings`
++ = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a slice
++ --> $DIR/iter_nth.rs:34:26
++ |
++LL | let bad_slice = &some_vec[..].iter().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a slice
++ --> $DIR/iter_nth.rs:35:31
++ |
++LL | let bad_boxed_slice = boxed_slice.iter().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a VecDeque
++ --> $DIR/iter_nth.rs:36:29
++ |
++LL | let bad_vec_deque = some_vec_deque.iter().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a Vec
++ --> $DIR/iter_nth.rs:41:23
++ |
++LL | let bad_vec = some_vec.iter_mut().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get_mut()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a slice
++ --> $DIR/iter_nth.rs:44:26
++ |
++LL | let bad_slice = &some_vec[..].iter_mut().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get_mut()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a VecDeque
++ --> $DIR/iter_nth.rs:47:29
++ |
++LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: calling `.get_mut()` is both faster and more readable
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::iter_nth_zero)]
++use std::collections::HashSet;
++
++struct Foo {}
++
++impl Foo {
++ fn nth(&self, index: usize) -> usize {
++ index + 1
++ }
++}
++
++fn main() {
++ let f = Foo {};
++ f.nth(0); // lint does not apply here
++
++ let mut s = HashSet::new();
++ s.insert(1);
++ let _x = s.iter().next();
++
++ let mut s2 = HashSet::new();
++ s2.insert(2);
++ let mut iter = s2.iter();
++ let _y = iter.next();
++
++ let mut s3 = HashSet::new();
++ s3.insert(3);
++ let mut iter2 = s3.iter();
++ let _unwrapped = iter2.next().unwrap();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::iter_nth_zero)]
++use std::collections::HashSet;
++
++struct Foo {}
++
++impl Foo {
++ fn nth(&self, index: usize) -> usize {
++ index + 1
++ }
++}
++
++fn main() {
++ let f = Foo {};
++ f.nth(0); // lint does not apply here
++
++ let mut s = HashSet::new();
++ s.insert(1);
++ let _x = s.iter().nth(0);
++
++ let mut s2 = HashSet::new();
++ s2.insert(2);
++ let mut iter = s2.iter();
++ let _y = iter.nth(0);
++
++ let mut s3 = HashSet::new();
++ s3.insert(3);
++ let mut iter2 = s3.iter();
++ let _unwrapped = iter2.nth(0).unwrap();
++}
--- /dev/null
--- /dev/null
++error: called `.nth(0)` on a `std::iter::Iterator`
++ --> $DIR/iter_nth_zero.rs:20:14
++ |
++LL | let _x = s.iter().nth(0);
++ | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()`
++ |
++ = note: `-D clippy::iter-nth-zero` implied by `-D warnings`
++
++error: called `.nth(0)` on a `std::iter::Iterator`
++ --> $DIR/iter_nth_zero.rs:25:14
++ |
++LL | let _y = iter.nth(0);
++ | ^^^^^^^^^^^ help: try calling: `iter.next()`
++
++error: called `.nth(0)` on a `std::iter::Iterator`
++ --> $DIR/iter_nth_zero.rs:30:22
++ |
++LL | let _unwrapped = iter2.nth(0).unwrap();
++ | ^^^^^^^^^^^^ help: try calling: `iter2.next()`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:option_helpers.rs
++
++#![warn(clippy::iter_skip_next)]
++#![allow(clippy::blacklisted_name)]
++
++extern crate option_helpers;
++
++use option_helpers::IteratorFalsePositives;
++
++/// Checks implementation of `ITER_SKIP_NEXT` lint
++fn iter_skip_next() {
++ let mut some_vec = vec![0, 1, 2, 3];
++ let _ = some_vec.iter().skip(42).next();
++ let _ = some_vec.iter().cycle().skip(42).next();
++ let _ = (1..10).skip(10).next();
++ let _ = &some_vec[..].iter().skip(3).next();
++ let foo = IteratorFalsePositives { foo: 0 };
++ let _ = foo.skip(42).next();
++ let _ = foo.filter().skip(42).next();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `skip(x).next()` on an iterator
++ --> $DIR/iter_skip_next.rs:13:13
++ |
++LL | let _ = some_vec.iter().skip(42).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::iter-skip-next` implied by `-D warnings`
++ = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++ --> $DIR/iter_skip_next.rs:14:13
++ |
++LL | let _ = some_vec.iter().cycle().skip(42).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++ --> $DIR/iter_skip_next.rs:15:13
++ |
++LL | let _ = (1..10).skip(10).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++ --> $DIR/iter_skip_next.rs:16:14
++ |
++LL | let _ = &some_vec[..].iter().skip(3).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: this is more succinctly expressed by calling `nth(x)`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::iterator_step_by_zero)]
++fn main() {
++ let _ = vec!["A", "B", "B"].iter().step_by(0);
++ let _ = "XXX".chars().step_by(0);
++ let _ = (0..1).step_by(0);
++
++ // No error, not an iterator.
++ let y = NotIterator;
++ y.step_by(0);
++
++ // No warning for non-zero step
++ let _ = (0..1).step_by(1);
++
++ let _ = (1..).step_by(0);
++ let _ = (1..=2).step_by(0);
++
++ let x = 0..1;
++ let _ = x.step_by(0);
++
++ // check const eval
++ let v1 = vec![1, 2, 3];
++ let _ = v1.iter().step_by(2 / 3);
++}
++
++struct NotIterator;
++impl NotIterator {
++ fn step_by(&self, _: u32) {}
++}
--- /dev/null
--- /dev/null
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:3:13
++ |
++LL | let _ = vec!["A", "B", "B"].iter().step_by(0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings`
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:4:13
++ |
++LL | let _ = "XXX".chars().step_by(0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:5:13
++ |
++LL | let _ = (0..1).step_by(0);
++ | ^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:14:13
++ |
++LL | let _ = (1..).step_by(0);
++ | ^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:15:13
++ |
++LL | let _ = (1..=2).step_by(0);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:18:13
++ |
++LL | let _ = x.step_by(0);
++ | ^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++ --> $DIR/iterator_step_by_zero.rs:22:13
++ |
++LL | let _ = v1.iter().step_by(2 / 3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::large_const_arrays)]
++#![allow(dead_code)]
++
++#[derive(Clone, Copy)]
++pub struct S {
++ pub data: [u64; 32],
++}
++
++// Should lint
++pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++
++// Good
++pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
++pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
++const G_FOO: [u32; 1_000] = [0u32; 1_000];
++
++fn main() {
++ // Should lint
++ pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++ static BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++ pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++ static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++
++ // Good
++ pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
++ const G_BAR: [u32; 1_000] = [0u32; 1_000];
++ pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
++ const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
++ pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
++ const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::large_const_arrays)]
++#![allow(dead_code)]
++
++#[derive(Clone, Copy)]
++pub struct S {
++ pub data: [u64; 32],
++}
++
++// Should lint
++pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++
++// Good
++pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
++pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
++const G_FOO: [u32; 1_000] = [0u32; 1_000];
++
++fn main() {
++ // Should lint
++ pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++ const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++ pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++ const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++
++ // Good
++ pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
++ const G_BAR: [u32; 1_000] = [0u32; 1_000];
++ pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
++ const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
++ pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
++ const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
++}
--- /dev/null
--- /dev/null
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:12:1
++ |
++LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++ | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++ |
++ = note: `-D clippy::large-const-arrays` implied by `-D warnings`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:13:1
++ |
++LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:14:1
++ |
++LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:23:5
++ |
++LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:24:5
++ |
++LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:25:5
++ |
++LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:26:5
++ |
++LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:27:5
++ |
++LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++ | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: large array defined as const
++ --> $DIR/large_const_arrays.rs:28:5
++ |
++LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ | |
++ | help: make this a static item: `static`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::large_digit_groups)]
++
++fn main() {
++ macro_rules! mac {
++ () => {
++ 0b1_10110_i64
++ };
++ }
++
++ let _good = (
++ 0b1011_i64,
++ 0o1_234_u32,
++ 0x1_234_567,
++ 1_2345_6789,
++ 1234_f32,
++ 1_234.12_f32,
++ 1_234.123_f32,
++ 1.123_4_f32,
++ );
++ let _bad = (
++ 0b11_0110_i64,
++ 0xdead_beef_usize,
++ 123_456_f32,
++ 123_456.12_f32,
++ 123_456.123_45_f64,
++ 123_456.123_456_f64,
++ );
++ // Ignore literals in macros
++ let _ = mac!();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::large_digit_groups)]
++
++fn main() {
++ macro_rules! mac {
++ () => {
++ 0b1_10110_i64
++ };
++ }
++
++ let _good = (
++ 0b1011_i64,
++ 0o1_234_u32,
++ 0x1_234_567,
++ 1_2345_6789,
++ 1234_f32,
++ 1_234.12_f32,
++ 1_234.123_f32,
++ 1.123_4_f32,
++ );
++ let _bad = (
++ 0b1_10110_i64,
++ 0xd_e_adbee_f_usize,
++ 1_23456_f32,
++ 1_23456.12_f32,
++ 1_23456.12345_f64,
++ 1_23456.12345_6_f64,
++ );
++ // Ignore literals in macros
++ let _ = mac!();
++}
--- /dev/null
--- /dev/null
++error: digit groups should be smaller
++ --> $DIR/large_digit_groups.rs:22:9
++ |
++LL | 0b1_10110_i64,
++ | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
++ |
++ = note: `-D clippy::large-digit-groups` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/large_digit_groups.rs:23:9
++ |
++LL | 0xd_e_adbee_f_usize,
++ | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
++ |
++ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digit groups should be smaller
++ --> $DIR/large_digit_groups.rs:24:9
++ |
++LL | 1_23456_f32,
++ | ^^^^^^^^^^^ help: consider: `123_456_f32`
++
++error: digit groups should be smaller
++ --> $DIR/large_digit_groups.rs:25:9
++ |
++LL | 1_23456.12_f32,
++ | ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32`
++
++error: digit groups should be smaller
++ --> $DIR/large_digit_groups.rs:26:9
++ |
++LL | 1_23456.12345_f64,
++ | ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64`
++
++error: digit groups should be smaller
++ --> $DIR/large_digit_groups.rs:27:9
++ |
++LL | 1_23456.12345_6_f64,
++ | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![allow(unused_variables)]
++#![warn(clippy::large_enum_variant)]
++
++enum LargeEnum {
++ A(i32),
++ B([i32; 8000]),
++}
++
++enum GenericEnumOk<T> {
++ A(i32),
++ B([T; 8000]),
++}
++
++enum GenericEnum2<T> {
++ A(i32),
++ B([i32; 8000]),
++ C(T, [i32; 8000]),
++}
++
++trait SomeTrait {
++ type Item;
++}
++
++enum LargeEnumGeneric<A: SomeTrait> {
++ Var(A::Item),
++}
++
++enum LargeEnum2 {
++ VariantOk(i32, u32),
++ ContainingLargeEnum(LargeEnum),
++}
++enum LargeEnum3 {
++ ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
++ VoidVariant,
++ StructLikeLittle { x: i32, y: i32 },
++}
++
++enum LargeEnum4 {
++ VariantOk(i32, u32),
++ StructLikeLarge { x: [i32; 8000], y: i32 },
++}
++
++enum LargeEnum5 {
++ VariantOk(i32, u32),
++ StructLikeLarge2 { x: [i32; 8000] },
++}
++
++enum LargeEnumOk {
++ LargeA([i32; 8000]),
++ LargeB([i32; 8001]),
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:7:5
++ |
++LL | B([i32; 8000]),
++ | ^^^^^^^^^^^^^^ this variant is 32000 bytes
++ |
++ = note: `-D clippy::large-enum-variant` implied by `-D warnings`
++note: and the second-largest variant is 4 bytes:
++ --> $DIR/large_enum_variant.rs:6:5
++ |
++LL | A(i32),
++ | ^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | B(Box<[i32; 8000]>),
++ | ^^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:31:5
++ |
++LL | ContainingLargeEnum(LargeEnum),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
++ |
++note: and the second-largest variant is 8 bytes:
++ --> $DIR/large_enum_variant.rs:30:5
++ |
++LL | VariantOk(i32, u32),
++ | ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | ContainingLargeEnum(Box<LargeEnum>),
++ | ^^^^^^^^^^^^^^
++
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:41:5
++ |
++LL | StructLikeLarge { x: [i32; 8000], y: i32 },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
++ |
++note: and the second-largest variant is 8 bytes:
++ --> $DIR/large_enum_variant.rs:40:5
++ |
++LL | VariantOk(i32, u32),
++ | ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ --> $DIR/large_enum_variant.rs:41:5
++ |
++LL | StructLikeLarge { x: [i32; 8000], y: i32 },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++ --> $DIR/large_enum_variant.rs:46:5
++ |
++LL | StructLikeLarge2 { x: [i32; 8000] },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
++ |
++note: and the second-largest variant is 8 bytes:
++ --> $DIR/large_enum_variant.rs:45:5
++ |
++LL | VariantOk(i32, u32),
++ | ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++ |
++LL | StructLikeLarge2 { x: Box<[i32; 8000]> },
++ | ^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::large_stack_arrays)]
++#![allow(clippy::large_enum_variant)]
++
++#[derive(Clone, Copy)]
++struct S {
++ pub data: [u64; 32],
++}
++
++#[derive(Clone, Copy)]
++enum E {
++ S(S),
++ T(u32),
++}
++
++fn main() {
++ let bad = (
++ [0u32; 20_000_000],
++ [S { data: [0; 32] }; 5000],
++ [Some(""); 20_000_000],
++ [E::T(0); 5000],
++ );
++
++ let good = (
++ [0u32; 1000],
++ [S { data: [0; 32] }; 1000],
++ [Some(""); 1000],
++ [E::T(0); 1000],
++ [(); 20_000_000],
++ );
++}
--- /dev/null
--- /dev/null
++error: allocating a local array larger than 512000 bytes
++ --> $DIR/large_stack_arrays.rs:17:9
++ |
++LL | [0u32; 20_000_000],
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
++ = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++ --> $DIR/large_stack_arrays.rs:18:9
++ |
++LL | [S { data: [0; 32] }; 5000],
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++ --> $DIR/large_stack_arrays.rs:19:9
++ |
++LL | [Some(""); 20_000_000],
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++ --> $DIR/large_stack_arrays.rs:20:9
++ |
++LL | [E::T(0); 5000],
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::len_without_is_empty)]
++#![allow(dead_code, unused)]
++
++pub struct PubOne;
++
++impl PubOne {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++}
++
++impl PubOne {
++ // A second impl for this struct -- the error span shouldn't mention this.
++ pub fn irrelevant(self: &Self) -> bool {
++ false
++ }
++}
++
++// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`.
++pub struct PubAllowed;
++
++#[allow(clippy::len_without_is_empty)]
++impl PubAllowed {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++}
++
++// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
++// impl containing `len`.
++impl PubAllowed {
++ pub fn irrelevant(self: &Self) -> bool {
++ false
++ }
++}
++
++pub trait PubTraitsToo {
++ fn len(self: &Self) -> isize;
++}
++
++impl PubTraitsToo for One {
++ fn len(self: &Self) -> isize {
++ 0
++ }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ pub fn is_empty(self: &Self, x: u32) -> bool {
++ false
++ }
++}
++
++struct NotPubOne;
++
++impl NotPubOne {
++ pub fn len(self: &Self) -> isize {
++ // No error; `len` is pub but `NotPubOne` is not exported anyway.
++ 1
++ }
++}
++
++struct One;
++
++impl One {
++ fn len(self: &Self) -> isize {
++ // No error; `len` is private; see issue #1085.
++ 1
++ }
++}
++
++trait TraitsToo {
++ fn len(self: &Self) -> isize;
++ // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++ fn len(self: &Self) -> isize {
++ 0
++ }
++}
++
++struct HasPrivateIsEmpty;
++
++impl HasPrivateIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++struct Wither;
++
++pub trait WithIsEmpty {
++ fn len(self: &Self) -> isize;
++ fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++ fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++pub trait Empty {
++ fn is_empty(&self) -> bool;
++}
++
++pub trait InheritingEmpty: Empty {
++ // Must not trigger `LEN_WITHOUT_IS_EMPTY`.
++ fn len(&self) -> isize;
++}
++
++// This used to ICE.
++pub trait Foo: Sized {}
++
++pub trait DependsOnFoo: Foo {
++ fn len(&mut self) -> usize;
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: item `PubOne` has a public `len` method but no corresponding `is_empty` method
++ --> $DIR/len_without_is_empty.rs:6:1
++ |
++LL | / impl PubOne {
++LL | | pub fn len(self: &Self) -> isize {
++LL | | 1
++LL | | }
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::len-without-is-empty` implied by `-D warnings`
++
++error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method
++ --> $DIR/len_without_is_empty.rs:37:1
++ |
++LL | / pub trait PubTraitsToo {
++LL | | fn len(self: &Self) -> isize;
++LL | | }
++ | |_^
++
++error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method
++ --> $DIR/len_without_is_empty.rs:49:1
++ |
++LL | / impl HasIsEmpty {
++LL | | pub fn len(self: &Self) -> isize {
++LL | | 1
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_^
++
++error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method
++ --> $DIR/len_without_is_empty.rs:61:1
++ |
++LL | / impl HasWrongIsEmpty {
++LL | | pub fn len(self: &Self) -> isize {
++LL | | 1
++LL | | }
++... |
++LL | | }
++LL | | }
++ | |_^
++
++error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method
++ --> $DIR/len_without_is_empty.rs:141:1
++ |
++LL | / pub trait DependsOnFoo: Foo {
++LL | | fn len(&mut self) -> usize;
++LL | | }
++ | |_^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::len_zero)]
++#![allow(dead_code, unused, clippy::len_without_is_empty)]
++
++pub struct One;
++struct Wither;
++
++trait TraitsToo {
++ fn len(self: &Self) -> isize;
++ // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++ fn len(self: &Self) -> isize {
++ 0
++ }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ pub fn is_empty(self: &Self, x: u32) -> bool {
++ false
++ }
++}
++
++pub trait WithIsEmpty {
++ fn len(self: &Self) -> isize;
++ fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++ fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++fn main() {
++ let x = [1, 2];
++ if x.is_empty() {
++ println!("This should not happen!");
++ }
++
++ if "".is_empty() {}
++
++ let y = One;
++ if y.len() == 0 {
++ // No error; `One` does not have `.is_empty()`.
++ println!("This should not happen either!");
++ }
++
++ let z: &dyn TraitsToo = &y;
++ if z.len() > 0 {
++ // No error; `TraitsToo` has no `.is_empty()` method.
++ println!("Nor should this!");
++ }
++
++ let has_is_empty = HasIsEmpty;
++ if has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if has_is_empty.len() > 1 {
++ // No error.
++ println!("This can happen.");
++ }
++ if has_is_empty.len() <= 1 {
++ // No error.
++ println!("This can happen.");
++ }
++ if has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if !has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if has_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ if 1 < has_is_empty.len() {
++ // No error.
++ println!("This can happen.");
++ }
++ if 1 >= has_is_empty.len() {
++ // No error.
++ println!("This can happen.");
++ }
++ assert!(!has_is_empty.is_empty());
++
++ let with_is_empty: &dyn WithIsEmpty = &Wither;
++ if with_is_empty.is_empty() {
++ println!("Or this!");
++ }
++ assert!(!with_is_empty.is_empty());
++
++ let has_wrong_is_empty = HasWrongIsEmpty;
++ if has_wrong_is_empty.len() == 0 {
++ // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
++ println!("Or this!");
++ }
++}
++
++fn test_slice(b: &[u8]) {
++ if !b.is_empty() {}
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::len_zero)]
++#![allow(dead_code, unused, clippy::len_without_is_empty)]
++
++pub struct One;
++struct Wither;
++
++trait TraitsToo {
++ fn len(self: &Self) -> isize;
++ // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++ fn len(self: &Self) -> isize {
++ 0
++ }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++ pub fn len(self: &Self) -> isize {
++ 1
++ }
++
++ pub fn is_empty(self: &Self, x: u32) -> bool {
++ false
++ }
++}
++
++pub trait WithIsEmpty {
++ fn len(self: &Self) -> isize;
++ fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++ fn len(self: &Self) -> isize {
++ 1
++ }
++
++ fn is_empty(self: &Self) -> bool {
++ false
++ }
++}
++
++fn main() {
++ let x = [1, 2];
++ if x.len() == 0 {
++ println!("This should not happen!");
++ }
++
++ if "".len() == 0 {}
++
++ let y = One;
++ if y.len() == 0 {
++ // No error; `One` does not have `.is_empty()`.
++ println!("This should not happen either!");
++ }
++
++ let z: &dyn TraitsToo = &y;
++ if z.len() > 0 {
++ // No error; `TraitsToo` has no `.is_empty()` method.
++ println!("Nor should this!");
++ }
++
++ let has_is_empty = HasIsEmpty;
++ if has_is_empty.len() == 0 {
++ println!("Or this!");
++ }
++ if has_is_empty.len() != 0 {
++ println!("Or this!");
++ }
++ if has_is_empty.len() > 0 {
++ println!("Or this!");
++ }
++ if has_is_empty.len() < 1 {
++ println!("Or this!");
++ }
++ if has_is_empty.len() >= 1 {
++ println!("Or this!");
++ }
++ if has_is_empty.len() > 1 {
++ // No error.
++ println!("This can happen.");
++ }
++ if has_is_empty.len() <= 1 {
++ // No error.
++ println!("This can happen.");
++ }
++ if 0 == has_is_empty.len() {
++ println!("Or this!");
++ }
++ if 0 != has_is_empty.len() {
++ println!("Or this!");
++ }
++ if 0 < has_is_empty.len() {
++ println!("Or this!");
++ }
++ if 1 <= has_is_empty.len() {
++ println!("Or this!");
++ }
++ if 1 > has_is_empty.len() {
++ println!("Or this!");
++ }
++ if 1 < has_is_empty.len() {
++ // No error.
++ println!("This can happen.");
++ }
++ if 1 >= has_is_empty.len() {
++ // No error.
++ println!("This can happen.");
++ }
++ assert!(!has_is_empty.is_empty());
++
++ let with_is_empty: &dyn WithIsEmpty = &Wither;
++ if with_is_empty.len() == 0 {
++ println!("Or this!");
++ }
++ assert!(!with_is_empty.is_empty());
++
++ let has_wrong_is_empty = HasWrongIsEmpty;
++ if has_wrong_is_empty.len() == 0 {
++ // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
++ println!("Or this!");
++ }
++}
++
++fn test_slice(b: &[u8]) {
++ if b.len() != 0 {}
++}
--- /dev/null
--- /dev/null
++error: length comparison to zero
++ --> $DIR/len_zero.rs:61:8
++ |
++LL | if x.len() == 0 {
++ | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()`
++ |
++ = note: `-D clippy::len-zero` implied by `-D warnings`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:65:8
++ |
++LL | if "".len() == 0 {}
++ | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:80:8
++ |
++LL | if has_is_empty.len() == 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:83:8
++ |
++LL | if has_is_empty.len() != 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:86:8
++ |
++LL | if has_is_empty.len() > 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++ --> $DIR/len_zero.rs:89:8
++ |
++LL | if has_is_empty.len() < 1 {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to one
++ --> $DIR/len_zero.rs:92:8
++ |
++LL | if has_is_empty.len() >= 1 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:103:8
++ |
++LL | if 0 == has_is_empty.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:106:8
++ |
++LL | if 0 != has_is_empty.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:109:8
++ |
++LL | if 0 < has_is_empty.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++ --> $DIR/len_zero.rs:112:8
++ |
++LL | if 1 <= has_is_empty.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++ --> $DIR/len_zero.rs:115:8
++ |
++LL | if 1 > has_is_empty.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:129:8
++ |
++LL | if with_is_empty.len() == 0 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()`
++
++error: length comparison to zero
++ --> $DIR/len_zero.rs:142:8
++ |
++LL | if b.len() != 0 {}
++ | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(
++ unused_variables,
++ unused_assignments,
++ clippy::similar_names,
++ clippy::blacklisted_name
++)]
++#![warn(clippy::useless_let_if_seq)]
++
++fn f() -> bool {
++ true
++}
++fn g(x: i32) -> i32 {
++ x + 1
++}
++
++fn issue985() -> i32 {
++ let mut x = 42;
++ if f() {
++ x = g(x);
++ }
++
++ x
++}
++
++fn issue985_alt() -> i32 {
++ let mut x = 42;
++ if f() {
++ f();
++ } else {
++ x = g(x);
++ }
++
++ x
++}
++
++fn issue975() -> String {
++ let mut udn = "dummy".to_string();
++ if udn.starts_with("uuid:") {
++ udn = String::from(&udn[5..]);
++ }
++ udn
++}
++
++fn early_return() -> u8 {
++ // FIXME: we could extend the lint to include such cases:
++ let foo;
++
++ if f() {
++ return 42;
++ } else {
++ foo = 0;
++ }
++
++ foo
++}
++
++fn main() {
++ early_return();
++ issue975();
++ issue985();
++ issue985_alt();
++
++ let mut foo = 0;
++ if f() {
++ foo = 42;
++ }
++
++ let mut bar = 0;
++ if f() {
++ f();
++ bar = 42;
++ } else {
++ f();
++ }
++
++ let quz;
++ if f() {
++ quz = 42;
++ } else {
++ quz = 0;
++ }
++
++ // `toto` is used several times
++ let mut toto;
++ if f() {
++ toto = 42;
++ } else {
++ for i in &[1, 2] {
++ toto = *i;
++ }
++
++ toto = 2;
++ }
++
++ // found in libcore, the inner if is not a statement but the block's expr
++ let mut ch = b'x';
++ if f() {
++ ch = b'*';
++ if f() {
++ ch = b'?';
++ }
++ }
++
++ // baz needs to be mut
++ let mut baz = 0;
++ if f() {
++ baz = 42;
++ }
++
++ baz = 1337;
++
++ // issue 3043 - types with interior mutability should not trigger this lint
++ use std::cell::Cell;
++ let mut val = Cell::new(1);
++ if true {
++ val = Cell::new(2);
++ }
++ println!("{}", val.get());
++}
--- /dev/null
--- /dev/null
++error: `if _ { .. } else { .. }` is an expression
++ --> $DIR/let_if_seq.rs:63:5
++ |
++LL | / let mut foo = 0;
++LL | | if f() {
++LL | | foo = 42;
++LL | | }
++ | |_____^ help: it is more idiomatic to write: `let <mut> foo = if f() { 42 } else { 0 };`
++ |
++ = note: `-D clippy::useless-let-if-seq` implied by `-D warnings`
++ = note: you might not need `mut` at all
++
++error: `if _ { .. } else { .. }` is an expression
++ --> $DIR/let_if_seq.rs:68:5
++ |
++LL | / let mut bar = 0;
++LL | | if f() {
++LL | | f();
++LL | | bar = 42;
++LL | | } else {
++LL | | f();
++LL | | }
++ | |_____^ help: it is more idiomatic to write: `let <mut> bar = if f() { ..; 42 } else { ..; 0 };`
++ |
++ = note: you might not need `mut` at all
++
++error: `if _ { .. } else { .. }` is an expression
++ --> $DIR/let_if_seq.rs:76:5
++ |
++LL | / let quz;
++LL | | if f() {
++LL | | quz = 42;
++LL | | } else {
++LL | | quz = 0;
++LL | | }
++ | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
++
++error: `if _ { .. } else { .. }` is an expression
++ --> $DIR/let_if_seq.rs:105:5
++ |
++LL | / let mut baz = 0;
++LL | | if f() {
++LL | | baz = 42;
++LL | | }
++ | |_____^ help: it is more idiomatic to write: `let <mut> baz = if f() { 42 } else { 0 };`
++ |
++ = note: you might not need `mut` at all
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++#![warn(clippy::let_and_return)]
++
++fn test() -> i32 {
++ let _y = 0; // no warning
++ let x = 5;
++ x
++}
++
++fn test_inner() -> i32 {
++ if true {
++ let x = 5;
++ x
++ } else {
++ 0
++ }
++}
++
++fn test_nowarn_1() -> i32 {
++ let mut x = 5;
++ x += 1;
++ x
++}
++
++fn test_nowarn_2() -> i32 {
++ let x = 5;
++ x + 1
++}
++
++fn test_nowarn_3() -> (i32, i32) {
++ // this should technically warn, but we do not compare complex patterns
++ let (x, y) = (5, 9);
++ (x, y)
++}
++
++fn test_nowarn_4() -> i32 {
++ // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type
++ let x: i32 = 5;
++ x
++}
++
++fn test_nowarn_5(x: i16) -> u16 {
++ #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
++ let x = x as u16;
++ x
++}
++
++// False positive example
++trait Decode {
++ fn decode<D: std::io::Read>(d: D) -> Result<Self, ()>
++ where
++ Self: Sized;
++}
++
++macro_rules! tuple_encode {
++ ($($x:ident),*) => (
++ impl<$($x: Decode),*> Decode for ($($x),*) {
++ #[inline]
++ #[allow(non_snake_case)]
++ fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> {
++ // Shouldn't trigger lint
++ Ok(($({let $x = Decode::decode(&mut d)?; $x }),*))
++ }
++ }
++ );
++}
++
++tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: returning the result of a `let` binding from a block
++ --> $DIR/let_return.rs:7:5
++ |
++LL | let x = 5;
++ | ---------- unnecessary `let` binding
++LL | x
++ | ^
++ |
++ = note: `-D clippy::let-and-return` implied by `-D warnings`
++help: return the expression directly
++ |
++LL |
++LL | 5
++ |
++
++error: returning the result of a `let` binding from a block
++ --> $DIR/let_return.rs:13:9
++ |
++LL | let x = 5;
++ | ---------- unnecessary `let` binding
++LL | x
++ | ^
++ |
++help: return the expression directly
++ |
++LL |
++LL | 5
++ |
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::let_underscore_lock)]
++
++fn main() {
++ let m = std::sync::Mutex::new(());
++ let rw = std::sync::RwLock::new(());
++
++ let _ = m.lock();
++ let _ = rw.read();
++ let _ = rw.write();
++ let _ = m.try_lock();
++ let _ = rw.try_read();
++ let _ = rw.try_write();
++}
--- /dev/null
--- /dev/null
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:7:5
++ |
++LL | let _ = m.lock();
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:8:5
++ |
++LL | let _ = rw.read();
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:9:5
++ |
++LL | let _ = rw.write();
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:10:5
++ |
++LL | let _ = m.try_lock();
++ | ^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:11:5
++ |
++LL | let _ = rw.try_read();
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++ --> $DIR/let_underscore_lock.rs:12:5
++ |
++LL | let _ = rw.try_write();
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::let_underscore_must_use)]
++
++// Debug implementations can fire this lint,
++// so we shouldn't lint external macros
++#[derive(Debug)]
++struct Foo {
++ field: i32,
++}
++
++#[must_use]
++fn f() -> u32 {
++ 0
++}
++
++fn g() -> Result<u32, u32> {
++ Ok(0)
++}
++
++#[must_use]
++fn l<T>(x: T) -> T {
++ x
++}
++
++fn h() -> u32 {
++ 0
++}
++
++struct S {}
++
++impl S {
++ #[must_use]
++ pub fn f(&self) -> u32 {
++ 0
++ }
++
++ pub fn g(&self) -> Result<u32, u32> {
++ Ok(0)
++ }
++
++ fn k(&self) -> u32 {
++ 0
++ }
++
++ #[must_use]
++ fn h() -> u32 {
++ 0
++ }
++
++ fn p() -> Result<u32, u32> {
++ Ok(0)
++ }
++}
++
++trait Trait {
++ #[must_use]
++ fn a() -> u32;
++}
++
++impl Trait for S {
++ fn a() -> u32 {
++ 0
++ }
++}
++
++fn main() {
++ let _ = f();
++ let _ = g();
++ let _ = h();
++ let _ = l(0_u32);
++
++ let s = S {};
++
++ let _ = s.f();
++ let _ = s.g();
++ let _ = s.k();
++
++ let _ = S::h();
++ let _ = S::p();
++
++ let _ = S::a();
++
++ let _ = if true { Ok(()) } else { Err(()) };
++
++ let a = Result::<(), ()>::Ok(());
++
++ let _ = a.is_ok();
++
++ let _ = a.map(|_| ());
++
++ let _ = a;
++
++ #[allow(clippy::let_underscore_must_use)]
++ let _ = a;
++}
--- /dev/null
--- /dev/null
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:66:5
++ |
++LL | let _ = f();
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::let-underscore-must-use` implied by `-D warnings`
++ = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:67:5
++ |
++LL | let _ = g();
++ | ^^^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:69:5
++ |
++LL | let _ = l(0_u32);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using function result
++
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:73:5
++ |
++LL | let _ = s.f();
++ | ^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:74:5
++ |
++LL | let _ = s.g();
++ | ^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:77:5
++ |
++LL | let _ = S::h();
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:78:5
++ |
++LL | let _ = S::p();
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:80:5
++ |
++LL | let _ = S::a();
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:82:5
++ |
++LL | let _ = if true { Ok(()) } else { Err(()) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++ --> $DIR/let_underscore_must_use.rs:86:5
++ |
++LL | let _ = a.is_ok();
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:88:5
++ |
++LL | let _ = a.map(|_| ());
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: non-binding let on an expression with `#[must_use]` type
++ --> $DIR/let_underscore_must_use.rs:90:5
++ |
++LL | let _ = a;
++ | ^^^^^^^^^^
++ |
++ = help: consider explicitly using expression value
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::let_unit_value)]
++#![allow(clippy::no_effect)]
++#![allow(unused_variables)]
++
++macro_rules! let_and_return {
++ ($n:expr) => {{
++ let ret = $n;
++ }};
++}
++
++fn main() {
++ println!("x");
++ let _y = 1; // this is fine
++ let _z = ((), 1); // this as well
++ if true {
++ ();
++ }
++
++ consume_units_with_for_loop(); // should be fine as well
++
++ multiline_sugg();
++
++ let_and_return!(()) // should be fine
++}
++
++// Related to issue #1964
++fn consume_units_with_for_loop() {
++ // `for_let_unit` lint should not be triggered by consuming them using for loop.
++ let v = vec![(), (), ()];
++ let mut count = 0;
++ for _ in v {
++ count += 1;
++ }
++ assert_eq!(count, 3);
++
++ // Same for consuming from some other Iterator<Item = ()>.
++ let (tx, rx) = ::std::sync::mpsc::channel();
++ tx.send(()).unwrap();
++ drop(tx);
++
++ count = 0;
++ for _ in rx.iter() {
++ count += 1;
++ }
++ assert_eq!(count, 1);
++}
++
++fn multiline_sugg() {
++ let v: Vec<u8> = vec![2];
++
++ v
++ .into_iter()
++ .map(|i| i * 2)
++ .filter(|i| i % 2 == 0)
++ .map(|_| ())
++ .next()
++ .unwrap();
++}
++
++#[derive(Copy, Clone)]
++pub struct ContainsUnit(()); // should be fine
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::let_unit_value)]
++#![allow(clippy::no_effect)]
++#![allow(unused_variables)]
++
++macro_rules! let_and_return {
++ ($n:expr) => {{
++ let ret = $n;
++ }};
++}
++
++fn main() {
++ let _x = println!("x");
++ let _y = 1; // this is fine
++ let _z = ((), 1); // this as well
++ if true {
++ let _a = ();
++ }
++
++ consume_units_with_for_loop(); // should be fine as well
++
++ multiline_sugg();
++
++ let_and_return!(()) // should be fine
++}
++
++// Related to issue #1964
++fn consume_units_with_for_loop() {
++ // `for_let_unit` lint should not be triggered by consuming them using for loop.
++ let v = vec![(), (), ()];
++ let mut count = 0;
++ for _ in v {
++ count += 1;
++ }
++ assert_eq!(count, 3);
++
++ // Same for consuming from some other Iterator<Item = ()>.
++ let (tx, rx) = ::std::sync::mpsc::channel();
++ tx.send(()).unwrap();
++ drop(tx);
++
++ count = 0;
++ for _ in rx.iter() {
++ count += 1;
++ }
++ assert_eq!(count, 1);
++}
++
++fn multiline_sugg() {
++ let v: Vec<u8> = vec![2];
++
++ let _ = v
++ .into_iter()
++ .map(|i| i * 2)
++ .filter(|i| i % 2 == 0)
++ .map(|_| ())
++ .next()
++ .unwrap();
++}
++
++#[derive(Copy, Clone)]
++pub struct ContainsUnit(()); // should be fine
--- /dev/null
--- /dev/null
++error: this let-binding has unit value
++ --> $DIR/let_unit.rs:14:5
++ |
++LL | let _x = println!("x");
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");`
++ |
++ = note: `-D clippy::let-unit-value` implied by `-D warnings`
++
++error: this let-binding has unit value
++ --> $DIR/let_unit.rs:18:9
++ |
++LL | let _a = ();
++ | ^^^^^^^^^^^^ help: omit the `let` binding: `();`
++
++error: this let-binding has unit value
++ --> $DIR/let_unit.rs:53:5
++ |
++LL | / let _ = v
++LL | | .into_iter()
++LL | | .map(|i| i * 2)
++LL | | .filter(|i| i % 2 == 0)
++LL | | .map(|_| ())
++LL | | .next()
++LL | | .unwrap();
++ | |__________________^
++ |
++help: omit the `let` binding
++ |
++LL | v
++LL | .into_iter()
++LL | .map(|i| i * 2)
++LL | .filter(|i| i % 2 == 0)
++LL | .map(|_| ())
++LL | .next()
++ ...
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++#[macro_use]
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++extern crate rustc_lint;
++use rustc_lint::LintPass;
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT_REGISTERED,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++declare_tool_lint! {
++ pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL,
++ Warn,
++ "",
++ report_in_external_macro: true
++}
++
++pub struct Pass;
++impl LintPass for Pass {
++ fn name(&self) -> &'static str {
++ "TEST_LINT"
++ }
++}
++
++declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]);
++
++pub struct Pass3;
++impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]);
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: the lint `TEST_LINT` is not added to any `LintPass`
++ --> $DIR/lint_without_lint_pass.rs:11:1
++ |
++LL | / declare_tool_lint! {
++LL | | pub clippy::TEST_LINT,
++LL | | Warn,
++LL | | "",
++LL | | report_in_external_macro: true
++LL | | }
++ | |_^
++ |
++note: the lint level is defined here
++ --> $DIR/lint_without_lint_pass.rs:1:9
++ |
++LL | #![deny(clippy::internal)]
++ | ^^^^^^^^^^^^^^^^
++ = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// does not test any rustfixable lints
++
++#![warn(clippy::mixed_case_hex_literals)]
++#![warn(clippy::zero_prefixed_literal)]
++#![allow(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++fn main() {
++ let ok1 = 0xABCD;
++ let ok3 = 0xab_cd;
++ let ok4 = 0xab_cd_i32;
++ let ok5 = 0xAB_CD_u32;
++ let ok5 = 0xAB_CD_isize;
++ let fail1 = 0xabCD;
++ let fail2 = 0xabCD_u32;
++ let fail2 = 0xabCD_isize;
++ let fail_multi_zero = 000_123usize;
++
++ let ok9 = 0;
++ let ok10 = 0_i64;
++ let fail8 = 0123;
++
++ let ok11 = 0o123;
++ let ok12 = 0b10_1010;
++
++ let ok13 = 0xab_abcd;
++ let ok14 = 0xBAFE_BAFE;
++ let ok15 = 0xab_cabc_abca_bcab_cabc;
++ let ok16 = 0xFE_BAFE_ABAB_ABCD;
++ let ok17 = 0x123_4567_8901_usize;
++ let ok18 = 0xF;
++
++ let fail19 = 12_3456_21;
++ let fail22 = 3__4___23;
++ let fail23 = 3__16___23;
++}
--- /dev/null
--- /dev/null
++error: inconsistent casing in hexadecimal literal
++ --> $DIR/literals.rs:14:17
++ |
++LL | let fail1 = 0xabCD;
++ | ^^^^^^
++ |
++ = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
++
++error: inconsistent casing in hexadecimal literal
++ --> $DIR/literals.rs:15:17
++ |
++LL | let fail2 = 0xabCD_u32;
++ | ^^^^^^^^^^
++
++error: inconsistent casing in hexadecimal literal
++ --> $DIR/literals.rs:16:17
++ |
++LL | let fail2 = 0xabCD_isize;
++ | ^^^^^^^^^^^^
++
++error: this is a decimal constant
++ --> $DIR/literals.rs:17:27
++ |
++LL | let fail_multi_zero = 000_123usize;
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++ |
++LL | let fail_multi_zero = 123usize;
++ | ^^^^^^^^
++help: if you mean to use an octal constant, use `0o`
++ |
++LL | let fail_multi_zero = 0o123usize;
++ | ^^^^^^^^^^
++
++error: this is a decimal constant
++ --> $DIR/literals.rs:21:17
++ |
++LL | let fail8 = 0123;
++ | ^^^^
++ |
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++ |
++LL | let fail8 = 123;
++ | ^^^
++help: if you mean to use an octal constant, use `0o`
++ |
++LL | let fail8 = 0o123;
++ | ^^^^^
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/literals.rs:33:18
++ |
++LL | let fail19 = 12_3456_21;
++ | ^^^^^^^^^^ help: consider: `12_345_621`
++ |
++ = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/literals.rs:34:18
++ |
++LL | let fail22 = 3__4___23;
++ | ^^^^^^^^^ help: consider: `3_423`
++
++error: digits grouped inconsistently by underscores
++ --> $DIR/literals.rs:35:18
++ |
++LL | let fail23 = 3__16___23;
++ | ^^^^^^^^^^ help: consider: `31_623`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::logic_bug)]
++
++fn main() {
++ let a: bool = unimplemented!();
++ let b: bool = unimplemented!();
++ let c: bool = unimplemented!();
++ let d: bool = unimplemented!();
++ let e: bool = unimplemented!();
++ let _ = a && b || a;
++ let _ = !(a && b);
++ let _ = false && a;
++ // don't lint on cfgs
++ let _ = cfg!(you_shall_not_not_pass) && a;
++ let _ = a || !b || !c || !d || !e;
++ let _ = !(a && b || c);
++}
++
++fn equality_stuff() {
++ let a: i32 = unimplemented!();
++ let b: i32 = unimplemented!();
++ let _ = a == b && a != b;
++ let _ = a < b && a >= b;
++ let _ = a > b && a <= b;
++ let _ = a > b && a == b;
++}
--- /dev/null
--- /dev/null
++error: this boolean expression contains a logic bug
++ --> $DIR/logic_bug.rs:10:13
++ |
++LL | let _ = a && b || a;
++ | ^^^^^^^^^^^ help: it would look like the following: `a`
++ |
++ = note: `-D clippy::logic-bug` implied by `-D warnings`
++help: this expression can be optimized out by applying boolean operations to the outer expression
++ --> $DIR/logic_bug.rs:10:18
++ |
++LL | let _ = a && b || a;
++ | ^
++
++error: this boolean expression contains a logic bug
++ --> $DIR/logic_bug.rs:12:13
++ |
++LL | let _ = false && a;
++ | ^^^^^^^^^^ help: it would look like the following: `false`
++ |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++ --> $DIR/logic_bug.rs:12:22
++ |
++LL | let _ = false && a;
++ | ^
++
++error: this boolean expression contains a logic bug
++ --> $DIR/logic_bug.rs:22:13
++ |
++LL | let _ = a == b && a != b;
++ | ^^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++ |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++ --> $DIR/logic_bug.rs:22:13
++ |
++LL | let _ = a == b && a != b;
++ | ^^^^^^
++
++error: this boolean expression contains a logic bug
++ --> $DIR/logic_bug.rs:23:13
++ |
++LL | let _ = a < b && a >= b;
++ | ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++ |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++ --> $DIR/logic_bug.rs:23:13
++ |
++LL | let _ = a < b && a >= b;
++ | ^^^^^
++
++error: this boolean expression contains a logic bug
++ --> $DIR/logic_bug.rs:24:13
++ |
++LL | let _ = a > b && a <= b;
++ | ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++ |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++ --> $DIR/logic_bug.rs:24:13
++ |
++LL | let _ = a > b && a <= b;
++ | ^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::lossy_float_literal)]
++
++fn main() {
++ // Lossy whole-number float literals
++ let _: f32 = 16_777_216.0;
++ let _: f32 = 16_777_220.0;
++ let _: f32 = 16_777_220.0;
++ let _: f32 = 16_777_220.0;
++ let _ = 16_777_220_f32;
++ let _: f32 = -16_777_220.0;
++ let _: f64 = 9_007_199_254_740_992.0;
++ let _: f64 = 9_007_199_254_740_992.0;
++ let _: f64 = 9_007_199_254_740_992.0;
++ let _ = 9_007_199_254_740_992_f64;
++ let _: f64 = -9_007_199_254_740_992.0;
++
++ // Lossless whole number float literals
++ let _: f32 = 16_777_216.0;
++ let _: f32 = 16_777_218.0;
++ let _: f32 = 16_777_220.0;
++ let _: f32 = -16_777_216.0;
++ let _: f32 = -16_777_220.0;
++ let _: f64 = 16_777_217.0;
++ let _: f64 = -16_777_217.0;
++ let _: f64 = 9_007_199_254_740_992.0;
++ let _: f64 = -9_007_199_254_740_992.0;
++
++ // Ignored whole number float literals
++ let _: f32 = 1e25;
++ let _: f32 = 1E25;
++ let _: f64 = 1e99;
++ let _: f64 = 1E99;
++ let _: f32 = 0.1;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::lossy_float_literal)]
++
++fn main() {
++ // Lossy whole-number float literals
++ let _: f32 = 16_777_217.0;
++ let _: f32 = 16_777_219.0;
++ let _: f32 = 16_777_219.;
++ let _: f32 = 16_777_219.000;
++ let _ = 16_777_219f32;
++ let _: f32 = -16_777_219.0;
++ let _: f64 = 9_007_199_254_740_993.0;
++ let _: f64 = 9_007_199_254_740_993.;
++ let _: f64 = 9_007_199_254_740_993.00;
++ let _ = 9_007_199_254_740_993f64;
++ let _: f64 = -9_007_199_254_740_993.0;
++
++ // Lossless whole number float literals
++ let _: f32 = 16_777_216.0;
++ let _: f32 = 16_777_218.0;
++ let _: f32 = 16_777_220.0;
++ let _: f32 = -16_777_216.0;
++ let _: f32 = -16_777_220.0;
++ let _: f64 = 16_777_217.0;
++ let _: f64 = -16_777_217.0;
++ let _: f64 = 9_007_199_254_740_992.0;
++ let _: f64 = -9_007_199_254_740_992.0;
++
++ // Ignored whole number float literals
++ let _: f32 = 1e25;
++ let _: f32 = 1E25;
++ let _: f64 = 1e99;
++ let _: f64 = 1E99;
++ let _: f32 = 0.1;
++}
--- /dev/null
--- /dev/null
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:6:18
++ |
++LL | let _: f32 = 16_777_217.0;
++ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
++ |
++ = note: `-D clippy::lossy-float-literal` implied by `-D warnings`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:7:18
++ |
++LL | let _: f32 = 16_777_219.0;
++ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:8:18
++ |
++LL | let _: f32 = 16_777_219.;
++ | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:9:18
++ |
++LL | let _: f32 = 16_777_219.000;
++ | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:10:13
++ |
++LL | let _ = 16_777_219f32;
++ | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:11:19
++ |
++LL | let _: f32 = -16_777_219.0;
++ | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:12:18
++ |
++LL | let _: f64 = 9_007_199_254_740_993.0;
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:13:18
++ |
++LL | let _: f64 = 9_007_199_254_740_993.;
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:14:18
++ |
++LL | let _: f64 = 9_007_199_254_740_993.00;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:15:13
++ |
++LL | let _ = 9_007_199_254_740_993f64;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64`
++
++error: literal cannot be represented as the underlying type without loss of precision
++ --> $DIR/lossy_float_literal.rs:16:19
++ |
++LL | let _: f64 = -9_007_199_254_740_993.0;
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++// edition:2018
++#![warn(clippy::macro_use_imports)]
++
++use std::collections::HashMap;
++#[macro_use]
++use std::prelude;
++
++fn main() {
++ let _ = HashMap::<u8, u8>::new();
++ println!();
++}
--- /dev/null
--- /dev/null
++error: `macro_use` attributes are no longer needed in the Rust 2018 edition
++ --> $DIR/macro_use_imports.rs:5:1
++ |
++LL | #[macro_use]
++ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::<macro name>`
++ |
++ = note: `-D clippy::macro-use-imports` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
++
++const LOOP_OFFSET: usize = 5000;
++
++pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
++ // plain manual memcpy
++ for i in 0..src.len() {
++ dst[i] = src[i];
++ }
++
++ // dst offset memcpy
++ for i in 0..src.len() {
++ dst[i + 10] = src[i];
++ }
++
++ // src offset memcpy
++ for i in 0..src.len() {
++ dst[i] = src[i + 10];
++ }
++
++ // src offset memcpy
++ for i in 11..src.len() {
++ dst[i] = src[i - 10];
++ }
++
++ // overwrite entire dst
++ for i in 0..dst.len() {
++ dst[i] = src[i];
++ }
++
++ // manual copy with branch - can't easily convert to memcpy!
++ for i in 0..src.len() {
++ dst[i] = src[i];
++ if dst[i] > 5 {
++ break;
++ }
++ }
++
++ // multiple copies - suggest two memcpy statements
++ for i in 10..256 {
++ dst[i] = src[i - 5];
++ dst2[i + 500] = src[i]
++ }
++
++ // this is a reversal - the copy lint shouldn't be triggered
++ for i in 10..LOOP_OFFSET {
++ dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i];
++ }
++
++ let some_var = 5;
++ // Offset in variable
++ for i in 10..LOOP_OFFSET {
++ dst[i + LOOP_OFFSET] = src[i - some_var];
++ }
++
++ // Non continuous copy - don't trigger lint
++ for i in 0..10 {
++ dst[i + i] = src[i];
++ }
++
++ let src_vec = vec![1, 2, 3, 4, 5];
++ let mut dst_vec = vec![0, 0, 0, 0, 0];
++
++ // make sure vectors are supported
++ for i in 0..src_vec.len() {
++ dst_vec[i] = src_vec[i];
++ }
++
++ // lint should not trigger when either
++ // source or destination type is not
++ // slice-like, like DummyStruct
++ struct DummyStruct(i32);
++
++ impl ::std::ops::Index<usize> for DummyStruct {
++ type Output = i32;
++
++ fn index(&self, _: usize) -> &i32 {
++ &self.0
++ }
++ }
++
++ let src = DummyStruct(5);
++ let mut dst_vec = vec![0; 10];
++
++ for i in 0..10 {
++ dst_vec[i] = src[i];
++ }
++
++ // Simplify suggestion (issue #3004)
++ let src = [0, 1, 2, 3, 4];
++ let mut dst = [0, 0, 0, 0, 0, 0];
++ let from = 1;
++
++ for i in from..from + src.len() {
++ dst[i] = src[i - from];
++ }
++
++ for i in from..from + 3 {
++ dst[i] = src[i - from];
++ }
++}
++
++#[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
++pub fn manual_clone(src: &[String], dst: &mut [String]) {
++ for i in 0..src.len() {
++ dst[i] = src[i].clone();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:7:14
++ |
++LL | for i in 0..src.len() {
++ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
++ |
++ = note: `-D clippy::manual-memcpy` implied by `-D warnings`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:12:14
++ |
++LL | for i in 0..src.len() {
++ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:17:14
++ |
++LL | for i in 0..src.len() {
++ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:22:14
++ |
++LL | for i in 11..src.len() {
++ | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:27:14
++ |
++LL | for i in 0..dst.len() {
++ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:40:14
++ |
++LL | for i in 10..256 {
++ | ^^^^^^^
++ |
++help: try replacing the loop by
++ |
++LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)])
++LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) {
++ |
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:52:14
++ |
++LL | for i in 10..LOOP_OFFSET {
++ | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:65:14
++ |
++LL | for i in 0..src_vec.len() {
++ | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:94:14
++ |
++LL | for i in from..from + src.len() {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:98:14
++ |
++LL | for i in from..from + 3 {
++ | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])`
++
++error: it looks like you're manually copying between slices
++ --> $DIR/manual_memcpy.rs:105:14
++ |
++LL | for i in 0..src.len() {
++ | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports)]
++
++use std::{i128, i32, u128, u32};
++
++fn main() {
++ let _ = 1u32.saturating_add(1);
++ let _ = 1u32.saturating_add(1);
++ let _ = 1u8.saturating_add(1);
++ let _ = 1u128.saturating_add(1);
++ let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
++ let _ = 1u8.checked_add(1).unwrap_or(0); // ok
++ let _ = 1u32.saturating_mul(1);
++
++ let _ = 1u32.saturating_sub(1);
++ let _ = 1u32.saturating_sub(1);
++ let _ = 1u8.saturating_sub(1);
++ let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
++ let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
++
++ let _ = 1i32.saturating_add(1);
++ let _ = 1i32.saturating_add(1);
++ let _ = 1i8.saturating_add(1);
++ let _ = 1i128.saturating_add(1);
++ let _ = 1i32.saturating_add(-1);
++ let _ = 1i32.saturating_add(-1);
++ let _ = 1i8.saturating_add(-1);
++ let _ = 1i128.saturating_add(-1);
++ let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
++ let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
++ let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
++
++ let _ = 1i32.saturating_sub(1);
++ let _ = 1i32.saturating_sub(1);
++ let _ = 1i8.saturating_sub(1);
++ let _ = 1i128.saturating_sub(1);
++ let _ = 1i32.saturating_sub(-1);
++ let _ = 1i32.saturating_sub(-1);
++ let _ = 1i8.saturating_sub(-1);
++ let _ = 1i128.saturating_sub(-1);
++ let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
++ let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
++ let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports)]
++
++use std::{i128, i32, u128, u32};
++
++fn main() {
++ let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
++ let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
++ let _ = 1u8.checked_add(1).unwrap_or(255);
++ let _ = 1u128
++ .checked_add(1)
++ .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
++ let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
++ let _ = 1u8.checked_add(1).unwrap_or(0); // ok
++ let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
++
++ let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
++ let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
++ let _ = 1u8.checked_sub(1).unwrap_or(0);
++ let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
++ let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
++
++ let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
++ let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
++ let _ = 1i8.checked_add(1).unwrap_or(127);
++ let _ = 1i128
++ .checked_add(1)
++ .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++ let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
++ let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
++ let _ = 1i8.checked_add(-1).unwrap_or(-128);
++ let _ = 1i128
++ .checked_add(-1)
++ .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++ let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
++ let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
++ let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
++
++ let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
++ let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
++ let _ = 1i8.checked_sub(1).unwrap_or(-128);
++ let _ = 1i128
++ .checked_sub(1)
++ .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++ let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
++ let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
++ let _ = 1i8.checked_sub(-1).unwrap_or(127);
++ let _ = 1i128
++ .checked_sub(-1)
++ .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++ let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
++ let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
++ let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
++}
--- /dev/null
--- /dev/null
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:8:13
++ |
++LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
++ |
++ = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:9:13
++ |
++LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:10:13
++ |
++LL | let _ = 1u8.checked_add(1).unwrap_or(255);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:11:13
++ |
++LL | let _ = 1u128
++ | _____________^
++LL | | .checked_add(1)
++LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
++ | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:16:13
++ |
++LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:18:13
++ |
++LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:19:13
++ |
++LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:20:13
++ |
++LL | let _ = 1u8.checked_sub(1).unwrap_or(0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:24:13
++ |
++LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:25:13
++ |
++LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:26:13
++ |
++LL | let _ = 1i8.checked_add(1).unwrap_or(127);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:27:13
++ |
++LL | let _ = 1i128
++ | _____________^
++LL | | .checked_add(1)
++LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++ | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:30:13
++ |
++LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:31:13
++ |
++LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:32:13
++ |
++LL | let _ = 1i8.checked_add(-1).unwrap_or(-128);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:33:13
++ |
++LL | let _ = 1i128
++ | _____________^
++LL | | .checked_add(-1)
++LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++ | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:40:13
++ |
++LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:41:13
++ |
++LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:42:13
++ |
++LL | let _ = 1i8.checked_sub(1).unwrap_or(-128);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:43:13
++ |
++LL | let _ = 1i128
++ | _____________^
++LL | | .checked_sub(1)
++LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++ | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:46:13
++ |
++LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:47:13
++ |
++LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:48:13
++ |
++LL | let _ = 1i8.checked_sub(-1).unwrap_or(127);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++ --> $DIR/manual_saturating_arithmetic.rs:49:13
++ |
++LL | let _ = 1i128
++ | _____________^
++LL | | .checked_sub(-1)
++LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++ | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)`
++
++error: aborting due to 24 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::many_single_char_names)]
++
++fn bla() {
++ let a: i32;
++ let (b, c, d): (i32, i64, i16);
++ {
++ {
++ let cdefg: i32;
++ let blar: i32;
++ }
++ {
++ let e: i32;
++ }
++ {
++ let e: i32;
++ let f: i32;
++ }
++ match 5 {
++ 1 => println!(),
++ e => panic!(),
++ }
++ match 5 {
++ 1 => println!(),
++ _ => panic!(),
++ }
++ }
++}
++
++fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
++
++fn bindings2() {
++ let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
++}
++
++fn shadowing() {
++ let a = 0i32;
++ let a = 0i32;
++ let a = 0i32;
++ let a = 0i32;
++ let a = 0i32;
++ let a = 0i32;
++ {
++ let a = 0i32;
++ }
++}
++
++fn patterns() {
++ enum Z {
++ A(i32),
++ B(i32),
++ C(i32),
++ D(i32),
++ E(i32),
++ F(i32),
++ }
++
++ // These should not trigger a warning, since the pattern bindings are a new scope.
++ match Z::A(0) {
++ Z::A(a) => {},
++ Z::B(b) => {},
++ Z::C(c) => {},
++ Z::D(d) => {},
++ Z::E(e) => {},
++ Z::F(f) => {},
++ }
++}
++
++#[allow(clippy::many_single_char_names)]
++fn issue_3198_allow_works() {
++ let (a, b, c, d, e) = (0, 0, 0, 0, 0);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: 5 bindings with single-character names in scope
++ --> $DIR/many_single_char_names.rs:4:9
++ |
++LL | let a: i32;
++ | ^
++LL | let (b, c, d): (i32, i64, i16);
++ | ^ ^ ^
++...
++LL | let e: i32;
++ | ^
++ |
++ = note: `-D clippy::many-single-char-names` implied by `-D warnings`
++
++error: 6 bindings with single-character names in scope
++ --> $DIR/many_single_char_names.rs:4:9
++ |
++LL | let a: i32;
++ | ^
++LL | let (b, c, d): (i32, i64, i16);
++ | ^ ^ ^
++...
++LL | let e: i32;
++ | ^
++LL | let f: i32;
++ | ^
++
++error: 5 bindings with single-character names in scope
++ --> $DIR/many_single_char_names.rs:4:9
++ |
++LL | let a: i32;
++ | ^
++LL | let (b, c, d): (i32, i64, i16);
++ | ^ ^ ^
++...
++LL | e => panic!(),
++ | ^
++
++error: 8 bindings with single-character names in scope
++ --> $DIR/many_single_char_names.rs:29:13
++ |
++LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
++ | ^ ^ ^ ^ ^ ^ ^ ^
++
++error: 8 bindings with single-character names in scope
++ --> $DIR/many_single_char_names.rs:32:10
++ |
++LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
++ | ^ ^ ^ ^ ^ ^ ^ ^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::iter_cloned_collect)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
++#![allow(clippy::missing_docs_in_private_items)]
++#![allow(clippy::redundant_closure_for_method_calls)]
++#![allow(clippy::many_single_char_names)]
++
++fn main() {
++ let _: Vec<i8> = vec![5_i8; 6].iter().copied().collect();
++ let _: Vec<String> = vec![String::new()].iter().cloned().collect();
++ let _: Vec<u32> = vec![42, 43].iter().copied().collect();
++ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
++ let _: Option<u64> = Some(&16).copied();
++ let _: Option<u8> = Some(&1).copied();
++
++ // Don't lint these
++ let v = vec![5_i8; 6];
++ let a = 0;
++ let b = &a;
++ let _ = v.iter().map(|_x| *b);
++ let _ = v.iter().map(|_x| a.clone());
++ let _ = v.iter().map(|&_x| a);
++
++ // Issue #498
++ let _ = std::env::args();
++
++ // Issue #4824 item types that aren't references
++ {
++ use std::rc::Rc;
++
++ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
++ let _: Option<u32> = o.map(|x| *x);
++ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
++ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++ }
++
++ // Issue #5524 mutable references
++ {
++ let mut c = 42;
++ let v = vec![&mut c];
++ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++ let mut d = 21;
++ let v = vec![&mut d];
++ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::iter_cloned_collect)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
++#![allow(clippy::missing_docs_in_private_items)]
++#![allow(clippy::redundant_closure_for_method_calls)]
++#![allow(clippy::many_single_char_names)]
++
++fn main() {
++ let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
++ let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
++ let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
++ let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
++ let _: Option<u64> = Some(&16).map(|b| *b);
++ let _: Option<u8> = Some(&1).map(|x| x.clone());
++
++ // Don't lint these
++ let v = vec![5_i8; 6];
++ let a = 0;
++ let b = &a;
++ let _ = v.iter().map(|_x| *b);
++ let _ = v.iter().map(|_x| a.clone());
++ let _ = v.iter().map(|&_x| a);
++
++ // Issue #498
++ let _ = std::env::args().map(|v| v.clone());
++
++ // Issue #4824 item types that aren't references
++ {
++ use std::rc::Rc;
++
++ let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
++ let _: Option<u32> = o.map(|x| *x);
++ let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
++ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++ }
++
++ // Issue #5524 mutable references
++ {
++ let mut c = 42;
++ let v = vec![&mut c];
++ let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++ let mut d = 21;
++ let v = vec![&mut d];
++ let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
++ }
++}
--- /dev/null
--- /dev/null
++error: You are using an explicit closure for copying elements
++ --> $DIR/map_clone.rs:10:22
++ |
++LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
++ |
++ = note: `-D clippy::map-clone` implied by `-D warnings`
++
++error: You are using an explicit closure for cloning elements
++ --> $DIR/map_clone.rs:11:26
++ |
++LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
++
++error: You are using an explicit closure for copying elements
++ --> $DIR/map_clone.rs:12:23
++ |
++LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
++
++error: You are using an explicit closure for copying elements
++ --> $DIR/map_clone.rs:14:26
++ |
++LL | let _: Option<u64> = Some(&16).map(|b| *b);
++ | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()`
++
++error: You are using an explicit closure for copying elements
++ --> $DIR/map_clone.rs:15:25
++ |
++LL | let _: Option<u8> = Some(&1).map(|x| x.clone());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()`
++
++error: You are needlessly cloning iterator elements
++ --> $DIR/map_clone.rs:26:29
++ |
++LL | let _ = std::env::args().map(|v| v.clone());
++ | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++ let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
++ let _: Option<_> = (Some(Some(1))).and_then(|x| x);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++ let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
++ let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
++}
--- /dev/null
--- /dev/null
++error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
++ --> $DIR/map_flatten.rs:7:21
++ |
++LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
++ |
++ = note: `-D clippy::map-flatten` implied by `-D warnings`
++
++error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
++ --> $DIR/map_flatten.rs:8:24
++ |
++LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++struct Mappable {}
++
++impl Mappable {
++ pub fn map(&self) {}
++}
++
++fn main() {
++ let m = Mappable {};
++ m.map();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++#![warn(clippy::match_as_ref)]
++
++fn match_as_ref() {
++ let owned: Option<()> = None;
++ let borrowed: Option<&()> = owned.as_ref();
++
++ let mut mut_owned: Option<()> = None;
++ let borrow_mut: Option<&mut ()> = mut_owned.as_mut();
++}
++
++mod issue4437 {
++ use std::{error::Error, fmt, num::ParseIntError};
++
++ #[derive(Debug)]
++ struct E {
++ source: Option<ParseIntError>,
++ }
++
++ impl Error for E {
++ fn source(&self) -> Option<&(dyn Error + 'static)> {
++ self.source.as_ref().map(|x| x as _)
++ }
++ }
++
++ impl fmt::Display for E {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ unimplemented!()
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++#![warn(clippy::match_as_ref)]
++
++fn match_as_ref() {
++ let owned: Option<()> = None;
++ let borrowed: Option<&()> = match owned {
++ None => None,
++ Some(ref v) => Some(v),
++ };
++
++ let mut mut_owned: Option<()> = None;
++ let borrow_mut: Option<&mut ()> = match mut_owned {
++ None => None,
++ Some(ref mut v) => Some(v),
++ };
++}
++
++mod issue4437 {
++ use std::{error::Error, fmt, num::ParseIntError};
++
++ #[derive(Debug)]
++ struct E {
++ source: Option<ParseIntError>,
++ }
++
++ impl Error for E {
++ fn source(&self) -> Option<&(dyn Error + 'static)> {
++ match self.source {
++ Some(ref s) => Some(s),
++ None => None,
++ }
++ }
++ }
++
++ impl fmt::Display for E {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ unimplemented!()
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: use `as_ref()` instead
++ --> $DIR/match_as_ref.rs:8:33
++ |
++LL | let borrowed: Option<&()> = match owned {
++ | _________________________________^
++LL | | None => None,
++LL | | Some(ref v) => Some(v),
++LL | | };
++ | |_____^ help: try this: `owned.as_ref()`
++ |
++ = note: `-D clippy::match-as-ref` implied by `-D warnings`
++
++error: use `as_mut()` instead
++ --> $DIR/match_as_ref.rs:14:39
++ |
++LL | let borrow_mut: Option<&mut ()> = match mut_owned {
++ | _______________________________________^
++LL | | None => None,
++LL | | Some(ref mut v) => Some(v),
++LL | | };
++ | |_____^ help: try this: `mut_owned.as_mut()`
++
++error: use `as_ref()` instead
++ --> $DIR/match_as_ref.rs:30:13
++ |
++LL | / match self.source {
++LL | | Some(ref s) => Some(s),
++LL | | None => None,
++LL | | }
++ | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::match_bool)]
++
++fn match_bool() {
++ let test: bool = true;
++
++ match test {
++ true => 0,
++ false => 42,
++ };
++
++ let option = 1;
++ match option == 1 {
++ true => 1,
++ false => 0,
++ };
++
++ match test {
++ true => (),
++ false => {
++ println!("Noooo!");
++ },
++ };
++
++ match test {
++ false => {
++ println!("Noooo!");
++ },
++ _ => (),
++ };
++
++ match test && test {
++ false => {
++ println!("Noooo!");
++ },
++ _ => (),
++ };
++
++ match test {
++ false => {
++ println!("Noooo!");
++ },
++ true => {
++ println!("Yes!");
++ },
++ };
++
++ // Not linted
++ match option {
++ 1..=10 => 1,
++ 11..=20 => 2,
++ _ => 3,
++ };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this boolean expression can be simplified
++ --> $DIR/match_bool.rs:31:11
++ |
++LL | match test && test {
++ | ^^^^^^^^^^^^ help: try: `test`
++ |
++ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:6:5
++ |
++LL | / match test {
++LL | | true => 0,
++LL | | false => 42,
++LL | | };
++ | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }`
++ |
++note: the lint level is defined here
++ --> $DIR/match_bool.rs:1:9
++ |
++LL | #![deny(clippy::match_bool)]
++ | ^^^^^^^^^^^^^^^^^^
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:12:5
++ |
++LL | / match option == 1 {
++LL | | true => 1,
++LL | | false => 0,
++LL | | };
++ | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }`
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:17:5
++ |
++LL | / match test {
++LL | | true => (),
++LL | | false => {
++LL | | println!("Noooo!");
++LL | | },
++LL | | };
++ | |_____^
++ |
++help: consider using an `if`/`else` expression
++ |
++LL | if !test {
++LL | println!("Noooo!");
++LL | };
++ |
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:24:5
++ |
++LL | / match test {
++LL | | false => {
++LL | | println!("Noooo!");
++LL | | },
++LL | | _ => (),
++LL | | };
++ | |_____^
++ |
++help: consider using an `if`/`else` expression
++ |
++LL | if !test {
++LL | println!("Noooo!");
++LL | };
++ |
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:31:5
++ |
++LL | / match test && test {
++LL | | false => {
++LL | | println!("Noooo!");
++LL | | },
++LL | | _ => (),
++LL | | };
++ | |_____^
++ |
++help: consider using an `if`/`else` expression
++ |
++LL | if !(test && test) {
++LL | println!("Noooo!");
++LL | };
++ |
++
++error: equal expressions as operands to `&&`
++ --> $DIR/match_bool.rs:31:11
++ |
++LL | match test && test {
++ | ^^^^^^^^^^^^
++ |
++ = note: `#[deny(clippy::eq_op)]` on by default
++
++error: you seem to be trying to match on a boolean expression
++ --> $DIR/match_bool.rs:38:5
++ |
++LL | / match test {
++LL | | false => {
++LL | | println!("Noooo!");
++LL | | },
++... |
++LL | | },
++LL | | };
++ | |_____^
++ |
++help: consider using an `if`/`else` expression
++ |
++LL | if test {
++LL | println!("Yes!");
++LL | } else {
++LL | println!("Noooo!");
++LL | };
++ |
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::match_on_vec_items)]
++
++fn match_with_wildcard() {
++ let arr = vec![0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 1;
++
++ // Lint, may panic
++ match arr[idx] {
++ 0 => println!("0"),
++ 1 => println!("1"),
++ _ => {},
++ }
++
++ // Lint, may panic
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ _ => {},
++ }
++}
++
++fn match_without_wildcard() {
++ let arr = vec![0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 2;
++
++ // Lint, may panic
++ match arr[idx] {
++ 0 => println!("0"),
++ 1 => println!("1"),
++ num => {},
++ }
++
++ // Lint, may panic
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ [ref sub @ ..] => {},
++ }
++}
++
++fn match_wildcard_and_action() {
++ let arr = vec![0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 3;
++
++ // Lint, may panic
++ match arr[idx] {
++ 0 => println!("0"),
++ 1 => println!("1"),
++ _ => println!("Hello, World!"),
++ }
++
++ // Lint, may panic
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ _ => println!("Hello, World!"),
++ }
++}
++
++fn match_vec_ref() {
++ let arr = &vec![0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 3;
++
++ // Lint, may panic
++ match arr[idx] {
++ 0 => println!("0"),
++ 1 => println!("1"),
++ _ => {},
++ }
++
++ // Lint, may panic
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ _ => {},
++ }
++}
++
++fn match_with_get() {
++ let arr = vec![0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 3;
++
++ // Ok
++ match arr.get(idx) {
++ Some(0) => println!("0"),
++ Some(1) => println!("1"),
++ _ => {},
++ }
++
++ // Ok
++ match arr.get(range) {
++ Some(&[0, 1]) => println!("0 1"),
++ Some(&[1, 2]) => println!("1 2"),
++ _ => {},
++ }
++}
++
++fn match_with_array() {
++ let arr = [0, 1, 2, 3];
++ let range = 1..3;
++ let idx = 3;
++
++ // Ok
++ match arr[idx] {
++ 0 => println!("0"),
++ 1 => println!("1"),
++ _ => {},
++ }
++
++ // Ok
++ match arr[range] {
++ [0, 1] => println!("0 1"),
++ [1, 2] => println!("1 2"),
++ _ => {},
++ }
++}
++
++fn main() {
++ match_with_wildcard();
++ match_without_wildcard();
++ match_wildcard_and_action();
++ match_vec_ref();
++ match_with_get();
++ match_with_array();
++}
--- /dev/null
--- /dev/null
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:9:11
++ |
++LL | match arr[idx] {
++ | ^^^^^^^^ help: try this: `arr.get(idx)`
++ |
++ = note: `-D clippy::match-on-vec-items` implied by `-D warnings`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:16:11
++ |
++LL | match arr[range] {
++ | ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:29:11
++ |
++LL | match arr[idx] {
++ | ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:36:11
++ |
++LL | match arr[range] {
++ | ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:49:11
++ |
++LL | match arr[idx] {
++ | ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:56:11
++ |
++LL | match arr[range] {
++ | ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:69:11
++ |
++LL | match arr[idx] {
++ | ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++ --> $DIR/match_on_vec_items.rs:76:11
++ |
++LL | match arr[range] {
++ | ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(exclusive_range_pattern)]
++#![feature(half_open_range_patterns)]
++#![warn(clippy::match_overlapping_arm)]
++#![allow(clippy::redundant_pattern_matching)]
++
++/// Tests for match_overlapping_arm
++
++fn overlapping() {
++ const FOO: u64 = 2;
++
++ match 42 {
++ 0..=10 => println!("0 ... 10"),
++ 0..=11 => println!("0 ... 11"),
++ _ => (),
++ }
++
++ match 42 {
++ 0..=5 => println!("0 ... 5"),
++ 6..=7 => println!("6 ... 7"),
++ FOO..=11 => println!("0 ... 11"),
++ _ => (),
++ }
++
++ match 42 {
++ 2 => println!("2"),
++ 0..=5 => println!("0 ... 5"),
++ _ => (),
++ }
++
++ match 42 {
++ 2 => println!("2"),
++ 0..=2 => println!("0 ... 2"),
++ _ => (),
++ }
++
++ match 42 {
++ 0..=10 => println!("0 ... 10"),
++ 11..=50 => println!("11 ... 50"),
++ _ => (),
++ }
++
++ match 42 {
++ 2 => println!("2"),
++ 0..2 => println!("0 .. 2"),
++ _ => (),
++ }
++
++ match 42 {
++ 0..10 => println!("0 .. 10"),
++ 10..50 => println!("10 .. 50"),
++ _ => (),
++ }
++
++ match 42 {
++ 0..11 => println!("0 .. 11"),
++ 0..=11 => println!("0 ... 11"),
++ _ => (),
++ }
++
++ /*
++ // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns
++ match 42 {
++ 0.. => println!("0 .. 42"),
++ 3.. => println!("3 .. 42"),
++ _ => (),
++ }
++
++ match 42 {
++ ..=23 => println!("0 ... 23"),
++ ..26 => println!("0 .. 26"),
++ _ => (),
++ }
++ */
++
++ if let None = Some(42) {
++ // nothing
++ } else if let None = Some(42) {
++ // another nothing :-)
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: some ranges overlap
++ --> $DIR/match_overlapping_arm.rs:12:9
++ |
++LL | 0..=10 => println!("0 ... 10"),
++ | ^^^^^^
++ |
++ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
++note: overlaps with this
++ --> $DIR/match_overlapping_arm.rs:13:9
++ |
++LL | 0..=11 => println!("0 ... 11"),
++ | ^^^^^^
++
++error: some ranges overlap
++ --> $DIR/match_overlapping_arm.rs:18:9
++ |
++LL | 0..=5 => println!("0 ... 5"),
++ | ^^^^^
++ |
++note: overlaps with this
++ --> $DIR/match_overlapping_arm.rs:20:9
++ |
++LL | FOO..=11 => println!("0 ... 11"),
++ | ^^^^^^^^
++
++error: some ranges overlap
++ --> $DIR/match_overlapping_arm.rs:26:9
++ |
++LL | 0..=5 => println!("0 ... 5"),
++ | ^^^^^
++ |
++note: overlaps with this
++ --> $DIR/match_overlapping_arm.rs:25:9
++ |
++LL | 2 => println!("2"),
++ | ^
++
++error: some ranges overlap
++ --> $DIR/match_overlapping_arm.rs:32:9
++ |
++LL | 0..=2 => println!("0 ... 2"),
++ | ^^^^^
++ |
++note: overlaps with this
++ --> $DIR/match_overlapping_arm.rs:31:9
++ |
++LL | 2 => println!("2"),
++ | ^
++
++error: some ranges overlap
++ --> $DIR/match_overlapping_arm.rs:55:9
++ |
++LL | 0..11 => println!("0 .. 11"),
++ | ^^^^^
++ |
++note: overlaps with this
++ --> $DIR/match_overlapping_arm.rs:56:9
++ |
++LL | 0..=11 => println!("0 ... 11"),
++ | ^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::match_ref_pats)]
++
++fn ref_pats() {
++ {
++ let v = &Some(0);
++ match v {
++ &Some(v) => println!("{:?}", v),
++ &None => println!("none"),
++ }
++ match v {
++ // This doesn't trigger; we have a different pattern.
++ &Some(v) => println!("some"),
++ other => println!("other"),
++ }
++ }
++ let tup = &(1, 2);
++ match tup {
++ &(v, 1) => println!("{}", v),
++ _ => println!("none"),
++ }
++ // Special case: using `&` both in expr and pats.
++ let w = Some(0);
++ match &w {
++ &Some(v) => println!("{:?}", v),
++ &None => println!("none"),
++ }
++ // False positive: only wildcard pattern.
++ let w = Some(0);
++ #[allow(clippy::match_single_binding)]
++ match w {
++ _ => println!("none"),
++ }
++
++ let a = &Some(0);
++ if let &None = a {
++ println!("none");
++ }
++
++ let b = Some(0);
++ if let &None = &b {
++ println!("none");
++ }
++}
++
++mod ice_3719 {
++ macro_rules! foo_variant(
++ ($idx:expr) => (Foo::get($idx).unwrap())
++ );
++
++ enum Foo {
++ A,
++ B,
++ }
++
++ impl Foo {
++ fn get(idx: u8) -> Option<&'static Self> {
++ match idx {
++ 0 => Some(&Foo::A),
++ 1 => Some(&Foo::B),
++ _ => None,
++ }
++ }
++ }
++
++ fn ice_3719() {
++ // ICE #3719
++ match foo_variant!(0) {
++ &Foo::A => println!("A"),
++ _ => println!("Wild"),
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you don't need to add `&` to all patterns
++ --> $DIR/match_ref_pats.rs:6:9
++ |
++LL | / match v {
++LL | | &Some(v) => println!("{:?}", v),
++LL | | &None => println!("none"),
++LL | | }
++ | |_________^
++ |
++ = note: `-D clippy::match-ref-pats` implied by `-D warnings`
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++ |
++LL | match *v {
++LL | Some(v) => println!("{:?}", v),
++LL | None => println!("none"),
++ |
++
++error: you don't need to add `&` to all patterns
++ --> $DIR/match_ref_pats.rs:17:5
++ |
++LL | / match tup {
++LL | | &(v, 1) => println!("{}", v),
++LL | | _ => println!("none"),
++LL | | }
++ | |_____^
++ |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++ |
++LL | match *tup {
++LL | (v, 1) => println!("{}", v),
++ |
++
++error: you don't need to add `&` to both the expression and the patterns
++ --> $DIR/match_ref_pats.rs:23:5
++ |
++LL | / match &w {
++LL | | &Some(v) => println!("{:?}", v),
++LL | | &None => println!("none"),
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | match w {
++LL | Some(v) => println!("{:?}", v),
++LL | None => println!("none"),
++ |
++
++error: you don't need to add `&` to all patterns
++ --> $DIR/match_ref_pats.rs:35:5
++ |
++LL | / if let &None = a {
++LL | | println!("none");
++LL | | }
++ | |_____^
++ |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++ |
++LL | if let None = *a {
++ | ^^^^ ^^
++
++error: you don't need to add `&` to both the expression and the patterns
++ --> $DIR/match_ref_pats.rs:40:5
++ |
++LL | / if let &None = &b {
++LL | | println!("none");
++LL | | }
++ | |_____^
++ |
++help: try
++ |
++LL | if let None = b {
++ | ^^^^ ^
++
++error: you don't need to add `&` to all patterns
++ --> $DIR/match_ref_pats.rs:67:9
++ |
++LL | / match foo_variant!(0) {
++LL | | &Foo::A => println!("A"),
++LL | | _ => println!("Wild"),
++LL | | }
++ | |_________^
++ |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++ |
++LL | match *foo_variant!(0) {
++LL | Foo::A => println!("A"),
++ |
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::match_same_arms)]
++
++pub enum Abc {
++ A,
++ B,
++ C,
++}
++
++fn match_same_arms() {
++ let _ = match Abc::A {
++ Abc::A => 0,
++ Abc::B => 1,
++ _ => 0, //~ ERROR match arms have same body
++ };
++
++ match (1, 2, 3) {
++ (1, .., 3) => 42,
++ (.., 3) => 42, //~ ERROR match arms have same body
++ _ => 0,
++ };
++
++ let _ = match 42 {
++ 42 => 1,
++ 51 => 1, //~ ERROR match arms have same body
++ 41 => 2,
++ 52 => 2, //~ ERROR match arms have same body
++ _ => 0,
++ };
++
++ let _ = match 42 {
++ 1 => 2,
++ 2 => 2, //~ ERROR 2nd matched arms have same body
++ 3 => 2, //~ ERROR 3rd matched arms have same body
++ 4 => 3,
++ _ => 0,
++ };
++}
++
++mod issue4244 {
++ #[derive(PartialEq, PartialOrd, Eq, Ord)]
++ pub enum CommandInfo {
++ BuiltIn { name: String, about: Option<String> },
++ External { name: String, path: std::path::PathBuf },
++ }
++
++ impl CommandInfo {
++ pub fn name(&self) -> String {
++ match self {
++ CommandInfo::BuiltIn { name, .. } => name.to_string(),
++ CommandInfo::External { name, .. } => name.to_string(),
++ }
++ }
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++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,
++ | ^^^^^^^^^^
++
++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,
++ | ^^
++
++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,
++ | ^^
++
++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,
++ | ^
++
++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,
++ | ^
++
++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
++ | ^
++
++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(),
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::match_same_arms)]
++#![allow(clippy::blacklisted_name)]
++
++fn bar<T>(_: T) {}
++fn foo() -> bool {
++ unimplemented!()
++}
++
++fn match_same_arms() {
++ let _ = match 42 {
++ 42 => {
++ foo();
++ let mut a = 42 + [23].len() as i32;
++ if true {
++ a += 7;
++ }
++ a = -31 - a;
++ a
++ },
++ _ => {
++ //~ ERROR match arms have same body
++ foo();
++ let mut a = 42 + [23].len() as i32;
++ if true {
++ a += 7;
++ }
++ a = -31 - a;
++ a
++ },
++ };
++
++ let _ = match 42 {
++ 42 => foo(),
++ 51 => foo(), //~ ERROR match arms have same body
++ _ => true,
++ };
++
++ let _ = match Some(42) {
++ Some(_) => 24,
++ None => 24, //~ ERROR match arms have same body
++ };
++
++ let _ = match Some(42) {
++ Some(foo) => 24,
++ None => 24,
++ };
++
++ let _ = match Some(42) {
++ Some(42) => 24,
++ Some(a) => 24, // bindings are different
++ None => 0,
++ };
++
++ let _ = match Some(42) {
++ Some(a) if a > 0 => 24,
++ Some(a) => 24, // one arm has a guard
++ None => 0,
++ };
++
++ match (Some(42), Some(42)) {
++ (Some(a), None) => bar(a),
++ (None, Some(a)) => bar(a), //~ ERROR match arms have same body
++ _ => (),
++ }
++
++ match (Some(42), Some(42)) {
++ (Some(a), ..) => bar(a),
++ (.., Some(a)) => bar(a), //~ ERROR match arms have same body
++ _ => (),
++ }
++
++ let _ = match Some(()) {
++ Some(()) => 0.0,
++ None => -0.0,
++ };
++
++ match (Some(42), Some("")) {
++ (Some(a), None) => bar(a),
++ (None, Some(a)) => bar(a), // bindings have different types
++ _ => (),
++ }
++
++ let x: Result<i32, &str> = Ok(3);
++
++ // No warning because of the guard.
++ match x {
++ Ok(x) if x * x == 64 => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => println!("err"),
++ }
++
++ // This used to be a false positive; see issue #1996.
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(x) if x * x == 64 => println!("ok 64"),
++ Ok(_) => println!("ok"),
++ Err(_) => println!("err"),
++ }
++
++ match (x, Some(1i32)) {
++ (Ok(x), Some(_)) => println!("ok {}", x),
++ (Ok(_), Some(x)) => println!("ok {}", x),
++ _ => println!("err"),
++ }
++
++ // No warning; different types for `x`.
++ match (x, Some(1.0f64)) {
++ (Ok(x), Some(_)) => println!("ok {}", x),
++ (Ok(_), Some(x)) => println!("ok {}", x),
++ _ => println!("err"),
++ }
++
++ // False negative #2251.
++ match x {
++ Ok(_tmp) => println!("ok"),
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => {
++ unreachable!();
++ },
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++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(),
++ | ^^
++
++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,
++ | ^^^^^^^
++
++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),
++ | ^^^^^^^^^^^^^^^
++
++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),
++ | ^^^^^^^^^^^^^
++
++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),
++ | ^^^^^^^^^^^^^^^^
++ = note: this error originates in a macro (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"),
++ | ^^^^^
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
++
++struct Point {
++ x: i32,
++ y: i32,
++}
++
++fn coords() -> Point {
++ Point { x: 1, y: 2 }
++}
++
++macro_rules! foo {
++ ($param:expr) => {
++ match $param {
++ _ => println!("whatever"),
++ }
++ };
++}
++
++fn main() {
++ let a = 1;
++ let b = 2;
++ let c = 3;
++ // Lint
++ let (x, y, z) = (a, b, c);
++ {
++ println!("{} {} {}", x, y, z);
++ }
++ // Lint
++ let (x, y, z) = (a, b, c);
++ println!("{} {} {}", x, y, z);
++ // Ok
++ foo!(a);
++ // Ok
++ match a {
++ 2 => println!("2"),
++ _ => println!("Not 2"),
++ }
++ // Ok
++ let d = Some(5);
++ match d {
++ Some(d) => println!("{}", d),
++ _ => println!("None"),
++ }
++ // Lint
++ println!("whatever");
++ // Lint
++ {
++ let x = 29;
++ println!("x has a value of {}", x);
++ }
++ // Lint
++ {
++ let e = 5 * a;
++ if e >= 5 {
++ println!("e is superior to 5");
++ }
++ }
++ // Lint
++ let p = Point { x: 0, y: 7 };
++ let Point { x, y } = p;
++ println!("Coords: ({}, {})", x, y);
++ // Lint
++ let Point { x: x1, y: y1 } = p;
++ println!("Coords: ({}, {})", x1, y1);
++ // Lint
++ let x = 5;
++ let ref r = x;
++ println!("Got a reference to {}", r);
++ // Lint
++ let mut x = 5;
++ let ref mut mr = x;
++ println!("Got a mutable reference to {}", mr);
++ // Lint
++ let Point { x, y } = coords();
++ let product = x * y;
++ // Lint
++ let v = vec![Some(1), Some(2), Some(3), Some(4)];
++ #[allow(clippy::let_and_return)]
++ let _ = v
++ .iter()
++ .map(|i| {
++ let unwrapped = i.unwrap();
++ unwrapped
++ })
++ .collect::<Vec<u8>>();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
++
++struct Point {
++ x: i32,
++ y: i32,
++}
++
++fn coords() -> Point {
++ Point { x: 1, y: 2 }
++}
++
++macro_rules! foo {
++ ($param:expr) => {
++ match $param {
++ _ => println!("whatever"),
++ }
++ };
++}
++
++fn main() {
++ let a = 1;
++ let b = 2;
++ let c = 3;
++ // Lint
++ match (a, b, c) {
++ (x, y, z) => {
++ println!("{} {} {}", x, y, z);
++ },
++ }
++ // Lint
++ match (a, b, c) {
++ (x, y, z) => println!("{} {} {}", x, y, z),
++ }
++ // Ok
++ foo!(a);
++ // Ok
++ match a {
++ 2 => println!("2"),
++ _ => println!("Not 2"),
++ }
++ // Ok
++ let d = Some(5);
++ match d {
++ Some(d) => println!("{}", d),
++ _ => println!("None"),
++ }
++ // Lint
++ match a {
++ _ => println!("whatever"),
++ }
++ // Lint
++ match a {
++ _ => {
++ let x = 29;
++ println!("x has a value of {}", x);
++ },
++ }
++ // Lint
++ match a {
++ _ => {
++ let e = 5 * a;
++ if e >= 5 {
++ println!("e is superior to 5");
++ }
++ },
++ }
++ // Lint
++ let p = Point { x: 0, y: 7 };
++ match p {
++ Point { x, y } => println!("Coords: ({}, {})", x, y),
++ }
++ // Lint
++ match p {
++ Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
++ }
++ // Lint
++ let x = 5;
++ match x {
++ ref r => println!("Got a reference to {}", r),
++ }
++ // Lint
++ let mut x = 5;
++ match x {
++ ref mut mr => println!("Got a mutable reference to {}", mr),
++ }
++ // Lint
++ let product = match coords() {
++ Point { x, y } => x * y,
++ };
++ // Lint
++ let v = vec![Some(1), Some(2), Some(3), Some(4)];
++ #[allow(clippy::let_and_return)]
++ let _ = v
++ .iter()
++ .map(|i| match i.unwrap() {
++ unwrapped => unwrapped,
++ })
++ .collect::<Vec<u8>>();
++}
--- /dev/null
--- /dev/null
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:28:5
++ |
++LL | / match (a, b, c) {
++LL | | (x, y, z) => {
++LL | | println!("{} {} {}", x, y, z);
++LL | | },
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::match-single-binding` implied by `-D warnings`
++help: consider using `let` statement
++ |
++LL | let (x, y, z) = (a, b, c);
++LL | {
++LL | println!("{} {} {}", x, y, z);
++LL | }
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:34:5
++ |
++LL | / match (a, b, c) {
++LL | | (x, y, z) => println!("{} {} {}", x, y, z),
++LL | | }
++ | |_____^
++ |
++help: consider using `let` statement
++ |
++LL | let (x, y, z) = (a, b, c);
++LL | println!("{} {} {}", x, y, z);
++ |
++
++error: this match could be replaced by its body itself
++ --> $DIR/match_single_binding.rs:51:5
++ |
++LL | / match a {
++LL | | _ => println!("whatever"),
++LL | | }
++ | |_____^ help: consider using the match body instead: `println!("whatever");`
++
++error: this match could be replaced by its body itself
++ --> $DIR/match_single_binding.rs:55:5
++ |
++LL | / match a {
++LL | | _ => {
++LL | | let x = 29;
++LL | | println!("x has a value of {}", x);
++LL | | },
++LL | | }
++ | |_____^
++ |
++help: consider using the match body instead
++ |
++LL | {
++LL | let x = 29;
++LL | println!("x has a value of {}", x);
++LL | }
++ |
++
++error: this match could be replaced by its body itself
++ --> $DIR/match_single_binding.rs:62:5
++ |
++LL | / match a {
++LL | | _ => {
++LL | | let e = 5 * a;
++LL | | if e >= 5 {
++... |
++LL | | },
++LL | | }
++ | |_____^
++ |
++help: consider using the match body instead
++ |
++LL | {
++LL | let e = 5 * a;
++LL | if e >= 5 {
++LL | println!("e is superior to 5");
++LL | }
++LL | }
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:72:5
++ |
++LL | / match p {
++LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
++LL | | }
++ | |_____^
++ |
++help: consider using `let` statement
++ |
++LL | let Point { x, y } = p;
++LL | println!("Coords: ({}, {})", x, y);
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:76:5
++ |
++LL | / match p {
++LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
++LL | | }
++ | |_____^
++ |
++help: consider using `let` statement
++ |
++LL | let Point { x: x1, y: y1 } = p;
++LL | println!("Coords: ({}, {})", x1, y1);
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:81:5
++ |
++LL | / match x {
++LL | | ref r => println!("Got a reference to {}", r),
++LL | | }
++ | |_____^
++ |
++help: consider using `let` statement
++ |
++LL | let ref r = x;
++LL | println!("Got a reference to {}", r);
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:86:5
++ |
++LL | / match x {
++LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
++LL | | }
++ | |_____^
++ |
++help: consider using `let` statement
++ |
++LL | let ref mut mr = x;
++LL | println!("Got a mutable reference to {}", mr);
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:90:5
++ |
++LL | / let product = match coords() {
++LL | | Point { x, y } => x * y,
++LL | | };
++ | |______^
++ |
++help: consider using `let` statement
++ |
++LL | let Point { x, y } = coords();
++LL | let product = x * y;
++ |
++
++error: this match could be written as a `let` statement
++ --> $DIR/match_single_binding.rs:98:18
++ |
++LL | .map(|i| match i.unwrap() {
++ | __________________^
++LL | | unwrapped => unwrapped,
++LL | | })
++ | |_________^
++ |
++help: consider using `let` statement
++ |
++LL | .map(|i| {
++LL | let unwrapped = i.unwrap();
++LL | unwrapped
++LL | })
++ |
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(exclusive_range_pattern)]
++#![allow(clippy::match_same_arms)]
++#![warn(clippy::match_wild_err_arm)]
++
++fn match_wild_err_arm() {
++ let x: Result<i32, &str> = Ok(3);
++
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => panic!("err"),
++ }
++
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => panic!(),
++ }
++
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => {
++ panic!();
++ },
++ }
++
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_e) => panic!(),
++ }
++
++ // Allowed when used in `panic!`.
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_e) => panic!("{}", _e),
++ }
++
++ // Allowed when not with `panic!` block.
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => println!("err"),
++ }
++
++ // Allowed when used with `unreachable!`.
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => unreachable!(),
++ }
++
++ // Allowed when used with `unreachable!`.
++ match x {
++ Ok(3) => println!("ok"),
++ Ok(_) => println!("ok"),
++ Err(_) => {
++ unreachable!();
++ },
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:11:9
++ |
++LL | Err(_) => panic!("err"),
++ | ^^^^^^
++ |
++ = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
++ = note: match each error separately or use the error output
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:17:9
++ |
++LL | Err(_) => panic!(),
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output
++
++error: `Err(_)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:23:9
++ |
++LL | Err(_) => {
++ | ^^^^^^
++ |
++ = note: match each error separately or use the error output
++
++error: `Err(_e)` matches all errors
++ --> $DIR/match_wild_err_arm.rs:31:9
++ |
++LL | Err(_e) => panic!(),
++ | ^^^^^^^
++ |
++ = note: match each error separately or use the error output
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++ One(usize),
++ Two(u8),
++}
++
++fn main() {
++ // bad
++ mem::discriminant(&Some(2));
++ mem::discriminant(&None::<u8>);
++ mem::discriminant(&Foo::One(5));
++ mem::discriminant(&Foo::Two(5));
++
++ let ro = &Some(3);
++ let rro = &ro;
++ mem::discriminant(ro);
++ mem::discriminant(*rro);
++ mem::discriminant(*rro);
++
++ macro_rules! mem_discriminant_but_in_a_macro {
++ ($param:expr) => {
++ mem::discriminant($param)
++ };
++ }
++
++ mem_discriminant_but_in_a_macro!(*rro);
++
++ let rrrrro = &&&rro;
++ mem::discriminant(****rrrrro);
++ mem::discriminant(****rrrrro);
++
++ // ok
++ mem::discriminant(&Some(2));
++ mem::discriminant(&None::<u8>);
++ mem::discriminant(&Foo::One(5));
++ mem::discriminant(&Foo::Two(5));
++ mem::discriminant(ro);
++ mem::discriminant(*rro);
++ mem::discriminant(****rrrrro);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++ One(usize),
++ Two(u8),
++}
++
++fn main() {
++ // bad
++ mem::discriminant(&&Some(2));
++ mem::discriminant(&&None::<u8>);
++ mem::discriminant(&&Foo::One(5));
++ mem::discriminant(&&Foo::Two(5));
++
++ let ro = &Some(3);
++ let rro = &ro;
++ mem::discriminant(&ro);
++ mem::discriminant(rro);
++ mem::discriminant(&rro);
++
++ macro_rules! mem_discriminant_but_in_a_macro {
++ ($param:expr) => {
++ mem::discriminant($param)
++ };
++ }
++
++ mem_discriminant_but_in_a_macro!(&rro);
++
++ let rrrrro = &&&rro;
++ mem::discriminant(&rrrrro);
++ mem::discriminant(*rrrrro);
++
++ // ok
++ mem::discriminant(&Some(2));
++ mem::discriminant(&None::<u8>);
++ mem::discriminant(&Foo::One(5));
++ mem::discriminant(&Foo::Two(5));
++ mem::discriminant(ro);
++ mem::discriminant(*rro);
++ mem::discriminant(****rrrrro);
++}
--- /dev/null
--- /dev/null
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:14:5
++ |
++LL | mem::discriminant(&&Some(2));
++ | ^^^^^^^^^^^^^^^^^^---------^
++ | |
++ | help: try dereferencing: `&Some(2)`
++ |
++note: the lint level is defined here
++ --> $DIR/mem_discriminant.rs:3:9
++ |
++LL | #![deny(clippy::mem_discriminant_non_enum)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>`
++ --> $DIR/mem_discriminant.rs:15:5
++ |
++LL | mem::discriminant(&&None::<u8>);
++ | ^^^^^^^^^^^^^^^^^^------------^
++ | |
++ | help: try dereferencing: `&None::<u8>`
++
++error: calling `mem::discriminant` on non-enum type `&Foo`
++ --> $DIR/mem_discriminant.rs:16:5
++ |
++LL | mem::discriminant(&&Foo::One(5));
++ | ^^^^^^^^^^^^^^^^^^-------------^
++ | |
++ | help: try dereferencing: `&Foo::One(5)`
++
++error: calling `mem::discriminant` on non-enum type `&Foo`
++ --> $DIR/mem_discriminant.rs:17:5
++ |
++LL | mem::discriminant(&&Foo::Two(5));
++ | ^^^^^^^^^^^^^^^^^^-------------^
++ | |
++ | help: try dereferencing: `&Foo::Two(5)`
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:21:5
++ |
++LL | mem::discriminant(&ro);
++ | ^^^^^^^^^^^^^^^^^^---^
++ | |
++ | help: try dereferencing: `ro`
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:22:5
++ |
++LL | mem::discriminant(rro);
++ | ^^^^^^^^^^^^^^^^^^---^
++ | |
++ | help: try dereferencing: `*rro`
++
++error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:23:5
++ |
++LL | mem::discriminant(&rro);
++ | ^^^^^^^^^^^^^^^^^^----^
++ | |
++ | help: try dereferencing: `*rro`
++
++error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:27:13
++ |
++LL | mem::discriminant($param)
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | mem_discriminant_but_in_a_macro!(&rro);
++ | ---------------------------------------
++ | | |
++ | | help: try dereferencing: `*rro`
++ | in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:34:5
++ |
++LL | mem::discriminant(&rrrrro);
++ | ^^^^^^^^^^^^^^^^^^-------^
++ | |
++ | help: try dereferencing: `****rrrrro`
++
++error: calling `mem::discriminant` on non-enum type `&&&std::option::Option<i32>`
++ --> $DIR/mem_discriminant.rs:35:5
++ |
++LL | mem::discriminant(*rrrrro);
++ | ^^^^^^^^^^^^^^^^^^-------^
++ | |
++ | help: try dereferencing: `****rrrrro`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++ One(usize),
++ Two(u8),
++}
++
++struct A(Foo);
++
++fn main() {
++ // bad
++ mem::discriminant(&"hello");
++ mem::discriminant(&A(Foo::One(0)));
++}
--- /dev/null
--- /dev/null
++error: calling `mem::discriminant` on non-enum type `&str`
++ --> $DIR/mem_discriminant_unfixable.rs:14:5
++ |
++LL | mem::discriminant(&"hello");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/mem_discriminant_unfixable.rs:1:9
++ |
++LL | #![deny(clippy::mem_discriminant_non_enum)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: calling `mem::discriminant` on non-enum type `A`
++ --> $DIR/mem_discriminant_unfixable.rs:15:5
++ |
++LL | mem::discriminant(&A(Foo::One(0)));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++use std::rc::Rc;
++use std::sync::Arc;
++
++use std::mem as memstuff;
++use std::mem::forget as forgetSomething;
++
++#[warn(clippy::mem_forget)]
++#[allow(clippy::forget_copy)]
++fn main() {
++ let five: i32 = 5;
++ forgetSomething(five);
++
++ let six: Arc<i32> = Arc::new(6);
++ memstuff::forget(six);
++
++ let seven: Rc<i32> = Rc::new(7);
++ std::mem::forget(seven);
++
++ let eight: Vec<i32> = vec![8];
++ forgetSomething(eight);
++
++ std::mem::forget(7);
++}
--- /dev/null
--- /dev/null
++error: usage of `mem::forget` on `Drop` type
++ --> $DIR/mem_forget.rs:14:5
++ |
++LL | memstuff::forget(six);
++ | ^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::mem-forget` implied by `-D warnings`
++
++error: usage of `mem::forget` on `Drop` type
++ --> $DIR/mem_forget.rs:17:5
++ |
++LL | std::mem::forget(seven);
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: usage of `mem::forget` on `Drop` type
++ --> $DIR/mem_forget.rs:20:5
++ |
++LL | forgetSomething(eight);
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(
++ clippy::all,
++ clippy::style,
++ clippy::mem_replace_option_with_none,
++ clippy::mem_replace_with_default
++)]
++
++use std::mem;
++
++fn replace_option_with_none() {
++ let mut an_option = Some(1);
++ let _ = an_option.take();
++ let an_option = &mut Some(1);
++ let _ = an_option.take();
++}
++
++fn replace_with_default() {
++ let mut s = String::from("foo");
++ let _ = std::mem::take(&mut s);
++ let s = &mut String::from("foo");
++ let _ = std::mem::take(s);
++ let _ = std::mem::take(s);
++}
++
++fn main() {
++ replace_option_with_none();
++ replace_with_default();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(
++ clippy::all,
++ clippy::style,
++ clippy::mem_replace_option_with_none,
++ clippy::mem_replace_with_default
++)]
++
++use std::mem;
++
++fn replace_option_with_none() {
++ let mut an_option = Some(1);
++ let _ = mem::replace(&mut an_option, None);
++ let an_option = &mut Some(1);
++ let _ = mem::replace(an_option, None);
++}
++
++fn replace_with_default() {
++ let mut s = String::from("foo");
++ let _ = std::mem::replace(&mut s, String::default());
++ let s = &mut String::from("foo");
++ let _ = std::mem::replace(s, String::default());
++ let _ = std::mem::replace(s, Default::default());
++}
++
++fn main() {
++ replace_option_with_none();
++ replace_with_default();
++}
--- /dev/null
--- /dev/null
++error: replacing an `Option` with `None`
++ --> $DIR/mem_replace.rs:14:13
++ |
++LL | let _ = mem::replace(&mut an_option, None);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
++ |
++ = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
++
++error: replacing an `Option` with `None`
++ --> $DIR/mem_replace.rs:16:13
++ |
++LL | let _ = mem::replace(an_option, None);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
++
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++ --> $DIR/mem_replace.rs:21:13
++ |
++LL | let _ = std::mem::replace(&mut s, String::default());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
++ |
++ = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
++
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++ --> $DIR/mem_replace.rs:23:13
++ |
++LL | let _ = std::mem::replace(s, String::default());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
++
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++ --> $DIR/mem_replace.rs:24:13
++ |
++LL | let _ = std::mem::replace(s, Default::default());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:macro_rules.rs
++#![warn(clippy::mem_replace_with_default)]
++
++#[macro_use]
++extern crate macro_rules;
++
++macro_rules! take {
++ ($s:expr) => {
++ std::mem::replace($s, Default::default())
++ };
++}
++
++fn replace_with_default() {
++ let s = &mut String::from("foo");
++ take!(s);
++ take_external!(s);
++}
++
++fn main() {
++ replace_with_default();
++}
--- /dev/null
--- /dev/null
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++ --> $DIR/mem_replace_macro.rs:9:9
++ |
++LL | std::mem::replace($s, Default::default())
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | take!(s);
++ | --------- in this macro invocation
++ |
++ = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// aux-build:option_helpers.rs
++// edition:2018
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(
++ clippy::blacklisted_name,
++ clippy::default_trait_access,
++ clippy::missing_docs_in_private_items,
++ clippy::missing_safety_doc,
++ clippy::non_ascii_literal,
++ clippy::new_without_default,
++ clippy::needless_pass_by_value,
++ clippy::print_stdout,
++ clippy::must_use_candidate,
++ clippy::use_self,
++ clippy::useless_format,
++ clippy::wrong_self_convention,
++ clippy::unused_self,
++ unused
++)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::HashSet;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++use std::ops::Mul;
++use std::rc::{self, Rc};
++use std::sync::{self, Arc};
++
++use option_helpers::IteratorFalsePositives;
++
++pub struct T;
++
++impl T {
++ pub fn add(self, other: T) -> T {
++ self
++ }
++
++ // no error, not public interface
++ pub(crate) fn drop(&mut self) {}
++
++ // no error, private function
++ fn neg(self) -> Self {
++ self
++ }
++
++ // no error, private function
++ fn eq(&self, other: T) -> bool {
++ true
++ }
++
++ // No error; self is a ref.
++ fn sub(&self, other: T) -> &T {
++ self
++ }
++
++ // No error; different number of arguments.
++ fn div(self) -> T {
++ self
++ }
++
++ // No error; wrong return type.
++ fn rem(self, other: T) {}
++
++ // Fine
++ fn into_u32(self) -> u32 {
++ 0
++ }
++
++ fn into_u16(&self) -> u16 {
++ 0
++ }
++
++ fn to_something(self) -> u32 {
++ 0
++ }
++
++ fn new(self) -> Self {
++ unimplemented!();
++ }
++}
++
++pub struct T1;
++
++impl T1 {
++ // Shouldn't trigger lint as it is unsafe.
++ pub unsafe fn add(self, rhs: T1) -> T1 {
++ self
++ }
++
++ // Should not trigger lint since this is an async function.
++ pub async fn next(&mut self) -> Option<T1> {
++ None
++ }
++}
++
++struct Lt<'a> {
++ foo: &'a u32,
++}
++
++impl<'a> Lt<'a> {
++ // The lifetime is different, but that’s irrelevant; see issue #734.
++ #[allow(clippy::needless_lifetimes)]
++ pub fn new<'b>(s: &'b str) -> Lt<'b> {
++ unimplemented!()
++ }
++}
++
++struct Lt2<'a> {
++ foo: &'a u32,
++}
++
++impl<'a> Lt2<'a> {
++ // The lifetime is different, but that’s irrelevant; see issue #734.
++ pub fn new(s: &str) -> Lt2 {
++ unimplemented!()
++ }
++}
++
++struct Lt3<'a> {
++ foo: &'a u32,
++}
++
++impl<'a> Lt3<'a> {
++ // The lifetime is different, but that’s irrelevant; see issue #734.
++ pub fn new() -> Lt3<'static> {
++ unimplemented!()
++ }
++}
++
++#[derive(Clone, Copy)]
++struct U;
++
++impl U {
++ fn new() -> Self {
++ U
++ }
++ // Ok because `U` is `Copy`.
++ fn to_something(self) -> u32 {
++ 0
++ }
++}
++
++struct V<T> {
++ _dummy: T,
++}
++
++impl<T> V<T> {
++ fn new() -> Option<V<T>> {
++ None
++ }
++}
++
++struct AsyncNew;
++
++impl AsyncNew {
++ async fn new() -> Option<Self> {
++ None
++ }
++}
++
++struct BadNew;
++
++impl BadNew {
++ fn new() -> i32 {
++ 0
++ }
++}
++
++impl Mul<T> for T {
++ type Output = T;
++ // No error, obviously.
++ fn mul(self, other: T) -> T {
++ self
++ }
++}
++
++/// Checks implementation of `FILTER_NEXT` lint.
++#[rustfmt::skip]
++fn filter_next() {
++ let v = vec![3, 2, 1, 0, -1, -2, -3];
++
++ // Single-line case.
++ let _ = v.iter().filter(|&x| *x < 0).next();
++
++ // Multi-line case.
++ let _ = v.iter().filter(|&x| {
++ *x < 0
++ }
++ ).next();
++
++ // Check that hat we don't lint if the caller is not an `Iterator`.
++ let foo = IteratorFalsePositives { foo: 0 };
++ let _ = foo.filter().next();
++}
++
++/// Checks implementation of `SEARCH_IS_SOME` lint.
++#[rustfmt::skip]
++fn search_is_some() {
++ let v = vec![3, 2, 1, 0, -1, -2, -3];
++ let y = &&42;
++
++ // Check `find().is_some()`, single-line case.
++ let _ = v.iter().find(|&x| *x < 0).is_some();
++ let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
++ let _ = (0..1).find(|x| *x == 0).is_some();
++ let _ = v.iter().find(|x| **x == 0).is_some();
++
++ // Check `find().is_some()`, multi-line case.
++ let _ = v.iter().find(|&x| {
++ *x < 0
++ }
++ ).is_some();
++
++ // Check `position().is_some()`, single-line case.
++ let _ = v.iter().position(|&x| x < 0).is_some();
++
++ // Check `position().is_some()`, multi-line case.
++ let _ = v.iter().position(|&x| {
++ x < 0
++ }
++ ).is_some();
++
++ // Check `rposition().is_some()`, single-line case.
++ let _ = v.iter().rposition(|&x| x < 0).is_some();
++
++ // Check `rposition().is_some()`, multi-line case.
++ let _ = v.iter().rposition(|&x| {
++ x < 0
++ }
++ ).is_some();
++
++ // Check that we don't lint if the caller is not an `Iterator`.
++ let foo = IteratorFalsePositives { foo: 0 };
++ let _ = foo.find().is_some();
++ let _ = foo.position().is_some();
++ let _ = foo.rposition().is_some();
++}
++
++fn main() {
++ filter_next();
++ search_is_some();
++}
--- /dev/null
--- /dev/null
++error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name
++ --> $DIR/methods.rs:39:5
++ |
++LL | / pub fn add(self, other: T) -> T {
++LL | | self
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::should-implement-trait` implied by `-D warnings`
++
++error: methods called `new` usually return `Self`
++ --> $DIR/methods.rs:169:5
++ |
++LL | / fn new() -> i32 {
++LL | | 0
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
++
++error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
++ --> $DIR/methods.rs:188:13
++ |
++LL | let _ = v.iter().filter(|&x| *x < 0).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::filter-next` implied by `-D warnings`
++ = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
++
++error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
++ --> $DIR/methods.rs:191:13
++ |
++LL | let _ = v.iter().filter(|&x| {
++ | _____________^
++LL | | *x < 0
++LL | | }
++LL | | ).next();
++ | |___________________________^
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:208:22
++ |
++LL | let _ = v.iter().find(|&x| *x < 0).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
++ |
++ = note: `-D clippy::search-is-some` implied by `-D warnings`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:209:20
++ |
++LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:210:20
++ |
++LL | let _ = (0..1).find(|x| *x == 0).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:211:22
++ |
++LL | let _ = v.iter().find(|x| **x == 0).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:214:13
++ |
++LL | let _ = v.iter().find(|&x| {
++ | _____________^
++LL | | *x < 0
++LL | | }
++LL | | ).is_some();
++ | |______________________________^
++
++error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:220:22
++ |
++LL | let _ = v.iter().position(|&x| x < 0).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
++
++error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:223:13
++ |
++LL | let _ = v.iter().position(|&x| {
++ | _____________^
++LL | | x < 0
++LL | | }
++LL | | ).is_some();
++ | |______________________________^
++
++error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:229:22
++ |
++LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
++
++error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
++ --> $DIR/methods.rs:232:13
++ |
++LL | let _ = v.iter().rposition(|&x| {
++ | _____________^
++LL | | x < 0
++LL | | }
++LL | | ).is_some();
++ | |______________________________^
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++
++use std::cmp::max as my_max;
++use std::cmp::min as my_min;
++use std::cmp::{max, min};
++
++const LARGE: usize = 3;
++
++fn main() {
++ let x;
++ x = 2usize;
++ min(1, max(3, x));
++ min(max(3, x), 1);
++ max(min(x, 1), 3);
++ max(3, min(x, 1));
++
++ my_max(3, my_min(x, 1));
++
++ min(3, max(1, x)); // ok, could be 1, 2 or 3 depending on x
++
++ min(1, max(LARGE, x)); // no error, we don't lookup consts here
++
++ let y = 2isize;
++ min(max(y, -1), 3);
++
++ let s;
++ s = "Hello";
++
++ min("Apple", max("Zoo", s));
++ max(min(s, "Apple"), "Zoo");
++
++ max("Apple", min(s, "Zoo")); // ok
++}
--- /dev/null
--- /dev/null
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:12:5
++ |
++LL | min(1, max(3, x));
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::min-max` implied by `-D warnings`
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:13:5
++ |
++LL | min(max(3, x), 1);
++ | ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:14:5
++ |
++LL | max(min(x, 1), 3);
++ | ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:15:5
++ |
++LL | max(3, min(x, 1));
++ | ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:17:5
++ |
++LL | my_max(3, my_min(x, 1));
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:29:5
++ |
++LL | min("Apple", max("Zoo", s));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++ --> $DIR/min_max.rs:30:5
++ |
++LL | max(min(s, "Apple"), "Zoo");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(target_os = "cloudabi")]
++fn cloudabi() {}
++
++#[cfg(target_os = "hermit")]
++fn hermit() {}
++
++#[cfg(target_os = "wasi")]
++fn wasi() {}
++
++#[cfg(target_os = "none")]
++fn none() {}
++
++// list with conditions
++#[cfg(all(not(any(windows, target_os = "cloudabi")), target_os = "wasi"))]
++fn list() {}
++
++// windows is a valid target family, should be ignored
++#[cfg(windows)]
++fn windows() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "hermit")]
++fn correct() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(cloudabi)]
++fn cloudabi() {}
++
++#[cfg(hermit)]
++fn hermit() {}
++
++#[cfg(wasi)]
++fn wasi() {}
++
++#[cfg(none)]
++fn none() {}
++
++// list with conditions
++#[cfg(all(not(any(windows, cloudabi)), wasi))]
++fn list() {}
++
++// windows is a valid target family, should be ignored
++#[cfg(windows)]
++fn windows() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "hermit")]
++fn correct() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_non_unix.rs:6:1
++ |
++LL | #[cfg(cloudabi)]
++ | ^^^^^^--------^^
++ | |
++ | help: try: `target_os = "cloudabi"`
++ |
++ = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_non_unix.rs:9:1
++ |
++LL | #[cfg(hermit)]
++ | ^^^^^^------^^
++ | |
++ | help: try: `target_os = "hermit"`
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_non_unix.rs:12:1
++ |
++LL | #[cfg(wasi)]
++ | ^^^^^^----^^
++ | |
++ | help: try: `target_os = "wasi"`
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_non_unix.rs:15:1
++ |
++LL | #[cfg(none)]
++ | ^^^^^^----^^
++ | |
++ | help: try: `target_os = "none"`
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_non_unix.rs:19:1
++ |
++LL | #[cfg(all(not(any(windows, cloudabi)), wasi))]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | #[cfg(all(not(any(windows, target_os = "cloudabi")), wasi))]
++ | ^^^^^^^^^^^^^^^^^^^^^^
++help: try
++ |
++LL | #[cfg(all(not(any(windows, cloudabi)), target_os = "wasi"))]
++ | ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(target_os = "linux")]
++fn linux() {}
++
++#[cfg(target_os = "freebsd")]
++fn freebsd() {}
++
++#[cfg(target_os = "dragonfly")]
++fn dragonfly() {}
++
++#[cfg(target_os = "openbsd")]
++fn openbsd() {}
++
++#[cfg(target_os = "netbsd")]
++fn netbsd() {}
++
++#[cfg(target_os = "macos")]
++fn macos() {}
++
++#[cfg(target_os = "ios")]
++fn ios() {}
++
++#[cfg(target_os = "android")]
++fn android() {}
++
++#[cfg(target_os = "emscripten")]
++fn emscripten() {}
++
++#[cfg(target_os = "fuchsia")]
++fn fuchsia() {}
++
++#[cfg(target_os = "haiku")]
++fn haiku() {}
++
++#[cfg(target_os = "illumos")]
++fn illumos() {}
++
++#[cfg(target_os = "l4re")]
++fn l4re() {}
++
++#[cfg(target_os = "redox")]
++fn redox() {}
++
++#[cfg(target_os = "solaris")]
++fn solaris() {}
++
++#[cfg(target_os = "vxworks")]
++fn vxworks() {}
++
++// list with conditions
++#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))]
++fn list() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "freebsd")]
++fn correct() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(linux)]
++fn linux() {}
++
++#[cfg(freebsd)]
++fn freebsd() {}
++
++#[cfg(dragonfly)]
++fn dragonfly() {}
++
++#[cfg(openbsd)]
++fn openbsd() {}
++
++#[cfg(netbsd)]
++fn netbsd() {}
++
++#[cfg(macos)]
++fn macos() {}
++
++#[cfg(ios)]
++fn ios() {}
++
++#[cfg(android)]
++fn android() {}
++
++#[cfg(emscripten)]
++fn emscripten() {}
++
++#[cfg(fuchsia)]
++fn fuchsia() {}
++
++#[cfg(haiku)]
++fn haiku() {}
++
++#[cfg(illumos)]
++fn illumos() {}
++
++#[cfg(l4re)]
++fn l4re() {}
++
++#[cfg(redox)]
++fn redox() {}
++
++#[cfg(solaris)]
++fn solaris() {}
++
++#[cfg(vxworks)]
++fn vxworks() {}
++
++// list with conditions
++#[cfg(all(not(any(solaris, linux)), freebsd))]
++fn list() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "freebsd")]
++fn correct() {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:6:1
++ |
++LL | #[cfg(linux)]
++ | ^^^^^^-----^^
++ | |
++ | help: try: `target_os = "linux"`
++ |
++ = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:9:1
++ |
++LL | #[cfg(freebsd)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "freebsd"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:12:1
++ |
++LL | #[cfg(dragonfly)]
++ | ^^^^^^---------^^
++ | |
++ | help: try: `target_os = "dragonfly"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:15:1
++ |
++LL | #[cfg(openbsd)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "openbsd"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:18:1
++ |
++LL | #[cfg(netbsd)]
++ | ^^^^^^------^^
++ | |
++ | help: try: `target_os = "netbsd"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:21:1
++ |
++LL | #[cfg(macos)]
++ | ^^^^^^-----^^
++ | |
++ | help: try: `target_os = "macos"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:24:1
++ |
++LL | #[cfg(ios)]
++ | ^^^^^^---^^
++ | |
++ | help: try: `target_os = "ios"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:27:1
++ |
++LL | #[cfg(android)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "android"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:30:1
++ |
++LL | #[cfg(emscripten)]
++ | ^^^^^^----------^^
++ | |
++ | help: try: `target_os = "emscripten"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:33:1
++ |
++LL | #[cfg(fuchsia)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "fuchsia"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:36:1
++ |
++LL | #[cfg(haiku)]
++ | ^^^^^^-----^^
++ | |
++ | help: try: `target_os = "haiku"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:39:1
++ |
++LL | #[cfg(illumos)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "illumos"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:42:1
++ |
++LL | #[cfg(l4re)]
++ | ^^^^^^----^^
++ | |
++ | help: try: `target_os = "l4re"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:45:1
++ |
++LL | #[cfg(redox)]
++ | ^^^^^^-----^^
++ | |
++ | help: try: `target_os = "redox"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:48:1
++ |
++LL | #[cfg(solaris)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "solaris"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:51:1
++ |
++LL | #[cfg(vxworks)]
++ | ^^^^^^-------^^
++ | |
++ | help: try: `target_os = "vxworks"`
++ |
++ = help: Did you mean `unix`?
++
++error: operating system used in target family position
++ --> $DIR/mismatched_target_os_unix.rs:55:1
++ |
++LL | #[cfg(all(not(any(solaris, linux)), freebsd))]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: Did you mean `unix`?
++help: try
++ |
++LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))]
++ | ^^^^^^^^^^^^^^^^^^^^^
++help: try
++ |
++LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))]
++ | ^^^^^^^^^^^^^^^^^^^
++help: try
++ |
++LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_docs_in_private_items)]
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: missing documentation for crate
++ --> $DIR/missing-doc-crate-missing.rs:1:1
++ |
++LL | / #![warn(clippy::missing_docs_in_private_items)]
++LL | |
++LL | | fn main() {}
++ | |____________^
++ |
++ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_docs_in_private_items)]
++#![feature(external_doc)]
++#![doc(include = "../../README.md")]
++
++fn main() {}
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_docs_in_private_items)]
++#![allow(dead_code)]
++#![feature(associated_type_defaults)]
++
++//! Some garbage docs for the crate here
++#![doc = "More garbage"]
++
++struct Foo {
++ a: isize,
++ b: isize,
++}
++
++pub struct PubFoo {
++ pub a: isize,
++ b: isize,
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub struct PubFoo2 {
++ pub a: isize,
++ pub c: isize,
++}
++
++/// dox
++pub trait A {
++ /// dox
++ fn foo(&self);
++ /// dox
++ fn foo_with_impl(&self) {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++trait B {
++ fn foo(&self);
++ fn foo_with_impl(&self) {}
++}
++
++pub trait C {
++ fn foo(&self);
++ fn foo_with_impl(&self) {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub trait D {
++ fn dummy(&self) {}
++}
++
++/// dox
++pub trait E: Sized {
++ type AssociatedType;
++ type AssociatedTypeDef = Self;
++
++ /// dox
++ type DocumentedType;
++ /// dox
++ type DocumentedTypeDef = Self;
++ /// dox
++ fn dummy(&self) {}
++}
++
++impl Foo {
++ pub fn foo() {}
++ fn bar() {}
++}
++
++impl PubFoo {
++ pub fn foo() {}
++ /// dox
++ pub fn foo1() {}
++ fn foo2() {}
++ #[allow(clippy::missing_docs_in_private_items)]
++ pub fn foo3() {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++trait F {
++ fn a();
++ fn b(&self);
++}
++
++// should need to redefine documentation for implementations of traits
++impl F for Foo {
++ fn a() {}
++ fn b(&self) {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: missing documentation for a struct
++ --> $DIR/missing-doc-impl.rs:8:1
++ |
++LL | / struct Foo {
++LL | | a: isize,
++LL | | b: isize,
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc-impl.rs:9:5
++ |
++LL | a: isize,
++ | ^^^^^^^^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc-impl.rs:10:5
++ |
++LL | b: isize,
++ | ^^^^^^^^
++
++error: missing documentation for a struct
++ --> $DIR/missing-doc-impl.rs:13:1
++ |
++LL | / pub struct PubFoo {
++LL | | pub a: isize,
++LL | | b: isize,
++LL | | }
++ | |_^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc-impl.rs:14:5
++ |
++LL | pub a: isize,
++ | ^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc-impl.rs:15:5
++ |
++LL | b: isize,
++ | ^^^^^^^^
++
++error: missing documentation for a trait
++ --> $DIR/missing-doc-impl.rs:38:1
++ |
++LL | / pub trait C {
++LL | | fn foo(&self);
++LL | | fn foo_with_impl(&self) {}
++LL | | }
++ | |_^
++
++error: missing documentation for a trait method
++ --> $DIR/missing-doc-impl.rs:39:5
++ |
++LL | fn foo(&self);
++ | ^^^^^^^^^^^^^^
++
++error: missing documentation for a trait method
++ --> $DIR/missing-doc-impl.rs:40:5
++ |
++LL | fn foo_with_impl(&self) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for an associated type
++ --> $DIR/missing-doc-impl.rs:50:5
++ |
++LL | type AssociatedType;
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for an associated type
++ --> $DIR/missing-doc-impl.rs:51:5
++ |
++LL | type AssociatedTypeDef = Self;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++ --> $DIR/missing-doc-impl.rs:62:5
++ |
++LL | pub fn foo() {}
++ | ^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++ --> $DIR/missing-doc-impl.rs:63:5
++ |
++LL | fn bar() {}
++ | ^^^^^^^^^^^
++
++error: missing documentation for a method
++ --> $DIR/missing-doc-impl.rs:67:5
++ |
++LL | pub fn foo() {}
++ | ^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++ --> $DIR/missing-doc-impl.rs:70:5
++ |
++LL | fn foo2() {}
++ | ^^^^^^^^^^^^
++
++error: aborting due to 15 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_docs_in_private_items)]
++// When denying at the crate level, be sure to not get random warnings from the
++// injected intrinsics by the compiler.
++#![allow(dead_code)]
++#![feature(global_asm)]
++
++//! Some garbage docs for the crate here
++#![doc = "More garbage"]
++
++type Typedef = String;
++pub type PubTypedef = String;
++
++mod module_no_dox {}
++pub mod pub_module_no_dox {}
++
++/// dox
++pub fn foo() {}
++pub fn foo2() {}
++fn foo3() {}
++#[allow(clippy::missing_docs_in_private_items)]
++pub fn foo4() {}
++
++// It sure is nice if doc(hidden) implies allow(missing_docs), and that it
++// applies recursively
++#[doc(hidden)]
++mod a {
++ pub fn baz() {}
++ pub mod b {
++ pub fn baz() {}
++ }
++}
++
++enum Baz {
++ BazA { a: isize, b: isize },
++ BarB,
++}
++
++pub enum PubBaz {
++ PubBazA { a: isize },
++}
++
++/// dox
++pub enum PubBaz2 {
++ /// dox
++ PubBaz2A {
++ /// dox
++ a: isize,
++ },
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub enum PubBaz3 {
++ PubBaz3A { b: isize },
++}
++
++#[doc(hidden)]
++pub fn baz() {}
++
++const FOO: u32 = 0;
++/// dox
++pub const FOO1: u32 = 0;
++#[allow(clippy::missing_docs_in_private_items)]
++pub const FOO2: u32 = 0;
++#[doc(hidden)]
++pub const FOO3: u32 = 0;
++pub const FOO4: u32 = 0;
++
++static BAR: u32 = 0;
++/// dox
++pub static BAR1: u32 = 0;
++#[allow(clippy::missing_docs_in_private_items)]
++pub static BAR2: u32 = 0;
++#[doc(hidden)]
++pub static BAR3: u32 = 0;
++pub static BAR4: u32 = 0;
++
++mod internal_impl {
++ /// dox
++ pub fn documented() {}
++ pub fn undocumented1() {}
++ pub fn undocumented2() {}
++ fn undocumented3() {}
++ /// dox
++ pub mod globbed {
++ /// dox
++ pub fn also_documented() {}
++ pub fn also_undocumented1() {}
++ fn also_undocumented2() {}
++ }
++}
++/// dox
++pub mod public_interface {
++ pub use internal_impl::documented as foo;
++ pub use internal_impl::globbed::*;
++ pub use internal_impl::undocumented1 as bar;
++ pub use internal_impl::{documented, undocumented2};
++}
++
++fn main() {}
++
++// Ensure global asm doesn't require documentation.
++global_asm! { "" }
--- /dev/null
--- /dev/null
++error: missing documentation for a type alias
++ --> $DIR/missing-doc.rs:10:1
++ |
++LL | type Typedef = String;
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: missing documentation for a type alias
++ --> $DIR/missing-doc.rs:11:1
++ |
++LL | pub type PubTypedef = String;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++ --> $DIR/missing-doc.rs:13:1
++ |
++LL | mod module_no_dox {}
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++ --> $DIR/missing-doc.rs:14:1
++ |
++LL | pub mod pub_module_no_dox {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:18:1
++ |
++LL | pub fn foo2() {}
++ | ^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:19:1
++ |
++LL | fn foo3() {}
++ | ^^^^^^^^^^^^
++
++error: missing documentation for an enum
++ --> $DIR/missing-doc.rs:33:1
++ |
++LL | / enum Baz {
++LL | | BazA { a: isize, b: isize },
++LL | | BarB,
++LL | | }
++ | |_^
++
++error: missing documentation for a variant
++ --> $DIR/missing-doc.rs:34:5
++ |
++LL | BazA { a: isize, b: isize },
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc.rs:34:12
++ |
++LL | BazA { a: isize, b: isize },
++ | ^^^^^^^^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc.rs:34:22
++ |
++LL | BazA { a: isize, b: isize },
++ | ^^^^^^^^
++
++error: missing documentation for a variant
++ --> $DIR/missing-doc.rs:35:5
++ |
++LL | BarB,
++ | ^^^^
++
++error: missing documentation for an enum
++ --> $DIR/missing-doc.rs:38:1
++ |
++LL | / pub enum PubBaz {
++LL | | PubBazA { a: isize },
++LL | | }
++ | |_^
++
++error: missing documentation for a variant
++ --> $DIR/missing-doc.rs:39:5
++ |
++LL | PubBazA { a: isize },
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++ --> $DIR/missing-doc.rs:39:15
++ |
++LL | PubBazA { a: isize },
++ | ^^^^^^^^
++
++error: missing documentation for a constant
++ --> $DIR/missing-doc.rs:59:1
++ |
++LL | const FOO: u32 = 0;
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a constant
++ --> $DIR/missing-doc.rs:66:1
++ |
++LL | pub const FOO4: u32 = 0;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a static
++ --> $DIR/missing-doc.rs:68:1
++ |
++LL | static BAR: u32 = 0;
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a static
++ --> $DIR/missing-doc.rs:75:1
++ |
++LL | pub static BAR4: u32 = 0;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++ --> $DIR/missing-doc.rs:77:1
++ |
++LL | / mod internal_impl {
++LL | | /// dox
++LL | | pub fn documented() {}
++LL | | pub fn undocumented1() {}
++... |
++LL | | }
++LL | | }
++ | |_^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:80:5
++ |
++LL | pub fn undocumented1() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:81:5
++ |
++LL | pub fn undocumented2() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:82:5
++ |
++LL | fn undocumented3() {}
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:87:9
++ |
++LL | pub fn also_undocumented1() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++ --> $DIR/missing-doc.rs:88:9
++ |
++LL | fn also_undocumented2() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 24 previous errors
++
--- /dev/null
--- /dev/null
++//! False-positive tests to ensure we don't suggest `const` for things where it would cause a
++//! compilation error.
++//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
++
++#![warn(clippy::missing_const_for_fn)]
++#![allow(incomplete_features)]
++#![feature(start, const_generics)]
++
++struct Game;
++
++// This should not be linted because it's already const
++const fn already_const() -> i32 {
++ 32
++}
++
++impl Game {
++ // This should not be linted because it's already const
++ pub const fn already_const() -> i32 {
++ 32
++ }
++}
++
++// Allowing on this function, because it would lint, which we don't want in this case.
++#[allow(clippy::missing_const_for_fn)]
++fn random() -> u32 {
++ 42
++}
++
++// We should not suggest to make this function `const` because `random()` is non-const
++fn random_caller() -> u32 {
++ random()
++}
++
++static Y: u32 = 0;
++
++// We should not suggest to make this function `const` because const functions are not allowed to
++// refer to a static variable
++fn get_y() -> u32 {
++ Y
++ //~^ ERROR E0013
++}
++
++// Don't lint entrypoint functions
++#[start]
++fn init(num: isize, something: *const *const u8) -> isize {
++ 1
++}
++
++trait Foo {
++ // This should not be suggested to be made const
++ // (rustc doesn't allow const trait methods)
++ fn f() -> u32;
++
++ // This should not be suggested to be made const either
++ fn g() -> u32 {
++ 33
++ }
++}
++
++// Don't lint in external macros (derive)
++#[derive(PartialEq, Eq)]
++struct Point(isize, isize);
++
++impl std::ops::Add for Point {
++ type Output = Self;
++
++ // Don't lint in trait impls of derived methods
++ fn add(self, other: Self) -> Self {
++ Point(self.0 + other.0, self.1 + other.1)
++ }
++}
++
++mod with_drop {
++ pub struct A;
++ pub struct B;
++ impl Drop for A {
++ fn drop(&mut self) {}
++ }
++
++ impl A {
++ // This can not be const because the type implements `Drop`.
++ pub fn a(self) -> B {
++ B
++ }
++ }
++
++ impl B {
++ // This can not be const because `a` implements `Drop`.
++ pub fn a(self, a: A) -> B {
++ B
++ }
++ }
++}
++
++fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] {
++ t
++}
++
++fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
++ let p = t.as_ptr() as *const [T; N];
++
++ unsafe { &*p }
++}
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_const_for_fn)]
++#![allow(incomplete_features, clippy::let_and_return)]
++#![feature(const_generics)]
++
++use std::mem::transmute;
++
++struct Game {
++ guess: i32,
++}
++
++impl Game {
++ // Could be const
++ pub fn new() -> Self {
++ Self { guess: 42 }
++ }
++
++ fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
++ b
++ }
++}
++
++// Could be const
++fn one() -> i32 {
++ 1
++}
++
++// Could also be const
++fn two() -> i32 {
++ let abc = 2;
++ abc
++}
++
++// Could be const (since Rust 1.39)
++fn string() -> String {
++ String::new()
++}
++
++// Could be const
++unsafe fn four() -> i32 {
++ 4
++}
++
++// Could also be const
++fn generic<T>(t: T) -> T {
++ t
++}
++
++fn sub(x: u32) -> usize {
++ unsafe { transmute(&x) }
++}
++
++// NOTE: This is currently not yet allowed to be const
++// Once implemented, Clippy should be able to suggest this as const, too.
++fn generic_arr<T: Copy>(t: [T; 1]) -> T {
++ t[0]
++}
++
++mod with_drop {
++ pub struct A;
++ pub struct B;
++ impl Drop for A {
++ fn drop(&mut self) {}
++ }
++
++ impl B {
++ // This can be const, because `a` is passed by reference
++ pub fn b(self, a: &A) -> B {
++ B
++ }
++ }
++}
++
++// Should not be const
++fn main() {}
--- /dev/null
--- /dev/null
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:13:5
++ |
++LL | / pub fn new() -> Self {
++LL | | Self { guess: 42 }
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:17:5
++ |
++LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
++LL | | b
++LL | | }
++ | |_____^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:23:1
++ |
++LL | / fn one() -> i32 {
++LL | | 1
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:28:1
++ |
++LL | / fn two() -> i32 {
++LL | | let abc = 2;
++LL | | abc
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:34:1
++ |
++LL | / fn string() -> String {
++LL | | String::new()
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:39:1
++ |
++LL | / unsafe fn four() -> i32 {
++LL | | 4
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:44:1
++ |
++LL | / fn generic<T>(t: T) -> T {
++LL | | t
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:48:1
++ |
++LL | / fn sub(x: u32) -> usize {
++LL | | unsafe { transmute(&x) }
++LL | | }
++ | |_^
++
++error: this could be a `const fn`
++ --> $DIR/could_be_const.rs:67:9
++ |
++LL | / pub fn b(self, a: &A) -> B {
++LL | | B
++LL | | }
++ | |_________^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::missing_inline_in_public_items)]
++#![crate_type = "dylib"]
++// When denying at the crate level, be sure to not get random warnings from the
++// injected intrinsics by the compiler.
++#![allow(dead_code, non_snake_case)]
++
++type Typedef = String;
++pub type PubTypedef = String;
++
++struct Foo {} // ok
++pub struct PubFoo {} // ok
++enum FooE {} // ok
++pub enum PubFooE {} // ok
++
++mod module {} // ok
++pub mod pub_module {} // ok
++
++fn foo() {}
++pub fn pub_foo() {} // missing #[inline]
++#[inline]
++pub fn pub_foo_inline() {} // ok
++#[inline(always)]
++pub fn pub_foo_inline_always() {} // ok
++
++#[allow(clippy::missing_inline_in_public_items)]
++pub fn pub_foo_no_inline() {}
++
++trait Bar {
++ fn Bar_a(); // ok
++ fn Bar_b() {} // ok
++}
++
++pub trait PubBar {
++ fn PubBar_a(); // ok
++ fn PubBar_b() {} // missing #[inline]
++ #[inline]
++ fn PubBar_c() {} // ok
++}
++
++// none of these need inline because Foo is not exported
++impl PubBar for Foo {
++ fn PubBar_a() {} // ok
++ fn PubBar_b() {} // ok
++ fn PubBar_c() {} // ok
++}
++
++// all of these need inline because PubFoo is exported
++impl PubBar for PubFoo {
++ fn PubBar_a() {} // missing #[inline]
++ fn PubBar_b() {} // missing #[inline]
++ fn PubBar_c() {} // missing #[inline]
++}
++
++// do not need inline because Foo is not exported
++impl Foo {
++ fn FooImpl() {} // ok
++}
++
++// need inline because PubFoo is exported
++impl PubFoo {
++ pub fn PubFooImpl() {} // missing #[inline]
++}
++
++// do not lint this since users cannot control the external code
++#[derive(Debug)]
++pub struct S {}
--- /dev/null
--- /dev/null
++error: missing `#[inline]` for a function
++ --> $DIR/missing_inline.rs:19:1
++ |
++LL | pub fn pub_foo() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings`
++
++error: missing `#[inline]` for a default trait method
++ --> $DIR/missing_inline.rs:35:5
++ |
++LL | fn PubBar_b() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++ --> $DIR/missing_inline.rs:49:5
++ |
++LL | fn PubBar_a() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++ --> $DIR/missing_inline.rs:50:5
++ |
++LL | fn PubBar_b() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++ --> $DIR/missing_inline.rs:51:5
++ |
++LL | fn PubBar_c() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++ --> $DIR/missing_inline.rs:61:5
++ |
++LL | pub fn PubFooImpl() {} // missing #[inline]
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables, clippy::excessive_precision)]
++
++fn main() {
++ let fail14 = 2_i32;
++ let fail15 = 4_i64;
++ let fail16 = 7_i8; //
++ let fail17 = 23_i16; //
++ let ok18 = 23_128;
++
++ let fail20 = 2_i8; //
++ let fail21 = 4_i16; //
++
++ let fail24 = 12.34_f64;
++ let fail25 = 1E2_f32;
++ let fail26 = 43E7_f64;
++ let fail27 = 243E17_f32;
++ #[allow(overflowing_literals)]
++ let fail28 = 241_251_235E723_f64;
++ let fail29 = 42_279.911_f32;
++
++ let _ = 1.123_45E1_f32;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables, clippy::excessive_precision)]
++
++fn main() {
++ let fail14 = 2_32;
++ let fail15 = 4_64;
++ let fail16 = 7_8; //
++ let fail17 = 23_16; //
++ let ok18 = 23_128;
++
++ let fail20 = 2__8; //
++ let fail21 = 4___16; //
++
++ let fail24 = 12.34_64;
++ let fail25 = 1E2_32;
++ let fail26 = 43E7_64;
++ let fail27 = 243E17_32;
++ #[allow(overflowing_literals)]
++ let fail28 = 241251235E723_64;
++ let fail29 = 42279.911_32;
++
++ let _ = 1.12345E1_32;
++}
--- /dev/null
--- /dev/null
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:6:18
++ |
++LL | let fail14 = 2_32;
++ | ^^^^ help: did you mean to write: `2_i32`
++ |
++ = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:7:18
++ |
++LL | let fail15 = 4_64;
++ | ^^^^ help: did you mean to write: `4_i64`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:8:18
++ |
++LL | let fail16 = 7_8; //
++ | ^^^ help: did you mean to write: `7_i8`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:9:18
++ |
++LL | let fail17 = 23_16; //
++ | ^^^^^ help: did you mean to write: `23_i16`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:12:18
++ |
++LL | let fail20 = 2__8; //
++ | ^^^^ help: did you mean to write: `2_i8`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:13:18
++ |
++LL | let fail21 = 4___16; //
++ | ^^^^^^ help: did you mean to write: `4_i16`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:15:18
++ |
++LL | let fail24 = 12.34_64;
++ | ^^^^^^^^ help: did you mean to write: `12.34_f64`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:16:18
++ |
++LL | let fail25 = 1E2_32;
++ | ^^^^^^ help: did you mean to write: `1E2_f32`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:17:18
++ |
++LL | let fail26 = 43E7_64;
++ | ^^^^^^^ help: did you mean to write: `43E7_f64`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:18:18
++ |
++LL | let fail27 = 243E17_32;
++ | ^^^^^^^^^ help: did you mean to write: `243E17_f32`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:20:18
++ |
++LL | let fail28 = 241251235E723_64;
++ | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:21:18
++ |
++LL | let fail29 = 42279.911_32;
++ | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32`
++
++error: mistyped literal suffix
++ --> $DIR/mistyped_literal_suffix.rs:23:13
++ |
++LL | let _ = 1.12345E1_32;
++ | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::module_inception)]
++
++mod foo {
++ mod bar {
++ mod bar {
++ mod foo {}
++ }
++ mod foo {}
++ }
++ mod foo {
++ mod bar {}
++ }
++}
++
++// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>.
++mod bar {
++ #[allow(clippy::module_inception)]
++ mod bar {}
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: module has the same name as its containing module
++ --> $DIR/module_inception.rs:5:9
++ |
++LL | / mod bar {
++LL | | mod foo {}
++LL | | }
++ | |_________^
++ |
++ = note: `-D clippy::module-inception` implied by `-D warnings`
++
++error: module has the same name as its containing module
++ --> $DIR/module_inception.rs:10:5
++ |
++LL | / mod foo {
++LL | | mod bar {}
++LL | | }
++ | |_____^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// compile-flags: --test
++
++#![warn(clippy::module_name_repetitions)]
++#![allow(dead_code)]
++
++mod foo {
++ pub fn foo() {}
++ pub fn foo_bar() {}
++ pub fn bar_foo() {}
++ pub struct FooCake {}
++ pub enum CakeFoo {}
++ pub struct Foo7Bar;
++
++ // Should not warn
++ pub struct Foobar;
++}
++
++#[cfg(test)]
++mod test {
++ #[test]
++ fn it_works() {
++ assert_eq!(2 + 2, 4);
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: item name starts with its containing module's name
++ --> $DIR/module_name_repetitions.rs:8:5
++ |
++LL | pub fn foo_bar() {}
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::module-name-repetitions` implied by `-D warnings`
++
++error: item name ends with its containing module's name
++ --> $DIR/module_name_repetitions.rs:9:5
++ |
++LL | pub fn bar_foo() {}
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: item name starts with its containing module's name
++ --> $DIR/module_name_repetitions.rs:10:5
++ |
++LL | pub struct FooCake {}
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: item name ends with its containing module's name
++ --> $DIR/module_name_repetitions.rs:11:5
++ |
++LL | pub enum CakeFoo {}
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: item name starts with its containing module's name
++ --> $DIR/module_name_repetitions.rs:12:5
++ |
++LL | pub struct Foo7Bar;
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++ unused,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::modulo_one
++)]
++
++fn main() {
++ // Lint when both sides are const and of the opposite sign
++ -1.6 % 2.1;
++ 1.6 % -2.1;
++ (1.1 - 2.3) % (1.1 + 2.3);
++ (1.1 + 2.3) % (1.1 - 2.3);
++
++ // Lint on floating point numbers
++ let a_f32: f32 = -1.6;
++ let mut b_f32: f32 = 2.1;
++ a_f32 % b_f32;
++ b_f32 % a_f32;
++ b_f32 %= a_f32;
++
++ let a_f64: f64 = -1.6;
++ let mut b_f64: f64 = 2.1;
++ a_f64 % b_f64;
++ b_f64 % a_f64;
++ b_f64 %= a_f64;
++
++ // No lint when both sides are const and of the same sign
++ 1.6 % 2.1;
++ -1.6 % -2.1;
++ (1.1 + 2.3) % (-1.1 + 2.3);
++ (-1.1 - 2.3) % (1.1 - 2.3);
++}
--- /dev/null
--- /dev/null
++error: you are using modulo operator on constants with different signs: `-1.600 % 2.100`
++ --> $DIR/modulo_arithmetic_float.rs:13:5
++ |
++LL | -1.6 % 2.1;
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `1.600 % -2.100`
++ --> $DIR/modulo_arithmetic_float.rs:14:5
++ |
++LL | 1.6 % -2.1;
++ | ^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `-1.200 % 3.400`
++ --> $DIR/modulo_arithmetic_float.rs:15:5
++ |
++LL | (1.1 - 2.3) % (1.1 + 2.3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `3.400 % -1.200`
++ --> $DIR/modulo_arithmetic_float.rs:16:5
++ |
++LL | (1.1 + 2.3) % (1.1 - 2.3);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:21:5
++ |
++LL | a_f32 % b_f32;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:22:5
++ |
++LL | b_f32 % a_f32;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:23:5
++ |
++LL | b_f32 %= a_f32;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:27:5
++ |
++LL | a_f64 % b_f64;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:28:5
++ |
++LL | b_f64 % a_f64;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_float.rs:29:5
++ |
++LL | b_f64 %= a_f64;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++ unused,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::modulo_one
++)]
++
++fn main() {
++ // Lint on signed integral numbers
++ let a = -1;
++ let mut b = 2;
++ a % b;
++ b % a;
++ b %= a;
++
++ let a_i8: i8 = 1;
++ let mut b_i8: i8 = 2;
++ a_i8 % b_i8;
++ b_i8 %= a_i8;
++
++ let a_i16: i16 = 1;
++ let mut b_i16: i16 = 2;
++ a_i16 % b_i16;
++ b_i16 %= a_i16;
++
++ let a_i32: i32 = 1;
++ let mut b_i32: i32 = 2;
++ a_i32 % b_i32;
++ b_i32 %= a_i32;
++
++ let a_i64: i64 = 1;
++ let mut b_i64: i64 = 2;
++ a_i64 % b_i64;
++ b_i64 %= a_i64;
++
++ let a_i128: i128 = 1;
++ let mut b_i128: i128 = 2;
++ a_i128 % b_i128;
++ b_i128 %= a_i128;
++
++ let a_isize: isize = 1;
++ let mut b_isize: isize = 2;
++ a_isize % b_isize;
++ b_isize %= a_isize;
++
++ let a = 1;
++ let mut b = 2;
++ a % b;
++ b %= a;
++
++ // No lint on unsigned integral value
++ let a_u8: u8 = 17;
++ let b_u8: u8 = 3;
++ a_u8 % b_u8;
++ let mut a_u8: u8 = 1;
++ a_u8 %= 2;
++
++ let a_u16: u16 = 17;
++ let b_u16: u16 = 3;
++ a_u16 % b_u16;
++ let mut a_u16: u16 = 1;
++ a_u16 %= 2;
++
++ let a_u32: u32 = 17;
++ let b_u32: u32 = 3;
++ a_u32 % b_u32;
++ let mut a_u32: u32 = 1;
++ a_u32 %= 2;
++
++ let a_u64: u64 = 17;
++ let b_u64: u64 = 3;
++ a_u64 % b_u64;
++ let mut a_u64: u64 = 1;
++ a_u64 %= 2;
++
++ let a_u128: u128 = 17;
++ let b_u128: u128 = 3;
++ a_u128 % b_u128;
++ let mut a_u128: u128 = 1;
++ a_u128 %= 2;
++
++ let a_usize: usize = 17;
++ let b_usize: usize = 3;
++ a_usize % b_usize;
++ let mut a_usize: usize = 1;
++ a_usize %= 2;
++}
--- /dev/null
--- /dev/null
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:15:5
++ |
++LL | a % b;
++ | ^^^^^
++ |
++ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:16:5
++ |
++LL | b % a;
++ | ^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:17:5
++ |
++LL | b %= a;
++ | ^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:21:5
++ |
++LL | a_i8 % b_i8;
++ | ^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:22:5
++ |
++LL | b_i8 %= a_i8;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:26:5
++ |
++LL | a_i16 % b_i16;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:27:5
++ |
++LL | b_i16 %= a_i16;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:31:5
++ |
++LL | a_i32 % b_i32;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:32:5
++ |
++LL | b_i32 %= a_i32;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:36:5
++ |
++LL | a_i64 % b_i64;
++ | ^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:37:5
++ |
++LL | b_i64 %= a_i64;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:41:5
++ |
++LL | a_i128 % b_i128;
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:42:5
++ |
++LL | b_i128 %= a_i128;
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:46:5
++ |
++LL | a_isize % b_isize;
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:47:5
++ |
++LL | b_isize %= a_isize;
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:51:5
++ |
++LL | a % b;
++ | ^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++ --> $DIR/modulo_arithmetic_integral.rs:52:5
++ |
++LL | b %= a;
++ | ^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++ unused,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated,
++ clippy::no_effect,
++ clippy::unnecessary_operation,
++ clippy::modulo_one
++)]
++
++fn main() {
++ // Lint when both sides are const and of the opposite sign
++ -1 % 2;
++ 1 % -2;
++ (1 - 2) % (1 + 2);
++ (1 + 2) % (1 - 2);
++ 35 * (7 - 4 * 2) % (-500 * -600);
++
++ -1i8 % 2i8;
++ 1i8 % -2i8;
++ -1i16 % 2i16;
++ 1i16 % -2i16;
++ -1i32 % 2i32;
++ 1i32 % -2i32;
++ -1i64 % 2i64;
++ 1i64 % -2i64;
++ -1i128 % 2i128;
++ 1i128 % -2i128;
++ -1isize % 2isize;
++ 1isize % -2isize;
++
++ // No lint when both sides are const and of the same sign
++ 1 % 2;
++ -1 % -2;
++ (1 + 2) % (-1 + 2);
++ (-1 - 2) % (1 - 2);
++
++ 1u8 % 2u8;
++ 1u16 % 2u16;
++ 1u32 % 2u32;
++ 1u64 % 2u64;
++ 1u128 % 2u128;
++ 1usize % 2usize;
++}
--- /dev/null
--- /dev/null
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:13:5
++ |
++LL | -1 % 2;
++ | ^^^^^^
++ |
++ = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:14:5
++ |
++LL | 1 % -2;
++ | ^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 3`
++ --> $DIR/modulo_arithmetic_integral_const.rs:15:5
++ |
++LL | (1 - 2) % (1 + 2);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `3 % -1`
++ --> $DIR/modulo_arithmetic_integral_const.rs:16:5
++ |
++LL | (1 + 2) % (1 - 2);
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-35 % 300000`
++ --> $DIR/modulo_arithmetic_integral_const.rs:17:5
++ |
++LL | 35 * (7 - 4 * 2) % (-500 * -600);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:19:5
++ |
++LL | -1i8 % 2i8;
++ | ^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:20:5
++ |
++LL | 1i8 % -2i8;
++ | ^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:21:5
++ |
++LL | -1i16 % 2i16;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:22:5
++ |
++LL | 1i16 % -2i16;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:23:5
++ |
++LL | -1i32 % 2i32;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:24:5
++ |
++LL | 1i32 % -2i32;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:25:5
++ |
++LL | -1i64 % 2i64;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:26:5
++ |
++LL | 1i64 % -2i64;
++ | ^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:27:5
++ |
++LL | -1i128 % 2i128;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:28:5
++ |
++LL | 1i128 % -2i128;
++ | ^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:29:5
++ |
++LL | -1isize % 2isize;
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++ --> $DIR/modulo_arithmetic_integral_const.rs:30:5
++ |
++LL | 1isize % -2isize;
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: double check for expected result especially when interoperating with different languages
++ = note: or consider using `rem_euclid` or similar function
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::modulo_one)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++static STATIC_ONE: usize = 2 - 1;
++
++fn main() {
++ 10 % 1;
++ 10 % 2;
++
++ const ONE: u32 = 1 * 1;
++
++ 2 % ONE;
++ 5 % STATIC_ONE;
++}
--- /dev/null
--- /dev/null
++error: any number modulo 1 will be 0
++ --> $DIR/modulo_one.rs:7:5
++ |
++LL | 10 % 1;
++ | ^^^^^^
++ |
++ = note: `-D clippy::modulo-one` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `1`
++ --> $DIR/modulo_one.rs:10:22
++ |
++LL | const ONE: u32 = 1 * 1;
++ | ^^^^^
++ |
++ = note: `-D clippy::identity-op` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `1`
++ --> $DIR/modulo_one.rs:10:22
++ |
++LL | const ONE: u32 = 1 * 1;
++ | ^^^^^
++
++error: any number modulo 1 will be 0
++ --> $DIR/modulo_one.rs:12:5
++ |
++LL | 2 % ONE;
++ | ^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(never_type)]
++#![allow(unused_mut, clippy::redundant_allocation)]
++#![warn(clippy::must_use_candidate)]
++use std::rc::Rc;
++use std::sync::atomic::{AtomicBool, Ordering};
++use std::sync::Arc;
++
++pub struct MyAtomic(AtomicBool);
++pub struct MyPure;
++
++#[must_use] pub fn pure(i: u8) -> u8 {
++ i
++}
++
++impl MyPure {
++ #[must_use] pub fn inherent_pure(&self) -> u8 {
++ 0
++ }
++}
++
++pub trait MyPureTrait {
++ fn trait_pure(&self, i: u32) -> u32 {
++ self.trait_impl_pure(i) + 1
++ }
++
++ fn trait_impl_pure(&self, i: u32) -> u32;
++}
++
++impl MyPureTrait for MyPure {
++ fn trait_impl_pure(&self, i: u32) -> u32 {
++ i
++ }
++}
++
++pub fn without_result() {
++ // OK
++}
++
++pub fn impure_primitive(i: &mut u8) -> u8 {
++ *i
++}
++
++pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
++ f(0)
++}
++
++#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++ true
++}
++
++pub fn quoth_the_raven(_more: !) -> u32 {
++ unimplemented!();
++}
++
++pub fn atomics(b: &AtomicBool) -> bool {
++ b.load(Ordering::SeqCst)
++}
++
++#[must_use] pub fn rcd(_x: Rc<u32>) -> bool {
++ true
++}
++
++pub fn rcmut(_x: Rc<&mut u32>) -> bool {
++ true
++}
++
++#[must_use] pub fn arcd(_x: Arc<u32>) -> bool {
++ false
++}
++
++pub fn inner_types(_m: &MyAtomic) -> bool {
++ true
++}
++
++static mut COUNTER: usize = 0;
++
++/// # Safety
++///
++/// Don't ever call this from multiple threads
++pub unsafe fn mutates_static() -> usize {
++ COUNTER += 1;
++ COUNTER
++}
++
++#[no_mangle]
++pub fn unmangled(i: bool) -> bool {
++ !i
++}
++
++fn main() {
++ assert_eq!(1, pure(1));
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(never_type)]
++#![allow(unused_mut, clippy::redundant_allocation)]
++#![warn(clippy::must_use_candidate)]
++use std::rc::Rc;
++use std::sync::atomic::{AtomicBool, Ordering};
++use std::sync::Arc;
++
++pub struct MyAtomic(AtomicBool);
++pub struct MyPure;
++
++pub fn pure(i: u8) -> u8 {
++ i
++}
++
++impl MyPure {
++ pub fn inherent_pure(&self) -> u8 {
++ 0
++ }
++}
++
++pub trait MyPureTrait {
++ fn trait_pure(&self, i: u32) -> u32 {
++ self.trait_impl_pure(i) + 1
++ }
++
++ fn trait_impl_pure(&self, i: u32) -> u32;
++}
++
++impl MyPureTrait for MyPure {
++ fn trait_impl_pure(&self, i: u32) -> u32 {
++ i
++ }
++}
++
++pub fn without_result() {
++ // OK
++}
++
++pub fn impure_primitive(i: &mut u8) -> u8 {
++ *i
++}
++
++pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
++ f(0)
++}
++
++pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++ true
++}
++
++pub fn quoth_the_raven(_more: !) -> u32 {
++ unimplemented!();
++}
++
++pub fn atomics(b: &AtomicBool) -> bool {
++ b.load(Ordering::SeqCst)
++}
++
++pub fn rcd(_x: Rc<u32>) -> bool {
++ true
++}
++
++pub fn rcmut(_x: Rc<&mut u32>) -> bool {
++ true
++}
++
++pub fn arcd(_x: Arc<u32>) -> bool {
++ false
++}
++
++pub fn inner_types(_m: &MyAtomic) -> bool {
++ true
++}
++
++static mut COUNTER: usize = 0;
++
++/// # Safety
++///
++/// Don't ever call this from multiple threads
++pub unsafe fn mutates_static() -> usize {
++ COUNTER += 1;
++ COUNTER
++}
++
++#[no_mangle]
++pub fn unmangled(i: bool) -> bool {
++ !i
++}
++
++fn main() {
++ assert_eq!(1, pure(1));
++}
--- /dev/null
--- /dev/null
++error: this function could have a `#[must_use]` attribute
++ --> $DIR/must_use_candidates.rs:12:1
++ |
++LL | pub fn pure(i: u8) -> u8 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8`
++ |
++ = note: `-D clippy::must-use-candidate` implied by `-D warnings`
++
++error: this method could have a `#[must_use]` attribute
++ --> $DIR/must_use_candidates.rs:17:5
++ |
++LL | pub fn inherent_pure(&self) -> u8 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8`
++
++error: this function could have a `#[must_use]` attribute
++ --> $DIR/must_use_candidates.rs:48:1
++ |
++LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool`
++
++error: this function could have a `#[must_use]` attribute
++ --> $DIR/must_use_candidates.rs:60:1
++ |
++LL | pub fn rcd(_x: Rc<u32>) -> bool {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool`
++
++error: this function could have a `#[must_use]` attribute
++ --> $DIR/must_use_candidates.rs:68:1
++ |
++LL | pub fn arcd(_x: Arc<u32>) -> bool {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++//run-rustfix
++// aux-build:macro_rules.rs
++
++#![warn(clippy::must_use_unit)]
++#![allow(clippy::unused_unit)]
++
++#[macro_use]
++extern crate macro_rules;
++
++
++pub fn must_use_default() {}
++
++
++pub fn must_use_unit() -> () {}
++
++
++pub fn must_use_with_note() {}
++
++fn main() {
++ must_use_default();
++ must_use_unit();
++ must_use_with_note();
++
++ // We should not lint in external macros
++ must_use_unit!();
++}
--- /dev/null
--- /dev/null
++//run-rustfix
++// aux-build:macro_rules.rs
++
++#![warn(clippy::must_use_unit)]
++#![allow(clippy::unused_unit)]
++
++#[macro_use]
++extern crate macro_rules;
++
++#[must_use]
++pub fn must_use_default() {}
++
++#[must_use]
++pub fn must_use_unit() -> () {}
++
++#[must_use = "With note"]
++pub fn must_use_with_note() {}
++
++fn main() {
++ must_use_default();
++ must_use_unit();
++ must_use_with_note();
++
++ // We should not lint in external macros
++ must_use_unit!();
++}
--- /dev/null
--- /dev/null
++error: this unit-returning function has a `#[must_use]` attribute
++ --> $DIR/must_use_unit.rs:11:1
++ |
++LL | #[must_use]
++ | ----------- help: remove the attribute
++LL | pub fn must_use_default() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::must-use-unit` implied by `-D warnings`
++
++error: this unit-returning function has a `#[must_use]` attribute
++ --> $DIR/must_use_unit.rs:14:1
++ |
++LL | #[must_use]
++ | ----------- help: remove the attribute
++LL | pub fn must_use_unit() -> () {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this unit-returning function has a `#[must_use]` attribute
++ --> $DIR/must_use_unit.rs:17:1
++ |
++LL | #[must_use = "With note"]
++ | ------------------------- help: remove the attribute
++LL | pub fn must_use_with_note() {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++#![warn(clippy::mut_from_ref)]
++
++struct Foo;
++
++impl Foo {
++ fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++ unimplemented!()
++ }
++}
++
++trait Ouch {
++ fn ouch(x: &Foo) -> &mut Foo;
++}
++
++impl Ouch for Foo {
++ fn ouch(x: &Foo) -> &mut Foo {
++ unimplemented!()
++ }
++}
++
++fn fail(x: &u32) -> &mut u16 {
++ unimplemented!()
++}
++
++fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++ unimplemented!()
++}
++
++fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++ unimplemented!()
++}
++
++// this is OK, because the result borrows y
++fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
++ unimplemented!()
++}
++
++// this is also OK, because the result could borrow y
++fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
++ unimplemented!()
++}
++
++fn main() {
++ //TODO
++}
--- /dev/null
--- /dev/null
++error: mutable borrow from immutable input(s)
++ --> $DIR/mut_from_ref.rs:7:39
++ |
++LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++ | ^^^^^^^^
++ |
++ = note: `-D clippy::mut-from-ref` implied by `-D warnings`
++note: immutable borrow here
++ --> $DIR/mut_from_ref.rs:7:29
++ |
++LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++ | ^^^^^
++
++error: mutable borrow from immutable input(s)
++ --> $DIR/mut_from_ref.rs:13:25
++ |
++LL | fn ouch(x: &Foo) -> &mut Foo;
++ | ^^^^^^^^
++ |
++note: immutable borrow here
++ --> $DIR/mut_from_ref.rs:13:16
++ |
++LL | fn ouch(x: &Foo) -> &mut Foo;
++ | ^^^^
++
++error: mutable borrow from immutable input(s)
++ --> $DIR/mut_from_ref.rs:22:21
++ |
++LL | fn fail(x: &u32) -> &mut u16 {
++ | ^^^^^^^^
++ |
++note: immutable borrow here
++ --> $DIR/mut_from_ref.rs:22:12
++ |
++LL | fn fail(x: &u32) -> &mut u16 {
++ | ^^^^
++
++error: mutable borrow from immutable input(s)
++ --> $DIR/mut_from_ref.rs:26:50
++ |
++LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++ | ^^^^^^^^^^^
++ |
++note: immutable borrow here
++ --> $DIR/mut_from_ref.rs:26:25
++ |
++LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++ | ^^^^^^^
++
++error: mutable borrow from immutable input(s)
++ --> $DIR/mut_from_ref.rs:30:67
++ |
++LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++ | ^^^^^^^^^^^
++ |
++note: immutable borrow here
++ --> $DIR/mut_from_ref.rs:30:27
++ |
++LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++ | ^^^^^^^ ^^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++use std::collections::{HashMap, HashSet};
++use std::hash::{Hash, Hasher};
++use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
++
++struct Key(AtomicUsize);
++
++impl Clone for Key {
++ fn clone(&self) -> Self {
++ Key(AtomicUsize::new(self.0.load(Relaxed)))
++ }
++}
++
++impl PartialEq for Key {
++ fn eq(&self, other: &Self) -> bool {
++ self.0.load(Relaxed) == other.0.load(Relaxed)
++ }
++}
++
++impl Eq for Key {}
++
++impl Hash for Key {
++ fn hash<H: Hasher>(&self, h: &mut H) {
++ self.0.load(Relaxed).hash(h);
++ }
++}
++
++fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
++ let _other: HashMap<Key, bool> = HashMap::new();
++ m.keys().cloned().collect()
++}
++
++fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
++
++#[allow(unused)]
++trait Trait {
++ type AssociatedType;
++
++ fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
++}
++
++fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
++ // nothing to see here, move along
++}
++
++fn tuples<U>(_m: &mut HashMap<((), U), ()>) {}
++
++fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
++
++fn main() {
++ let _ = should_not_take_this_arg(&mut HashMap::new(), 1);
++ this_is_ok(&mut HashMap::new());
++ tuples::<Key>(&mut HashMap::new());
++ tuples::<()>(&mut HashMap::new());
++ tuples_bad::<()>(&mut HashMap::new());
++}
--- /dev/null
--- /dev/null
++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: `#[deny(clippy::mutable_key_type)]` on by default
++
++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
++
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
++#![warn(clippy::mut_mut)]
++
++fn fun(x: &mut &mut u32) -> bool {
++ **x > 0
++}
++
++fn less_fun(x: *mut *mut u32) {
++ let y = x;
++}
++
++macro_rules! mut_ptr {
++ ($p:expr) => {
++ &mut $p
++ };
++}
++
++#[allow(unused_mut, unused_variables)]
++fn main() {
++ let mut x = &mut &mut 1u32;
++ {
++ let mut y = &mut x;
++ }
++
++ if fun(x) {
++ let y: &mut &mut u32 = &mut &mut 2;
++ **y + **x;
++ }
++
++ if fun(x) {
++ let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++ ***y + **x;
++ }
++
++ let mut z = mut_ptr!(&mut 3u32);
++}
++
++fn issue939() {
++ let array = [5, 6, 7, 8, 9];
++ let mut args = array.iter().skip(2);
++ for &arg in &mut args {
++ println!("{}", arg);
++ }
++
++ let args = &mut args;
++ for arg in args {
++ println!(":{}", arg);
++ }
++}
--- /dev/null
--- /dev/null
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:4:11
++ |
++LL | fn fun(x: &mut &mut u32) -> bool {
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::mut-mut` implied by `-D warnings`
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:20:17
++ |
++LL | let mut x = &mut &mut 1u32;
++ | ^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:14:9
++ |
++LL | &mut $p
++ | ^^^^^^^
++...
++LL | let mut z = mut_ptr!(&mut 3u32);
++ | ------------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this expression mutably borrows a mutable reference. Consider reborrowing
++ --> $DIR/mut_mut.rs:22:21
++ |
++LL | let mut y = &mut x;
++ | ^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:26:32
++ |
++LL | let y: &mut &mut u32 = &mut &mut 2;
++ | ^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:26:16
++ |
++LL | let y: &mut &mut u32 = &mut &mut 2;
++ | ^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:31:37
++ |
++LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++ | ^^^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:31:16
++ |
++LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++ --> $DIR/mut_mut.rs:31:21
++ |
++LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++
++fn main() {
++ mut_range_bound_upper();
++ mut_range_bound_lower();
++ mut_range_bound_both();
++ mut_range_bound_no_mutation();
++ immut_range_bound();
++ mut_borrow_range_bound();
++ immut_borrow_range_bound();
++}
++
++fn mut_range_bound_upper() {
++ let mut m = 4;
++ for i in 0..m {
++ m = 5;
++ } // warning
++}
++
++fn mut_range_bound_lower() {
++ let mut m = 4;
++ for i in m..10 {
++ m *= 2;
++ } // warning
++}
++
++fn mut_range_bound_both() {
++ let mut m = 4;
++ let mut n = 6;
++ for i in m..n {
++ m = 5;
++ n = 7;
++ } // warning (1 for each mutated bound)
++}
++
++fn mut_range_bound_no_mutation() {
++ let mut m = 4;
++ for i in 0..m {
++ continue;
++ } // no warning
++}
++
++fn mut_borrow_range_bound() {
++ let mut m = 4;
++ for i in 0..m {
++ let n = &mut m; // warning
++ *n += 1;
++ }
++}
++
++fn immut_borrow_range_bound() {
++ let mut m = 4;
++ for i in 0..m {
++ let n = &m; // should be no warning?
++ }
++}
++
++fn immut_range_bound() {
++ let m = 4;
++ for i in 0..m {
++ continue;
++ } // no warning
++}
--- /dev/null
--- /dev/null
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++ --> $DIR/mut_range_bound.rs:16:9
++ |
++LL | m = 5;
++ | ^
++ |
++ = note: `-D clippy::mut-range-bound` implied by `-D warnings`
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++ --> $DIR/mut_range_bound.rs:23:9
++ |
++LL | m *= 2;
++ | ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++ --> $DIR/mut_range_bound.rs:31:9
++ |
++LL | m = 5;
++ | ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++ --> $DIR/mut_range_bound.rs:32:9
++ |
++LL | n = 7;
++ | ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++ --> $DIR/mut_range_bound.rs:46:22
++ |
++LL | let n = &mut m; // warning
++ | ^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused_variables)]
++
++fn takes_an_immutable_reference(a: &i32) {}
++fn takes_a_mutable_reference(a: &mut i32) {}
++
++struct MyStruct;
++
++impl MyStruct {
++ fn takes_an_immutable_reference(&self, a: &i32) {}
++
++ fn takes_a_mutable_reference(&self, a: &mut i32) {}
++}
++
++#[warn(clippy::unnecessary_mut_passed)]
++fn main() {
++ // Functions
++ takes_an_immutable_reference(&mut 42);
++ let as_ptr: fn(&i32) = takes_an_immutable_reference;
++ as_ptr(&mut 42);
++
++ // Methods
++ let my_struct = MyStruct;
++ my_struct.takes_an_immutable_reference(&mut 42);
++
++ // No error
++
++ // Functions
++ takes_an_immutable_reference(&42);
++ let as_ptr: fn(&i32) = takes_an_immutable_reference;
++ as_ptr(&42);
++
++ takes_a_mutable_reference(&mut 42);
++ let as_ptr: fn(&mut i32) = takes_a_mutable_reference;
++ as_ptr(&mut 42);
++
++ let a = &mut 42;
++ takes_an_immutable_reference(a);
++
++ // Methods
++ my_struct.takes_an_immutable_reference(&42);
++ my_struct.takes_a_mutable_reference(&mut 42);
++ my_struct.takes_an_immutable_reference(a);
++}
--- /dev/null
--- /dev/null
++error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
++ --> $DIR/mut_reference.rs:17:34
++ |
++LL | takes_an_immutable_reference(&mut 42);
++ | ^^^^^^^
++ |
++ = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
++
++error: The function/method `as_ptr` doesn't need a mutable reference
++ --> $DIR/mut_reference.rs:19:12
++ |
++LL | as_ptr(&mut 42);
++ | ^^^^^^^
++
++error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
++ --> $DIR/mut_reference.rs:23:44
++ |
++LL | my_struct.takes_an_immutable_reference(&mut 42);
++ | ^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![warn(clippy::mutex_integer)]
++
++fn main() {
++ use std::sync::Mutex;
++ Mutex::new(true);
++ Mutex::new(5usize);
++ Mutex::new(9isize);
++ let mut x = 4u32;
++ Mutex::new(&x as *const u32);
++ Mutex::new(&mut x as *mut u32);
++ Mutex::new(0u32);
++ Mutex::new(0i32);
++ Mutex::new(0f32); // there are no float atomics, so this should not lint
++}
--- /dev/null
--- /dev/null
++error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:6:5
++ |
++LL | Mutex::new(true);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::mutex-atomic` implied by `-D warnings`
++
++error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:7:5
++ |
++LL | Mutex::new(5usize);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:8:5
++ |
++LL | Mutex::new(9isize);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:10:5
++ |
++LL | Mutex::new(&x as *const u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:11:5
++ |
++LL | Mutex::new(&mut x as *mut u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:12:5
++ |
++LL | Mutex::new(0u32);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::mutex-integer` implied by `-D warnings`
++
++error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++ --> $DIR/mutex_atomic.rs:13:5
++ |
++LL | Mutex::new(0i32);
++ | ^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::needless_bool)]
++#![allow(
++ unused,
++ dead_code,
++ clippy::no_effect,
++ clippy::if_same_then_else,
++ clippy::needless_return
++)]
++
++use std::cell::Cell;
++
++macro_rules! bool_comparison_trigger {
++ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
++
++ #[derive(Clone)]
++ pub struct Trigger {
++ $($i: (Cell<bool>, bool, bool)),+
++ }
++
++ #[allow(dead_code)]
++ impl Trigger {
++ pub fn trigger(&self, key: &str) -> bool {
++ $(
++ if let stringify!($i) = key {
++ return self.$i.1 && self.$i.2 == $def;
++ }
++ )+
++ false
++ }
++ }
++ )
++}
++
++fn main() {
++ let x = true;
++ let y = false;
++ x;
++ !x;
++ !(x && y);
++ if x {
++ x
++ } else {
++ false
++ }; // would also be questionable, but we don't catch this yet
++ bool_ret3(x);
++ bool_ret4(x);
++ bool_ret5(x, x);
++ bool_ret6(x, x);
++ needless_bool(x);
++ needless_bool2(x);
++ needless_bool3(x);
++}
++
++fn bool_ret3(x: bool) -> bool {
++ return x;
++}
++
++fn bool_ret4(x: bool) -> bool {
++ return !x;
++}
++
++fn bool_ret5(x: bool, y: bool) -> bool {
++ return x && y;
++}
++
++fn bool_ret6(x: bool, y: bool) -> bool {
++ return !(x && y);
++}
++
++fn needless_bool(x: bool) {
++ if x {};
++}
++
++fn needless_bool2(x: bool) {
++ if !x {};
++}
++
++fn needless_bool3(x: bool) {
++ bool_comparison_trigger! {
++ test_one: false, false;
++ test_three: false, false;
++ test_two: true, true;
++ }
++
++ if x {};
++ if !x {};
++}
++
++fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
++ let b = false;
++ let returns_bool = || false;
++
++ let x = if b {
++ true
++ } else { !returns_bool() };
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::needless_bool)]
++#![allow(
++ unused,
++ dead_code,
++ clippy::no_effect,
++ clippy::if_same_then_else,
++ clippy::needless_return
++)]
++
++use std::cell::Cell;
++
++macro_rules! bool_comparison_trigger {
++ ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => (
++
++ #[derive(Clone)]
++ pub struct Trigger {
++ $($i: (Cell<bool>, bool, bool)),+
++ }
++
++ #[allow(dead_code)]
++ impl Trigger {
++ pub fn trigger(&self, key: &str) -> bool {
++ $(
++ if let stringify!($i) = key {
++ return self.$i.1 && self.$i.2 == $def;
++ }
++ )+
++ false
++ }
++ }
++ )
++}
++
++fn main() {
++ let x = true;
++ let y = false;
++ if x {
++ true
++ } else {
++ false
++ };
++ if x {
++ false
++ } else {
++ true
++ };
++ if x && y {
++ false
++ } else {
++ true
++ };
++ if x {
++ x
++ } else {
++ false
++ }; // would also be questionable, but we don't catch this yet
++ bool_ret3(x);
++ bool_ret4(x);
++ bool_ret5(x, x);
++ bool_ret6(x, x);
++ needless_bool(x);
++ needless_bool2(x);
++ needless_bool3(x);
++}
++
++fn bool_ret3(x: bool) -> bool {
++ if x {
++ return true;
++ } else {
++ return false;
++ };
++}
++
++fn bool_ret4(x: bool) -> bool {
++ if x {
++ return false;
++ } else {
++ return true;
++ };
++}
++
++fn bool_ret5(x: bool, y: bool) -> bool {
++ if x && y {
++ return true;
++ } else {
++ return false;
++ };
++}
++
++fn bool_ret6(x: bool, y: bool) -> bool {
++ if x && y {
++ return false;
++ } else {
++ return true;
++ };
++}
++
++fn needless_bool(x: bool) {
++ if x == true {};
++}
++
++fn needless_bool2(x: bool) {
++ if x == false {};
++}
++
++fn needless_bool3(x: bool) {
++ bool_comparison_trigger! {
++ test_one: false, false;
++ test_three: false, false;
++ test_two: true, true;
++ }
++
++ if x == true {};
++ if x == false {};
++}
++
++fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
++ let b = false;
++ let returns_bool = || false;
++
++ let x = if b {
++ true
++ } else if returns_bool() {
++ false
++ } else {
++ true
++ };
++}
--- /dev/null
--- /dev/null
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:39:5
++ |
++LL | / if x {
++LL | | true
++LL | | } else {
++LL | | false
++LL | | };
++ | |_____^ help: you can reduce it to: `x`
++ |
++ = note: `-D clippy::needless-bool` implied by `-D warnings`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:44:5
++ |
++LL | / if x {
++LL | | false
++LL | | } else {
++LL | | true
++LL | | };
++ | |_____^ help: you can reduce it to: `!x`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:49:5
++ |
++LL | / if x && y {
++LL | | false
++LL | | } else {
++LL | | true
++LL | | };
++ | |_____^ help: you can reduce it to: `!(x && y)`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:69:5
++ |
++LL | / if x {
++LL | | return true;
++LL | | } else {
++LL | | return false;
++LL | | };
++ | |_____^ help: you can reduce it to: `return x`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:77:5
++ |
++LL | / if x {
++LL | | return false;
++LL | | } else {
++LL | | return true;
++LL | | };
++ | |_____^ help: you can reduce it to: `return !x`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:85:5
++ |
++LL | / if x && y {
++LL | | return true;
++LL | | } else {
++LL | | return false;
++LL | | };
++ | |_____^ help: you can reduce it to: `return x && y`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:93:5
++ |
++LL | / if x && y {
++LL | | return false;
++LL | | } else {
++LL | | return true;
++LL | | };
++ | |_____^ help: you can reduce it to: `return !(x && y)`
++
++error: equality checks against true are unnecessary
++ --> $DIR/fixable.rs:101:8
++ |
++LL | if x == true {};
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++ |
++ = note: `-D clippy::bool-comparison` implied by `-D warnings`
++
++error: equality checks against false can be replaced by a negation
++ --> $DIR/fixable.rs:105:8
++ |
++LL | if x == false {};
++ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: equality checks against true are unnecessary
++ --> $DIR/fixable.rs:115:8
++ |
++LL | if x == true {};
++ | ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: equality checks against false can be replaced by a negation
++ --> $DIR/fixable.rs:116:8
++ |
++LL | if x == false {};
++ | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: this if-then-else expression returns a bool literal
++ --> $DIR/fixable.rs:125:12
++ |
++LL | } else if returns_bool() {
++ | ____________^
++LL | | false
++LL | | } else {
++LL | | true
++LL | | };
++ | |_____^ help: you can reduce it to: `{ !returns_bool() }`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_bool)]
++#![allow(
++ unused,
++ dead_code,
++ clippy::no_effect,
++ clippy::if_same_then_else,
++ clippy::needless_return
++)]
++
++fn main() {
++ let x = true;
++ let y = false;
++ if x {
++ true
++ } else {
++ true
++ };
++ if x {
++ false
++ } else {
++ false
++ };
++ if x {
++ x
++ } else {
++ false
++ }; // would also be questionable, but we don't catch this yet
++ bool_ret(x);
++ bool_ret2(x);
++}
++
++fn bool_ret(x: bool) -> bool {
++ if x {
++ return true;
++ } else {
++ return true;
++ };
++}
++
++fn bool_ret2(x: bool) -> bool {
++ if x {
++ return false;
++ } else {
++ return false;
++ };
++}
--- /dev/null
--- /dev/null
++error: this if-then-else expression will always return true
++ --> $DIR/simple.rs:13:5
++ |
++LL | / if x {
++LL | | true
++LL | | } else {
++LL | | true
++LL | | };
++ | |_____^
++ |
++ = note: `-D clippy::needless-bool` implied by `-D warnings`
++
++error: this if-then-else expression will always return false
++ --> $DIR/simple.rs:18:5
++ |
++LL | / if x {
++LL | | false
++LL | | } else {
++LL | | false
++LL | | };
++ | |_____^
++
++error: this if-then-else expression will always return true
++ --> $DIR/simple.rs:33:5
++ |
++LL | / if x {
++LL | | return true;
++LL | | } else {
++LL | | return true;
++LL | | };
++ | |_____^
++
++error: this if-then-else expression will always return false
++ --> $DIR/simple.rs:41:5
++ |
++LL | / if x {
++LL | | return false;
++LL | | } else {
++LL | | return false;
++LL | | };
++ | |_____^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::needless_borrowed_reference)]
++
++fn x(y: &i32) -> i32 {
++ *y
++}
++
++#[warn(clippy::all, clippy::needless_borrow)]
++#[allow(unused_variables)]
++fn main() {
++ let a = 5;
++ let b = x(&a);
++ let c = x(&a);
++ let s = &String::from("hi");
++ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
++ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
++ let vec = Vec::new();
++ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
++ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
++ if let Some(cake) = Some(&5) {}
++ let garbl = match 42 {
++ 44 => &a,
++ 45 => {
++ println!("foo");
++ &&a // FIXME: this should lint, too
++ },
++ 46 => &a,
++ _ => panic!(),
++ };
++}
++
++fn f<T: Copy>(y: &T) -> T {
++ *y
++}
++
++fn g(y: &[u8]) -> u8 {
++ y[0]
++}
++
++trait Trait {}
++
++impl<'a> Trait for &'a str {}
++
++fn h(_: &dyn Trait) {}
++#[warn(clippy::needless_borrow)]
++#[allow(dead_code)]
++fn issue_1432() {
++ let mut v = Vec::<String>::new();
++ let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++ let _ = v.iter().filter(|&a| a.is_empty());
++
++ let _ = v.iter().filter(|&a| a.is_empty());
++}
++
++#[allow(dead_code)]
++#[warn(clippy::needless_borrow)]
++#[derive(Debug)]
++enum Foo<'a> {
++ Str(&'a str),
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::needless_borrowed_reference)]
++
++fn x(y: &i32) -> i32 {
++ *y
++}
++
++#[warn(clippy::all, clippy::needless_borrow)]
++#[allow(unused_variables)]
++fn main() {
++ let a = 5;
++ let b = x(&a);
++ let c = x(&&a);
++ let s = &String::from("hi");
++ let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
++ let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
++ let vec = Vec::new();
++ let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
++ h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
++ if let Some(ref cake) = Some(&5) {}
++ let garbl = match 42 {
++ 44 => &a,
++ 45 => {
++ println!("foo");
++ &&a // FIXME: this should lint, too
++ },
++ 46 => &&a,
++ _ => panic!(),
++ };
++}
++
++fn f<T: Copy>(y: &T) -> T {
++ *y
++}
++
++fn g(y: &[u8]) -> u8 {
++ y[0]
++}
++
++trait Trait {}
++
++impl<'a> Trait for &'a str {}
++
++fn h(_: &dyn Trait) {}
++#[warn(clippy::needless_borrow)]
++#[allow(dead_code)]
++fn issue_1432() {
++ let mut v = Vec::<String>::new();
++ let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++ let _ = v.iter().filter(|&ref a| a.is_empty());
++
++ let _ = v.iter().filter(|&a| a.is_empty());
++}
++
++#[allow(dead_code)]
++#[warn(clippy::needless_borrow)]
++#[derive(Debug)]
++enum Foo<'a> {
++ Str(&'a str),
++}
--- /dev/null
--- /dev/null
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++ --> $DIR/needless_borrow.rs:14:15
++ |
++LL | let c = x(&&a);
++ | ^^^ help: change this to: `&a`
++ |
++ = note: `-D clippy::needless-borrow` implied by `-D warnings`
++
++error: this pattern creates a reference to a reference
++ --> $DIR/needless_borrow.rs:21:17
++ |
++LL | if let Some(ref cake) = Some(&5) {}
++ | ^^^^^^^^ help: change this to: `cake`
++
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++ --> $DIR/needless_borrow.rs:28:15
++ |
++LL | 46 => &&a,
++ | ^^^ help: change this to: `&a`
++
++error: this pattern creates a reference to a reference
++ --> $DIR/needless_borrow.rs:51:31
++ |
++LL | let _ = v.iter().filter(|&ref a| a.is_empty());
++ | ^^^^^ help: change this to: `a`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::needless_borrowed_reference)]
++#[allow(unused_variables)]
++fn main() {
++ let mut v = Vec::<String>::new();
++ let _ = v.iter_mut().filter(|a| a.is_empty());
++ // ^ should be linted
++
++ let var = 3;
++ let thingy = Some(&var);
++ if let Some(&ref v) = thingy {
++ // ^ should be linted
++ }
++
++ let mut var2 = 5;
++ let thingy2 = Some(&mut var2);
++ if let Some(&mut ref mut v) = thingy2 {
++ // ^ should **not** be linted
++ // v is borrowed as mutable.
++ *v = 10;
++ }
++ if let Some(&mut ref v) = thingy2 {
++ // ^ should **not** be linted
++ // here, v is borrowed as immutable.
++ // can't do that:
++ //*v = 15;
++ }
++}
++
++#[allow(dead_code)]
++enum Animal {
++ Cat(u64),
++ Dog(u64),
++}
++
++#[allow(unused_variables)]
++#[allow(dead_code)]
++fn foo(a: &Animal, b: &Animal) {
++ match (a, b) {
++ (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
++ // ^ and ^ should **not** be linted
++ (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[warn(clippy::needless_borrowed_reference)]
++#[allow(unused_variables)]
++fn main() {
++ let mut v = Vec::<String>::new();
++ let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++ // ^ should be linted
++
++ let var = 3;
++ let thingy = Some(&var);
++ if let Some(&ref v) = thingy {
++ // ^ should be linted
++ }
++
++ let mut var2 = 5;
++ let thingy2 = Some(&mut var2);
++ if let Some(&mut ref mut v) = thingy2 {
++ // ^ should **not** be linted
++ // v is borrowed as mutable.
++ *v = 10;
++ }
++ if let Some(&mut ref v) = thingy2 {
++ // ^ should **not** be linted
++ // here, v is borrowed as immutable.
++ // can't do that:
++ //*v = 15;
++ }
++}
++
++#[allow(dead_code)]
++enum Animal {
++ Cat(u64),
++ Dog(u64),
++}
++
++#[allow(unused_variables)]
++#[allow(dead_code)]
++fn foo(a: &Animal, b: &Animal) {
++ match (a, b) {
++ (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
++ // ^ and ^ should **not** be linted
++ (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted
++ }
++}
--- /dev/null
--- /dev/null
++error: this pattern takes a reference on something that is being de-referenced
++ --> $DIR/needless_borrowed_ref.rs:7:34
++ |
++LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++ | ^^^^^^ help: try removing the `&ref` part and just keep: `a`
++ |
++ = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::suspicious_map)]
++
++use std::collections::{BTreeSet, HashMap, HashSet};
++
++#[warn(clippy::needless_collect)]
++#[allow(unused_variables, clippy::iter_cloned_collect)]
++fn main() {
++ let sample = [1; 5];
++ let len = sample.iter().count();
++ if sample.iter().next().is_none() {
++ // Empty
++ }
++ sample.iter().cloned().any(|x| x == 1);
++ sample.iter().map(|x| (x, x)).count();
++ // Notice the `HashSet`--this should not be linted
++ sample.iter().collect::<HashSet<_>>().len();
++ // Neither should this
++ sample.iter().collect::<BTreeSet<_>>().len();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::suspicious_map)]
++
++use std::collections::{BTreeSet, HashMap, HashSet};
++
++#[warn(clippy::needless_collect)]
++#[allow(unused_variables, clippy::iter_cloned_collect)]
++fn main() {
++ let sample = [1; 5];
++ let len = sample.iter().collect::<Vec<_>>().len();
++ if sample.iter().collect::<Vec<_>>().is_empty() {
++ // Empty
++ }
++ sample.iter().cloned().collect::<Vec<_>>().contains(&1);
++ sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
++ // Notice the `HashSet`--this should not be linted
++ sample.iter().collect::<HashSet<_>>().len();
++ // Neither should this
++ sample.iter().collect::<BTreeSet<_>>().len();
++}
--- /dev/null
--- /dev/null
++error: avoid using `collect()` when not needed
++ --> $DIR/needless_collect.rs:11:28
++ |
++LL | let len = sample.iter().collect::<Vec<_>>().len();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
++ |
++ = note: `-D clippy::needless-collect` implied by `-D warnings`
++
++error: avoid using `collect()` when not needed
++ --> $DIR/needless_collect.rs:12:21
++ |
++LL | if sample.iter().collect::<Vec<_>>().is_empty() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()`
++
++error: avoid using `collect()` when not needed
++ --> $DIR/needless_collect.rs:15:27
++ |
++LL | sample.iter().cloned().collect::<Vec<_>>().contains(&1);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)`
++
++error: avoid using `collect()` when not needed
++ --> $DIR/needless_collect.rs:16:34
++ |
++LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_continue)]
++
++macro_rules! zero {
++ ($x:expr) => {
++ $x == 0
++ };
++}
++
++macro_rules! nonzero {
++ ($x:expr) => {
++ !zero!($x)
++ };
++}
++
++fn main() {
++ let mut i = 1;
++ while i < 10 {
++ i += 1;
++
++ if i % 2 == 0 && i % 3 == 0 {
++ println!("{}", i);
++ println!("{}", i + 1);
++ if i % 5 == 0 {
++ println!("{}", i + 2);
++ }
++ let i = 0;
++ println!("bar {} ", i);
++ } else {
++ continue;
++ }
++
++ println!("bleh");
++ {
++ println!("blah");
++ }
++
++ // some comments that also should ideally be included in the
++ // output of the lint suggestion if possible.
++ if !(!(i == 2) || !(i == 5)) {
++ println!("lama");
++ }
++
++ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++ continue;
++ } else {
++ println!("Blabber");
++ println!("Jabber");
++ }
++
++ println!("bleh");
++ }
++}
++
++mod issue_2329 {
++ fn condition() -> bool {
++ unimplemented!()
++ }
++ fn update_condition() {}
++
++ // only the outer loop has a label
++ fn foo() {
++ 'outer: loop {
++ println!("Entry");
++ while condition() {
++ update_condition();
++ if condition() {
++ println!("foo-1");
++ } else {
++ continue 'outer; // should not lint here
++ }
++ println!("foo-2");
++
++ update_condition();
++ if condition() {
++ continue 'outer; // should not lint here
++ } else {
++ println!("foo-3");
++ }
++ println!("foo-4");
++ }
++ }
++ }
++
++ // both loops have labels
++ fn bar() {
++ 'outer: loop {
++ println!("Entry");
++ 'inner: while condition() {
++ update_condition();
++ if condition() {
++ println!("bar-1");
++ } else {
++ continue 'outer; // should not lint here
++ }
++ println!("bar-2");
++
++ update_condition();
++ if condition() {
++ println!("bar-3");
++ } else {
++ continue 'inner; // should lint here
++ }
++ println!("bar-4");
++
++ update_condition();
++ if condition() {
++ continue; // should lint here
++ } else {
++ println!("bar-5");
++ }
++ println!("bar-6");
++ }
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: this `else` block is redundant
++ --> $DIR/needless_continue.rs:28:16
++ |
++LL | } else {
++ | ________________^
++LL | | continue;
++LL | | }
++ | |_________^
++ |
++ = note: `-D clippy::needless-continue` implied by `-D warnings`
++ = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
++ if i % 2 == 0 && i % 3 == 0 {
++ println!("{}", i);
++ println!("{}", i + 1);
++ if i % 5 == 0 {
++ println!("{}", i + 2);
++ }
++ let i = 0;
++ println!("bar {} ", i);
++ // merged code follows:
++ println!("bleh");
++ {
++ println!("blah");
++ }
++ if !(!(i == 2) || !(i == 5)) {
++ println!("lama");
++ }
++ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++ continue;
++ } else {
++ println!("Blabber");
++ println!("Jabber");
++ }
++ println!("bleh");
++ }
++
++error: there is no need for an explicit `else` block for this `if` expression
++ --> $DIR/needless_continue.rs:43:9
++ |
++LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++LL | | continue;
++LL | | } else {
++LL | | println!("Blabber");
++LL | | println!("Jabber");
++LL | | }
++ | |_________^
++ |
++ = help: consider dropping the `else` clause
++ if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++ continue;
++ }
++ {
++ println!("Blabber");
++ println!("Jabber");
++ }
++
++error: this `else` block is redundant
++ --> $DIR/needless_continue.rs:100:24
++ |
++LL | } else {
++ | ________________________^
++LL | | continue 'inner; // should lint here
++LL | | }
++ | |_________________^
++ |
++ = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
++ if condition() {
++ println!("bar-3");
++ // merged code follows:
++ println!("bar-4");
++ update_condition();
++ if condition() {
++ continue; // should lint here
++ } else {
++ println!("bar-5");
++ }
++ println!("bar-6");
++ }
++
++error: there is no need for an explicit `else` block for this `if` expression
++ --> $DIR/needless_continue.rs:106:17
++ |
++LL | / if condition() {
++LL | | continue; // should lint here
++LL | | } else {
++LL | | println!("bar-5");
++LL | | }
++ | |_________________^
++ |
++ = help: consider dropping the `else` clause
++ if condition() {
++ continue; // should lint here
++ }
++ {
++ println!("bar-5");
++ }
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++/// This is a test for needless `fn main()` in doctests.
++///
++/// # Examples
++///
++/// This should lint
++/// ```
++/// fn main() {
++/// unimplemented!();
++/// }
++/// ```
++///
++/// This should, too.
++///
++/// ```rust
++/// fn main() {
++/// unimplemented!();
++/// }
++/// ```
++///
++/// This one too.
++///
++/// ```no_run
++/// fn main() {
++/// unimplemented!();
++/// }
++/// ```
++fn bad_doctests() {}
++
++/// # Examples
++///
++/// This shouldn't lint, because the `main` is empty:
++/// ```
++/// fn main(){}
++/// ```
++///
++/// This shouldn't lint either, because there's a `static`:
++/// ```
++/// static ANSWER: i32 = 42;
++///
++/// fn main() {
++/// assert_eq!(42, ANSWER);
++/// }
++/// ```
++///
++/// Neither should this lint because of `extern crate`:
++/// ```
++/// #![feature(test)]
++/// extern crate test;
++/// fn main() {
++/// assert_eq(1u8, test::black_box(1));
++/// }
++/// ```
++///
++/// We should not lint ignored examples:
++///
++/// ```rust,ignore
++/// fn main() {
++/// unimplemented!();
++/// }
++/// ```
++///
++/// Or even non-rust examples:
++///
++/// ```text
++/// fn main() {
++/// is what starts the program
++/// }
++/// ```
++fn no_false_positives() {}
++
++fn main() {
++ bad_doctests();
++ no_false_positives();
++}
--- /dev/null
--- /dev/null
++error: needless `fn main` in doctest
++ --> $DIR/needless_doc_main.rs:7:4
++ |
++LL | /// fn main() {
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
++
++error: needless `fn main` in doctest
++ --> $DIR/needless_doc_main.rs:15:4
++ |
++LL | /// fn main() {
++ | ^^^^^^^^^^^^
++
++error: needless `fn main` in doctest
++ --> $DIR/needless_doc_main.rs:23:4
++ |
++LL | /// fn main() {
++ | ^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_lifetimes)]
++#![allow(dead_code, clippy::needless_pass_by_value)]
++
++fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
++
++fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
++
++// No error; same lifetime on two params.
++fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
++
++// No error; static involved.
++fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
++
++fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
++
++fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
++ x
++}
++
++// No error; multiple input refs.
++fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
++ x
++}
++
++// No error; multiple input refs.
++fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
++ x
++}
++
++// No error; static involved.
++fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
++ x
++}
++
++// No error.
++fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
++ Ok(x)
++}
++
++// No error; two input refs.
++fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
++ x.unwrap()
++}
++
++fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
++ Ok(x)
++}
++
++// Where-clause, but without lifetimes.
++fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
++where
++ T: Copy,
++{
++ Ok(x)
++}
++
++type Ref<'r> = &'r u8;
++
++// No error; same lifetime on two params.
++fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
++
++fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
++
++// No error; bounded lifetime.
++fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
++
++// No error; bounded lifetime.
++fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
++where
++ 'b: 'a,
++{
++}
++
++struct Lt<'a, I: 'static> {
++ x: &'a I,
++}
++
++// No error; fn bound references `'a`.
++fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++where
++ F: Fn(Lt<'a, I>) -> Lt<'a, I>,
++{
++ unreachable!()
++}
++
++fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++where
++ for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
++{
++ unreachable!()
++}
++
++// No error; see below.
++fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
++ f(x);
++}
++
++fn fn_bound_3_cannot_elide() {
++ let x = 42;
++ let p = &x;
++ let mut q = &x;
++ // This will fail if we elide lifetimes of `fn_bound_3`.
++ fn_bound_3(p, |y| q = y);
++}
++
++// No error; multiple input refs.
++fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
++ if cond {
++ x
++ } else {
++ f()
++ }
++}
++
++struct X {
++ x: u8,
++}
++
++impl X {
++ fn self_and_out<'s>(&'s self) -> &'s u8 {
++ &self.x
++ }
++
++ // No error; multiple input refs.
++ fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
++ &self.x
++ }
++
++ fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
++
++ // No error; same lifetimes on two params.
++ fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
++}
++
++struct Foo<'a>(&'a u8);
++
++impl<'a> Foo<'a> {
++ // No error; lifetime `'a` not defined in method.
++ fn self_shared_lifetime(&self, _: &'a u8) {}
++ // No error; bounds exist.
++ fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
++}
++
++fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
++ unimplemented!()
++}
++
++fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
++fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
++fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes.
++fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++ unimplemented!()
++}
++
++trait WithLifetime<'a> {}
++
++type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
++
++// Should not warn because it won't build without the lifetime.
++fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
++ unimplemented!()
++}
++
++// Should warn because there is no lifetime on `Drop`, so this would be
++// unambiguous if we elided the lifetime.
++fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
++ unimplemented!()
++}
++
++type FooAlias<'a> = Foo<'a>;
++
++fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
++fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
++fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
++ unimplemented!()
++}
++
++// No warning; two input lifetimes.
++fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++ unimplemented!()
++}
++
++fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
++ unimplemented!()
++}
++
++fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
++ unimplemented!()
++}
++
++fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
++ unimplemented!()
++}
++fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
++ unimplemented!()
++}
++
++// Don't warn on these; see issue #292.
++fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
++ unimplemented!()
++}
++
++// See issue #740.
++struct Test {
++ vec: Vec<usize>,
++}
++
++impl Test {
++ fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
++ unimplemented!()
++ }
++}
++
++trait LintContext<'a> {}
++
++fn f<'a, T: LintContext<'a>>(_: &T) {}
++
++fn test<'a>(x: &'a [u8]) -> u8 {
++ let y: &'a u8 = &x[5];
++ *y
++}
++
++// Issue #3284: give hint regarding lifetime in return type.
++struct Cow<'a> {
++ x: &'a str,
++}
++fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
++ unimplemented!()
++}
++
++// Make sure we still warn on implementations
++mod issue4291 {
++ trait BadTrait {
++ fn needless_lt<'a>(x: &'a u8) {}
++ }
++
++ impl BadTrait for () {
++ fn needless_lt<'a>(_x: &'a u8) {}
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:4:1
++ |
++LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:6:1
++ |
++LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:16:1
++ |
++LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:45:1
++ |
++LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:50:1
++ |
++LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:62:1
++ |
++LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:86:1
++ |
++LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:120:5
++ |
++LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:129:5
++ |
++LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:148:1
++ |
++LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:178:1
++ |
++LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:184:1
++ |
++LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:203:1
++ |
++LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:211:1
++ |
++LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:247:1
++ |
++LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:254:9
++ |
++LL | fn needless_lt<'a>(x: &'a u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++ --> $DIR/needless_lifetimes.rs:258:9
++ |
++LL | fn needless_lt<'a>(_x: &'a u8) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_pass_by_value)]
++#![allow(
++ dead_code,
++ clippy::single_match,
++ clippy::redundant_pattern_matching,
++ clippy::many_single_char_names,
++ clippy::option_option,
++ clippy::redundant_clone
++)]
++
++use std::borrow::Borrow;
++use std::collections::HashSet;
++use std::convert::AsRef;
++use std::mem::MaybeUninit;
++
++// `v` should be warned
++// `w`, `x` and `y` are allowed (moved or mutated)
++fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
++ assert_eq!(v.len(), 42);
++
++ consume(w);
++
++ x.push(T::default());
++
++ y
++}
++
++fn consume<T>(_: T) {}
++
++struct Wrapper(String);
++
++fn bar(x: String, y: Wrapper) {
++ assert_eq!(x.len(), 42);
++ assert_eq!(y.0.len(), 42);
++}
++
++// V implements `Borrow<V>`, but should be warned correctly
++fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
++ println!("{}", t.borrow());
++ println!("{}", u.as_ref());
++ consume(&v);
++}
++
++// ok
++fn test_fn<F: Fn(i32) -> i32>(f: F) {
++ f(1);
++}
++
++// x should be warned, but y is ok
++fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
++ match x {
++ Some(Some(_)) => 1, // not moved
++ _ => 0,
++ };
++
++ match y {
++ Some(Some(s)) => consume(s), // moved
++ _ => (),
++ };
++}
++
++// x and y should be warned, but z is ok
++fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++ let Wrapper(s) = z; // moved
++ let Wrapper(ref t) = y; // not moved
++ let Wrapper(_) = y; // still not moved
++
++ assert_eq!(x.0.len(), s.len());
++ println!("{}", t);
++}
++
++trait Foo {}
++
++// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead
++trait Serialize {}
++impl<'a, T> Serialize for &'a T where T: Serialize {}
++impl Serialize for i32 {}
++
++fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
++
++fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++ s.capacity();
++ let _ = t.clone();
++ u.capacity();
++ let _ = v.clone();
++}
++
++struct S<T, U>(T, U);
++
++impl<T: Serialize, U> S<T, U> {
++ fn foo(
++ self,
++ // taking `self` by value is always allowed
++ s: String,
++ t: String,
++ ) -> usize {
++ s.len() + t.capacity()
++ }
++
++ fn bar(_t: T, // Ok, since `&T: Serialize` too
++ ) {
++ }
++
++ fn baz(&self, _u: U, _s: Self) {}
++}
++
++trait FalsePositive {
++ fn visit_str(s: &str);
++ fn visit_string(s: String) {
++ Self::visit_str(&s);
++ }
++}
++
++// shouldn't warn on extern funcs
++extern "C" fn ext(x: MaybeUninit<usize>) -> usize {
++ unsafe { x.assume_init() }
++}
++
++// whitelist RangeArgument
++fn range<T: ::std::ops::RangeBounds<usize>>(range: T) {
++ let _ = range.start_bound();
++}
++
++struct CopyWrapper(u32);
++
++fn bar_copy(x: u32, y: CopyWrapper) {
++ assert_eq!(x, 42);
++ assert_eq!(y.0, 42);
++}
++
++// x and y should be warned, but z is ok
++fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++ let CopyWrapper(s) = z; // moved
++ let CopyWrapper(ref t) = y; // not moved
++ let CopyWrapper(_) = y; // still not moved
++
++ assert_eq!(x.0, s);
++ println!("{}", t);
++}
++
++// The following 3 lines should not cause an ICE. See #2831
++trait Bar<'a, A> {}
++impl<'b, T> Bar<'b, T> for T {}
++fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
++
++// Also this should not cause an ICE. See #2831
++trait Club<'a, A> {}
++impl<T> Club<'static, T> for T {}
++fn more_fun(_item: impl Club<'static, i32>) {}
++
++fn is_sync<T>(_: T)
++where
++ T: Sync,
++{
++}
++
++fn main() {
++ // This should not cause an ICE either
++ // https://github.com/rust-lang/rust-clippy/issues/3144
++ is_sync(HashSet::<usize>::new());
++}
--- /dev/null
--- /dev/null
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:18:23
++ |
++LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
++ | ^^^^^^ help: consider changing the type to: `&[T]`
++ |
++ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:32:11
++ |
++LL | fn bar(x: String, y: Wrapper) {
++ | ^^^^^^ help: consider changing the type to: `&str`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:32:22
++ |
++LL | fn bar(x: String, y: Wrapper) {
++ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:38:71
++ |
++LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
++ | ^ help: consider taking a reference instead: `&V`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:50:18
++ |
++LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:63:24
++ |
++LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:63:36
++ |
++LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++ | ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:79:49
++ |
++LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
++ | ^ help: consider taking a reference instead: `&T`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:81:18
++ |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++ | ^^^^^^ help: consider taking a reference instead: `&String`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:81:29
++ |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++ | ^^^^^^
++ |
++help: consider changing the type to
++ |
++LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
++ | ^^^^
++help: change `t.clone()` to
++ |
++LL | let _ = t.to_string();
++ | ^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:81:40
++ |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++ | ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:81:53
++ |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++ | ^^^^^^^^
++ |
++help: consider changing the type to
++ |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
++ | ^^^^^^
++help: change `v.clone()` to
++ |
++LL | let _ = v.to_owned();
++ | ^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:94:12
++ |
++LL | s: String,
++ | ^^^^^^ help: consider changing the type to: `&str`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:95:12
++ |
++LL | t: String,
++ | ^^^^^^ help: consider taking a reference instead: `&String`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:104:23
++ |
++LL | fn baz(&self, _u: U, _s: Self) {}
++ | ^ help: consider taking a reference instead: `&U`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:104:30
++ |
++LL | fn baz(&self, _u: U, _s: Self) {}
++ | ^^^^ help: consider taking a reference instead: `&Self`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:126:24
++ |
++LL | fn bar_copy(x: u32, y: CopyWrapper) {
++ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++ |
++help: consider marking this type as `Copy`
++ --> $DIR/needless_pass_by_value.rs:124:1
++ |
++LL | struct CopyWrapper(u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:132:29
++ |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++ |
++help: consider marking this type as `Copy`
++ --> $DIR/needless_pass_by_value.rs:124:1
++ |
++LL | struct CopyWrapper(u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:132:45
++ |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++ |
++help: consider marking this type as `Copy`
++ --> $DIR/needless_pass_by_value.rs:124:1
++ |
++LL | struct CopyWrapper(u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:132:61
++ |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++ | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++ |
++help: consider marking this type as `Copy`
++ --> $DIR/needless_pass_by_value.rs:124:1
++ |
++LL | struct CopyWrapper(u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:144:40
++ |
++LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
++ | ^ help: consider taking a reference instead: `&S`
++
++error: this argument is passed by value, but not consumed in the function body
++ --> $DIR/needless_pass_by_value.rs:149:20
++ |
++LL | fn more_fun(_item: impl Club<'static, i32>) {}
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
++
++error: aborting due to 22 previous errors
++
--- /dev/null
--- /dev/null
++#![crate_type = "proc-macro"]
++#![warn(clippy::needless_pass_by_value)]
++
++extern crate proc_macro;
++
++use proc_macro::TokenStream;
++
++#[proc_macro_derive(Foo)]
++pub fn foo(_input: TokenStream) -> TokenStream {
++ unimplemented!()
++}
++
++#[proc_macro]
++pub fn bar(_input: TokenStream) -> TokenStream {
++ unimplemented!()
++}
++
++#[proc_macro_attribute]
++pub fn baz(_args: TokenStream, _input: TokenStream) -> TokenStream {
++ unimplemented!()
++}
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_range_loop)]
++
++static STATIC: [usize; 4] = [0, 1, 8, 16];
++const CONST: [usize; 4] = [0, 1, 8, 16];
++const MAX_LEN: usize = 42;
++
++fn main() {
++ let mut vec = vec![1, 2, 3, 4];
++ let vec2 = vec![1, 2, 3, 4];
++ for i in 0..vec.len() {
++ println!("{}", vec[i]);
++ }
++
++ for i in 0..vec.len() {
++ let i = 42; // make a different `i`
++ println!("{}", vec[i]); // ok, not the `i` of the for-loop
++ }
++
++ for i in 0..vec.len() {
++ let _ = vec[i];
++ }
++
++ // ICE #746
++ for j in 0..4 {
++ println!("{:?}", STATIC[j]);
++ }
++
++ for j in 0..4 {
++ println!("{:?}", CONST[j]);
++ }
++
++ for i in 0..vec.len() {
++ println!("{} {}", vec[i], i);
++ }
++ for i in 0..vec.len() {
++ // not an error, indexing more than one variable
++ println!("{} {}", vec[i], vec2[i]);
++ }
++
++ for i in 0..vec.len() {
++ println!("{}", vec2[i]);
++ }
++
++ for i in 5..vec.len() {
++ println!("{}", vec[i]);
++ }
++
++ for i in 0..MAX_LEN {
++ println!("{}", vec[i]);
++ }
++
++ for i in 0..=MAX_LEN {
++ println!("{}", vec[i]);
++ }
++
++ for i in 5..10 {
++ println!("{}", vec[i]);
++ }
++
++ for i in 5..=10 {
++ println!("{}", vec[i]);
++ }
++
++ for i in 5..vec.len() {
++ println!("{} {}", vec[i], i);
++ }
++
++ for i in 5..10 {
++ println!("{} {}", vec[i], i);
++ }
++
++ // #2542
++ for i in 0..vec.len() {
++ vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i));
++ }
++
++ // #3788
++ let test = Test {
++ inner: vec![1, 2, 3, 4],
++ };
++ for i in 0..2 {
++ println!("{}", test[i]);
++ }
++}
++
++struct Test {
++ inner: Vec<usize>,
++}
++
++impl std::ops::Index<usize> for Test {
++ type Output = usize;
++ fn index(&self, index: usize) -> &Self::Output {
++ &self.inner[index]
++ }
++}
--- /dev/null
--- /dev/null
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:10:14
++ |
++LL | for i in 0..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::needless-range-loop` implied by `-D warnings`
++help: consider using an iterator
++ |
++LL | for <item> in &vec {
++ | ^^^^^^ ^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:19:14
++ |
++LL | for i in 0..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &vec {
++ | ^^^^^^ ^^^^
++
++error: the loop variable `j` is only used to index `STATIC`.
++ --> $DIR/needless_range_loop.rs:24:14
++ |
++LL | for j in 0..4 {
++ | ^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &STATIC {
++ | ^^^^^^ ^^^^^^^
++
++error: the loop variable `j` is only used to index `CONST`.
++ --> $DIR/needless_range_loop.rs:28:14
++ |
++LL | for j in 0..4 {
++ | ^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &CONST {
++ | ^^^^^^ ^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++ --> $DIR/needless_range_loop.rs:32:14
++ |
++LL | for i in 0..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for (i, <item>) in vec.iter().enumerate() {
++ | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec2`.
++ --> $DIR/needless_range_loop.rs:40:14
++ |
++LL | for i in 0..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec2.iter().take(vec.len()) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:44:14
++ |
++LL | for i in 5..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter().skip(5) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:48:14
++ |
++LL | for i in 0..MAX_LEN {
++ | ^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter().take(MAX_LEN) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:52:14
++ |
++LL | for i in 0..=MAX_LEN {
++ | ^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter().take(MAX_LEN + 1) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:56:14
++ |
++LL | for i in 5..10 {
++ | ^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter().take(10).skip(5) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop.rs:60:14
++ |
++LL | for i in 5..=10 {
++ | ^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter().take(10 + 1).skip(5) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++ --> $DIR/needless_range_loop.rs:64:14
++ |
++LL | for i in 5..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for (i, <item>) in vec.iter().enumerate().skip(5) {
++ | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++ --> $DIR/needless_range_loop.rs:68:14
++ |
++LL | for i in 5..10 {
++ | ^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
++ | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++ --> $DIR/needless_range_loop.rs:73:14
++ |
++LL | for i in 0..vec.len() {
++ | ^^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for (i, <item>) in vec.iter_mut().enumerate() {
++ | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_range_loop)]
++
++fn calc_idx(i: usize) -> usize {
++ (i + i + 20) % 4
++}
++
++fn main() {
++ let ns = vec![2, 3, 5, 7];
++
++ for i in 3..10 {
++ println!("{}", ns[i]);
++ }
++
++ for i in 3..10 {
++ println!("{}", ns[i % 4]);
++ }
++
++ for i in 3..10 {
++ println!("{}", ns[i % ns.len()]);
++ }
++
++ for i in 3..10 {
++ println!("{}", ns[calc_idx(i)]);
++ }
++
++ for i in 3..10 {
++ println!("{}", ns[calc_idx(i) % 4]);
++ }
++
++ let mut ms = vec![1, 2, 3, 4, 5, 6];
++ for i in 0..ms.len() {
++ ms[i] *= 2;
++ }
++ assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
++
++ let mut ms = vec![1, 2, 3, 4, 5, 6];
++ for i in 0..ms.len() {
++ let x = &mut ms[i];
++ *x *= 2;
++ }
++ assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
++
++ let g = vec![1, 2, 3, 4, 5, 6];
++ let glen = g.len();
++ for i in 0..glen {
++ let x: u32 = g[i + 1..].iter().sum();
++ println!("{}", g[i] + x);
++ }
++ assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
++
++ let mut g = vec![1, 2, 3, 4, 5, 6];
++ let glen = g.len();
++ for i in 0..glen {
++ g[i] = g[i + 1..].iter().sum();
++ }
++ assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
++
++ let x = 5;
++ let mut vec = vec![0; 9];
++
++ for i in x..x + 4 {
++ vec[i] += 1;
++ }
++
++ let x = 5;
++ let mut vec = vec![0; 10];
++
++ for i in x..=x + 4 {
++ vec[i] += 1;
++ }
++
++ let arr = [1, 2, 3];
++
++ for i in 0..3 {
++ println!("{}", arr[i]);
++ }
++
++ for i in 0..2 {
++ println!("{}", arr[i]);
++ }
++
++ for i in 1..3 {
++ println!("{}", arr[i]);
++ }
++}
--- /dev/null
--- /dev/null
++error: the loop variable `i` is only used to index `ns`.
++ --> $DIR/needless_range_loop2.rs:10:14
++ |
++LL | for i in 3..10 {
++ | ^^^^^
++ |
++ = note: `-D clippy::needless-range-loop` implied by `-D warnings`
++help: consider using an iterator
++ |
++LL | for <item> in ns.iter().take(10).skip(3) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `ms`.
++ --> $DIR/needless_range_loop2.rs:31:14
++ |
++LL | for i in 0..ms.len() {
++ | ^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &mut ms {
++ | ^^^^^^ ^^^^^^^
++
++error: the loop variable `i` is only used to index `ms`.
++ --> $DIR/needless_range_loop2.rs:37:14
++ |
++LL | for i in 0..ms.len() {
++ | ^^^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &mut ms {
++ | ^^^^^^ ^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop2.rs:61:14
++ |
++LL | for i in x..x + 4 {
++ | ^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter_mut().skip(x).take(4) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++ --> $DIR/needless_range_loop2.rs:68:14
++ |
++LL | for i in x..=x + 4 {
++ | ^^^^^^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in vec.iter_mut().skip(x).take(4 + 1) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++ --> $DIR/needless_range_loop2.rs:74:14
++ |
++LL | for i in 0..3 {
++ | ^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in &arr {
++ | ^^^^^^ ^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++ --> $DIR/needless_range_loop2.rs:78:14
++ |
++LL | for i in 0..2 {
++ | ^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in arr.iter().take(2) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++ --> $DIR/needless_range_loop2.rs:82:14
++ |
++LL | for i in 1..3 {
++ | ^^^^
++ |
++help: consider using an iterator
++ |
++LL | for <item> in arr.iter().skip(1) {
++ | ^^^^^^ ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::needless_bool)]
++#![allow(clippy::if_same_then_else, clippy::single_match)]
++#![warn(clippy::needless_return)]
++
++macro_rules! the_answer {
++ () => {
++ 42
++ };
++}
++
++fn test_end_of_fn() -> bool {
++ if true {
++ // no error!
++ return true;
++ }
++ true
++}
++
++fn test_no_semicolon() -> bool {
++ true
++}
++
++fn test_if_block() -> bool {
++ if true {
++ true
++ } else {
++ false
++ }
++}
++
++fn test_match(x: bool) -> bool {
++ match x {
++ true => false,
++ false => {
++ true
++ },
++ }
++}
++
++fn test_closure() {
++ let _ = || {
++ true
++ };
++ let _ = || true;
++}
++
++fn test_macro_call() -> i32 {
++ return the_answer!();
++}
++
++fn test_void_fun() {
++
++}
++
++fn test_void_if_fun(b: bool) {
++ if b {
++
++ } else {
++
++ }
++}
++
++fn test_void_match(x: u32) {
++ match x {
++ 0 => (),
++ _ => {},
++ }
++}
++
++fn main() {
++ let _ = test_end_of_fn();
++ let _ = test_no_semicolon();
++ let _ = test_if_block();
++ let _ = test_match(true);
++ test_closure();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused, clippy::needless_bool)]
++#![allow(clippy::if_same_then_else, clippy::single_match)]
++#![warn(clippy::needless_return)]
++
++macro_rules! the_answer {
++ () => {
++ 42
++ };
++}
++
++fn test_end_of_fn() -> bool {
++ if true {
++ // no error!
++ return true;
++ }
++ return true;
++}
++
++fn test_no_semicolon() -> bool {
++ return true;
++}
++
++fn test_if_block() -> bool {
++ if true {
++ return true;
++ } else {
++ return false;
++ }
++}
++
++fn test_match(x: bool) -> bool {
++ match x {
++ true => return false,
++ false => {
++ return true;
++ },
++ }
++}
++
++fn test_closure() {
++ let _ = || {
++ return true;
++ };
++ let _ = || return true;
++}
++
++fn test_macro_call() -> i32 {
++ return the_answer!();
++}
++
++fn test_void_fun() {
++ return;
++}
++
++fn test_void_if_fun(b: bool) {
++ if b {
++ return;
++ } else {
++ return;
++ }
++}
++
++fn test_void_match(x: u32) {
++ match x {
++ 0 => (),
++ _ => return,
++ }
++}
++
++fn main() {
++ let _ = test_end_of_fn();
++ let _ = test_no_semicolon();
++ let _ = test_if_block();
++ let _ = test_match(true);
++ test_closure();
++}
--- /dev/null
--- /dev/null
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:18:5
++ |
++LL | return true;
++ | ^^^^^^^^^^^^ help: remove `return`: `true`
++ |
++ = note: `-D clippy::needless-return` implied by `-D warnings`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:22:5
++ |
++LL | return true;
++ | ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:27:9
++ |
++LL | return true;
++ | ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:29:9
++ |
++LL | return false;
++ | ^^^^^^^^^^^^^ help: remove `return`: `false`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:35:17
++ |
++LL | true => return false,
++ | ^^^^^^^^^^^^ help: remove `return`: `false`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:37:13
++ |
++LL | return true;
++ | ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:44:9
++ |
++LL | return true;
++ | ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:46:16
++ |
++LL | let _ = || return true;
++ | ^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:54:5
++ |
++LL | return;
++ | ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:59:9
++ |
++LL | return;
++ | ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:61:9
++ |
++LL | return;
++ | ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++ --> $DIR/needless_return.rs:68:14
++ |
++LL | _ => return,
++ | ^^^^^^ help: replace `return` with an empty block: `{}`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::needless_update)]
++#![allow(clippy::no_effect)]
++
++struct S {
++ pub a: i32,
++ pub b: i32,
++}
++
++fn main() {
++ let base = S { a: 0, b: 0 };
++ S { ..base }; // no error
++ S { a: 1, ..base }; // no error
++ S { a: 1, b: 1, ..base };
++}
--- /dev/null
--- /dev/null
++error: struct update has no effect, all the fields in the struct have already been specified
++ --> $DIR/needless_update.rs:13:23
++ |
++LL | S { a: 1, b: 1, ..base };
++ | ^^^^
++ |
++ = note: `-D clippy::needless-update` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++//! This test case utilizes `f64` an easy example for `PartialOrd` only types
++//! but the lint itself actually validates any expression where the left
++//! operand implements `PartialOrd` but not `Ord`.
++
++use std::cmp::Ordering;
++
++#[warn(clippy::neg_cmp_op_on_partial_ord)]
++fn main() {
++ let a_value = 1.0;
++ let another_value = 7.0;
++
++ // --- Bad ---
++
++ // Not Less but potentially Greater, Equal or Uncomparable.
++ let _not_less = !(a_value < another_value);
++
++ // Not Less or Equal but potentially Greater or Uncomparable.
++ let _not_less_or_equal = !(a_value <= another_value);
++
++ // Not Greater but potentially Less, Equal or Uncomparable.
++ let _not_greater = !(a_value > another_value);
++
++ // Not Greater or Equal but potentially Less or Uncomparable.
++ let _not_greater_or_equal = !(a_value >= another_value);
++
++ // --- Good ---
++
++ let _not_less = match a_value.partial_cmp(&another_value) {
++ None | Some(Ordering::Greater) | Some(Ordering::Equal) => true,
++ _ => false,
++ };
++ let _not_less_or_equal = match a_value.partial_cmp(&another_value) {
++ None | Some(Ordering::Greater) => true,
++ _ => false,
++ };
++ let _not_greater = match a_value.partial_cmp(&another_value) {
++ None | Some(Ordering::Less) | Some(Ordering::Equal) => true,
++ _ => false,
++ };
++ let _not_greater_or_equal = match a_value.partial_cmp(&another_value) {
++ None | Some(Ordering::Less) => true,
++ _ => false,
++ };
++
++ // --- Should not trigger ---
++
++ let _ = a_value < another_value;
++ let _ = a_value <= another_value;
++ let _ = a_value > another_value;
++ let _ = a_value >= another_value;
++
++ // --- regression tests ---
++
++ // Issue 2856: False positive on assert!()
++ //
++ // The macro always negates the result of the given comparison in its
++ // internal check which automatically triggered the lint. As it's an
++ // external macro there was no chance to do anything about it which led
++ // to a whitelisting of all external macros.
++ assert!(a_value < another_value);
++}
--- /dev/null
--- /dev/null
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++ --> $DIR/neg_cmp_op_on_partial_ord.rs:15:21
++ |
++LL | let _not_less = !(a_value < another_value);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++ --> $DIR/neg_cmp_op_on_partial_ord.rs:18:30
++ |
++LL | let _not_less_or_equal = !(a_value <= another_value);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++ --> $DIR/neg_cmp_op_on_partial_ord.rs:21:24
++ |
++LL | let _not_greater = !(a_value > another_value);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++ --> $DIR/neg_cmp_op_on_partial_ord.rs:24:33
++ |
++LL | let _not_greater_or_equal = !(a_value >= another_value);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::neg_multiply)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++use std::ops::Mul;
++
++struct X;
++
++impl Mul<isize> for X {
++ type Output = X;
++
++ fn mul(self, _r: isize) -> Self {
++ self
++ }
++}
++
++impl Mul<X> for isize {
++ type Output = X;
++
++ fn mul(self, _r: X) -> X {
++ X
++ }
++}
++
++fn main() {
++ let x = 0;
++
++ x * -1;
++
++ -1 * x;
++
++ -1 * -1; // should be ok
++
++ X * -1; // should be ok
++ -1 * X; // should also be ok
++}
--- /dev/null
--- /dev/null
++error: Negation by multiplying with `-1`
++ --> $DIR/neg_multiply.rs:27:5
++ |
++LL | x * -1;
++ | ^^^^^^
++ |
++ = note: `-D clippy::neg-multiply` implied by `-D warnings`
++
++error: Negation by multiplying with `-1`
++ --> $DIR/neg_multiply.rs:29:5
++ |
++LL | -1 * x;
++ | ^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(
++ clippy::single_match,
++ unused_assignments,
++ unused_variables,
++ clippy::while_immutable_condition
++)]
++
++fn test1() {
++ let mut x = 0;
++ loop {
++ // clippy::never_loop
++ x += 1;
++ if x == 1 {
++ return;
++ }
++ break;
++ }
++}
++
++fn test2() {
++ let mut x = 0;
++ loop {
++ x += 1;
++ if x == 1 {
++ break;
++ }
++ }
++}
++
++fn test3() {
++ let mut x = 0;
++ loop {
++ // never loops
++ x += 1;
++ break;
++ }
++}
++
++fn test4() {
++ let mut x = 1;
++ loop {
++ x += 1;
++ match x {
++ 5 => return,
++ _ => (),
++ }
++ }
++}
++
++fn test5() {
++ let i = 0;
++ loop {
++ // never loops
++ while i == 0 {
++ // never loops
++ break;
++ }
++ return;
++ }
++}
++
++fn test6() {
++ let mut x = 0;
++ 'outer: loop {
++ x += 1;
++ loop {
++ // never loops
++ if x == 5 {
++ break;
++ }
++ continue 'outer;
++ }
++ return;
++ }
++}
++
++fn test7() {
++ let mut x = 0;
++ loop {
++ x += 1;
++ match x {
++ 1 => continue,
++ _ => (),
++ }
++ return;
++ }
++}
++
++fn test8() {
++ let mut x = 0;
++ loop {
++ x += 1;
++ match x {
++ 5 => return,
++ _ => continue,
++ }
++ }
++}
++
++fn test9() {
++ let x = Some(1);
++ while let Some(y) = x {
++ // never loops
++ return;
++ }
++}
++
++fn test10() {
++ for x in 0..10 {
++ // never loops
++ match x {
++ 1 => break,
++ _ => return,
++ }
++ }
++}
++
++fn test11<F: FnMut() -> i32>(mut f: F) {
++ loop {
++ return match f() {
++ 1 => continue,
++ _ => (),
++ };
++ }
++}
++
++pub fn test12(a: bool, b: bool) {
++ 'label: loop {
++ loop {
++ if a {
++ continue 'label;
++ }
++ if b {
++ break;
++ }
++ }
++ break;
++ }
++}
++
++pub fn test13() {
++ let mut a = true;
++ loop {
++ // infinite loop
++ while a {
++ if true {
++ a = false;
++ continue;
++ }
++ return;
++ }
++ }
++}
++
++pub fn test14() {
++ let mut a = true;
++ 'outer: while a {
++ // never loops
++ while a {
++ if a {
++ a = false;
++ continue;
++ }
++ }
++ break 'outer;
++ }
++}
++
++// Issue #1991: the outter loop should not warn.
++pub fn test15() {
++ 'label: loop {
++ while false {
++ break 'label;
++ }
++ }
++}
++
++// Issue #4058: `continue` in `break` expression
++pub fn test16() {
++ let mut n = 1;
++ loop {
++ break if n != 5 {
++ n += 1;
++ continue;
++ };
++ }
++}
++
++fn main() {
++ test1();
++ test2();
++ test3();
++ test4();
++ test5();
++ test6();
++ test7();
++ test8();
++ test9();
++ test10();
++ test11(|| 0);
++ test12(true, false);
++ test13();
++ test14();
++}
--- /dev/null
--- /dev/null
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:10:5
++ |
++LL | / loop {
++LL | | // clippy::never_loop
++LL | | x += 1;
++LL | | if x == 1 {
++... |
++LL | | break;
++LL | | }
++ | |_____^
++ |
++ = note: `#[deny(clippy::never_loop)]` on by default
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:32:5
++ |
++LL | / loop {
++LL | | // never loops
++LL | | x += 1;
++LL | | break;
++LL | | }
++ | |_____^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:52:5
++ |
++LL | / loop {
++LL | | // never loops
++LL | | while i == 0 {
++LL | | // never loops
++... |
++LL | | return;
++LL | | }
++ | |_____^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:54:9
++ |
++LL | / while i == 0 {
++LL | | // never loops
++LL | | break;
++LL | | }
++ | |_________^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:66:9
++ |
++LL | / loop {
++LL | | // never loops
++LL | | if x == 5 {
++LL | | break;
++LL | | }
++LL | | continue 'outer;
++LL | | }
++ | |_________^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:102:5
++ |
++LL | / while let Some(y) = x {
++LL | | // never loops
++LL | | return;
++LL | | }
++ | |_____^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:109:5
++ |
++LL | / for x in 0..10 {
++LL | | // never loops
++LL | | match x {
++LL | | 1 => break,
++LL | | _ => return,
++LL | | }
++LL | | }
++ | |_____^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:157:5
++ |
++LL | / 'outer: while a {
++LL | | // never loops
++LL | | while a {
++LL | | if a {
++... |
++LL | | break 'outer;
++LL | | }
++ | |_____^
++
++error: this loop never actually loops
++ --> $DIR/never_loop.rs:172:9
++ |
++LL | / while false {
++LL | | break 'label;
++LL | | }
++ | |_________^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::new_ret_no_self)]
++#![allow(dead_code)]
++
++fn main() {}
++
++trait R {
++ type Item;
++}
++
++trait Q {
++ type Item;
++ type Item2;
++}
++
++struct S;
++
++impl R for S {
++ type Item = Self;
++}
++
++impl S {
++ // should not trigger the lint
++ pub fn new() -> impl R<Item = Self> {
++ S
++ }
++}
++
++struct S2;
++
++impl R for S2 {
++ type Item = Self;
++}
++
++impl S2 {
++ // should not trigger the lint
++ pub fn new(_: String) -> impl R<Item = Self> {
++ S2
++ }
++}
++
++struct S3;
++
++impl R for S3 {
++ type Item = u32;
++}
++
++impl S3 {
++ // should trigger the lint
++ pub fn new(_: String) -> impl R<Item = u32> {
++ S3
++ }
++}
++
++struct S4;
++
++impl Q for S4 {
++ type Item = u32;
++ type Item2 = Self;
++}
++
++impl S4 {
++ // should not trigger the lint
++ pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
++ S4
++ }
++}
++
++struct T;
++
++impl T {
++ // should not trigger lint
++ pub fn new() -> Self {
++ unimplemented!();
++ }
++}
++
++struct U;
++
++impl U {
++ // should trigger lint
++ pub fn new() -> u32 {
++ unimplemented!();
++ }
++}
++
++struct V;
++
++impl V {
++ // should trigger lint
++ pub fn new(_: String) -> u32 {
++ unimplemented!();
++ }
++}
++
++struct TupleReturnerOk;
++
++impl TupleReturnerOk {
++ // should not trigger lint
++ pub fn new() -> (Self, u32) {
++ unimplemented!();
++ }
++}
++
++struct TupleReturnerOk2;
++
++impl TupleReturnerOk2 {
++ // should not trigger lint (it doesn't matter which element in the tuple is Self)
++ pub fn new() -> (u32, Self) {
++ unimplemented!();
++ }
++}
++
++struct TupleReturnerOk3;
++
++impl TupleReturnerOk3 {
++ // should not trigger lint (tuple can contain multiple Self)
++ pub fn new() -> (Self, Self) {
++ unimplemented!();
++ }
++}
++
++struct TupleReturnerBad;
++
++impl TupleReturnerBad {
++ // should trigger lint
++ pub fn new() -> (u32, u32) {
++ unimplemented!();
++ }
++}
++
++struct MutPointerReturnerOk;
++
++impl MutPointerReturnerOk {
++ // should not trigger lint
++ pub fn new() -> *mut Self {
++ unimplemented!();
++ }
++}
++
++struct MutPointerReturnerOk2;
++
++impl MutPointerReturnerOk2 {
++ // should not trigger lint
++ pub fn new() -> *const Self {
++ unimplemented!();
++ }
++}
++
++struct MutPointerReturnerBad;
++
++impl MutPointerReturnerBad {
++ // should trigger lint
++ pub fn new() -> *mut V {
++ unimplemented!();
++ }
++}
++
++struct GenericReturnerOk;
++
++impl GenericReturnerOk {
++ // should not trigger lint
++ pub fn new() -> Option<Self> {
++ unimplemented!();
++ }
++}
++
++struct GenericReturnerBad;
++
++impl GenericReturnerBad {
++ // should trigger lint
++ pub fn new() -> Option<u32> {
++ unimplemented!();
++ }
++}
++
++struct NestedReturnerOk;
++
++impl NestedReturnerOk {
++ // should not trigger lint
++ pub fn new() -> (Option<Self>, u32) {
++ unimplemented!();
++ }
++}
++
++struct NestedReturnerOk2;
++
++impl NestedReturnerOk2 {
++ // should not trigger lint
++ pub fn new() -> ((Self, u32), u32) {
++ unimplemented!();
++ }
++}
++
++struct NestedReturnerOk3;
++
++impl NestedReturnerOk3 {
++ // should not trigger lint
++ pub fn new() -> Option<(Self, u32)> {
++ unimplemented!();
++ }
++}
++
++struct WithLifetime<'a> {
++ cat: &'a str,
++}
++
++impl<'a> WithLifetime<'a> {
++ // should not trigger the lint, because the lifetimes are different
++ pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> {
++ unimplemented!();
++ }
++}
--- /dev/null
--- /dev/null
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:49:5
++ |
++LL | / pub fn new(_: String) -> impl R<Item = u32> {
++LL | | S3
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
++
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:81:5
++ |
++LL | / pub fn new() -> u32 {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:90:5
++ |
++LL | / pub fn new(_: String) -> u32 {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:126:5
++ |
++LL | / pub fn new() -> (u32, u32) {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:153:5
++ |
++LL | / pub fn new() -> *mut V {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: methods called `new` usually return `Self`
++ --> $DIR/new_ret_no_self.rs:171:5
++ |
++LL | / pub fn new() -> Option<u32> {
++LL | | unimplemented!();
++LL | | }
++ | |_____^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(const_fn)]
++#![allow(dead_code, clippy::missing_safety_doc)]
++#![warn(clippy::new_without_default)]
++
++pub struct Foo;
++
++impl Foo {
++ pub fn new() -> Foo {
++ Foo
++ }
++}
++
++pub struct Bar;
++
++impl Bar {
++ pub fn new() -> Self {
++ Bar
++ }
++}
++
++pub struct Ok;
++
++impl Ok {
++ pub fn new() -> Self {
++ Ok
++ }
++}
++
++impl Default for Ok {
++ fn default() -> Self {
++ Ok
++ }
++}
++
++pub struct Params;
++
++impl Params {
++ pub fn new(_: u32) -> Self {
++ Params
++ }
++}
++
++pub struct GenericsOk<T> {
++ bar: T,
++}
++
++impl<U> Default for GenericsOk<U> {
++ fn default() -> Self {
++ unimplemented!();
++ }
++}
++
++impl<'c, V> GenericsOk<V> {
++ pub fn new() -> GenericsOk<V> {
++ unimplemented!()
++ }
++}
++
++pub struct LtOk<'a> {
++ foo: &'a bool,
++}
++
++impl<'b> Default for LtOk<'b> {
++ fn default() -> Self {
++ unimplemented!();
++ }
++}
++
++impl<'c> LtOk<'c> {
++ pub fn new() -> LtOk<'c> {
++ unimplemented!()
++ }
++}
++
++pub struct LtKo<'a> {
++ foo: &'a bool,
++}
++
++impl<'c> LtKo<'c> {
++ pub fn new() -> LtKo<'c> {
++ unimplemented!()
++ }
++ // FIXME: that suggestion is missing lifetimes
++}
++
++struct Private;
++
++impl Private {
++ fn new() -> Private {
++ unimplemented!()
++ } // We don't lint private items
++}
++
++struct Const;
++
++impl Const {
++ pub const fn new() -> Const {
++ Const
++ } // const fns can't be implemented via Default
++}
++
++pub struct IgnoreGenericNew;
++
++impl IgnoreGenericNew {
++ pub fn new<T>() -> Self {
++ IgnoreGenericNew
++ } // the derived Default does not make sense here as the result depends on T
++}
++
++pub trait TraitWithNew: Sized {
++ fn new() -> Self {
++ panic!()
++ }
++}
++
++pub struct IgnoreUnsafeNew;
++
++impl IgnoreUnsafeNew {
++ pub unsafe fn new() -> Self {
++ IgnoreUnsafeNew
++ }
++}
++
++#[derive(Default)]
++pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
++
++impl<'a, T> OptionRefWrapper<'a, T> {
++ pub fn new() -> Self {
++ OptionRefWrapper(None)
++ }
++}
++
++pub struct Allow(Foo);
++
++impl Allow {
++ #[allow(clippy::new_without_default)]
++ pub fn new() -> Self {
++ unimplemented!()
++ }
++}
++
++pub struct AllowDerive;
++
++impl AllowDerive {
++ #[allow(clippy::new_without_default)]
++ pub fn new() -> Self {
++ unimplemented!()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you should consider deriving a `Default` implementation for `Foo`
++ --> $DIR/new_without_default.rs:8:5
++ |
++LL | / pub fn new() -> Foo {
++LL | | Foo
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::new-without-default` implied by `-D warnings`
++help: try this
++ |
++LL | #[derive(Default)]
++ |
++
++error: you should consider deriving a `Default` implementation for `Bar`
++ --> $DIR/new_without_default.rs:16:5
++ |
++LL | / pub fn new() -> Self {
++LL | | Bar
++LL | | }
++ | |_____^
++ |
++help: try this
++ |
++LL | #[derive(Default)]
++ |
++
++error: you should consider adding a `Default` implementation for `LtKo<'c>`
++ --> $DIR/new_without_default.rs:80:5
++ |
++LL | / pub fn new() -> LtKo<'c> {
++LL | | unimplemented!()
++LL | | }
++ | |_____^
++ |
++help: try this
++ |
++LL | impl Default for LtKo<'c> {
++LL | fn default() -> Self {
++LL | Self::new()
++LL | }
++LL | }
++ |
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(box_syntax)]
++#![warn(clippy::no_effect)]
++#![allow(dead_code)]
++#![allow(path_statements)]
++#![allow(clippy::deref_addrof)]
++#![allow(clippy::redundant_field_names)]
++#![feature(untagged_unions)]
++
++struct Unit;
++struct Tuple(i32);
++struct Struct {
++ field: i32,
++}
++enum Enum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++struct DropUnit;
++impl Drop for DropUnit {
++ fn drop(&mut self) {}
++}
++struct DropStruct {
++ field: i32,
++}
++impl Drop for DropStruct {
++ fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++ fn drop(&mut self) {}
++}
++enum DropEnum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++impl Drop for DropEnum {
++ fn drop(&mut self) {}
++}
++struct FooString {
++ s: String,
++}
++union Union {
++ a: u8,
++ b: f64,
++}
++
++fn get_number() -> i32 {
++ 0
++}
++fn get_struct() -> Struct {
++ Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++ DropStruct { field: 0 }
++}
++
++unsafe fn unsafe_fn() -> i32 {
++ 0
++}
++
++fn main() {
++ let s = get_struct();
++ let s2 = get_struct();
++
++ 0;
++ s2;
++ Unit;
++ Tuple(0);
++ Struct { field: 0 };
++ Struct { ..s };
++ Union { a: 0 };
++ Enum::Tuple(0);
++ Enum::Struct { field: 0 };
++ 5 + 6;
++ *&42;
++ &6;
++ (5, 6, 7);
++ box 42;
++ ..;
++ 5..;
++ ..5;
++ 5..6;
++ 5..=6;
++ [42, 55];
++ [42, 55][1];
++ (42, 55).1;
++ [42; 55];
++ [42; 55][13];
++ let mut x = 0;
++ || x += 5;
++ let s: String = "foo".into();
++ FooString { s: s };
++
++ // Do not warn
++ get_number();
++ unsafe { unsafe_fn() };
++ DropUnit;
++ DropStruct { field: 0 };
++ DropTuple(0);
++ DropEnum::Tuple(0);
++ DropEnum::Struct { field: 0 };
++}
--- /dev/null
--- /dev/null
++error: statement with no effect
++ --> $DIR/no_effect.rs:65:5
++ |
++LL | 0;
++ | ^^
++ |
++ = note: `-D clippy::no-effect` implied by `-D warnings`
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:66:5
++ |
++LL | s2;
++ | ^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:67:5
++ |
++LL | Unit;
++ | ^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:68:5
++ |
++LL | Tuple(0);
++ | ^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:69:5
++ |
++LL | Struct { field: 0 };
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:70:5
++ |
++LL | Struct { ..s };
++ | ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:71:5
++ |
++LL | Union { a: 0 };
++ | ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:72:5
++ |
++LL | Enum::Tuple(0);
++ | ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:73:5
++ |
++LL | Enum::Struct { field: 0 };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:74:5
++ |
++LL | 5 + 6;
++ | ^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:75:5
++ |
++LL | *&42;
++ | ^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:76:5
++ |
++LL | &6;
++ | ^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:77:5
++ |
++LL | (5, 6, 7);
++ | ^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:78:5
++ |
++LL | box 42;
++ | ^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:79:5
++ |
++LL | ..;
++ | ^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:80:5
++ |
++LL | 5..;
++ | ^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:81:5
++ |
++LL | ..5;
++ | ^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:82:5
++ |
++LL | 5..6;
++ | ^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:84:5
++ |
++LL | [42, 55];
++ | ^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:85:5
++ |
++LL | [42, 55][1];
++ | ^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:86:5
++ |
++LL | (42, 55).1;
++ | ^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:87:5
++ |
++LL | [42; 55];
++ | ^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:88:5
++ |
++LL | [42; 55][13];
++ | ^^^^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:90:5
++ |
++LL | || x += 5;
++ | ^^^^^^^^^^
++
++error: statement with no effect
++ --> $DIR/no_effect.rs:92:5
++ |
++LL | FooString { s: s };
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 25 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++#![allow(unused, clippy::println_empty_string)]
++
++#[derive(Clone, Debug)]
++enum MaybeInst {
++ Split,
++ Split1(usize),
++ Split2(usize),
++}
++
++struct InstSplit {
++ uiae: usize,
++}
++
++impl MaybeInst {
++ fn fill(&mut self) {
++ let filled = match *self {
++ MaybeInst::Split1(goto1) => panic!(1),
++ MaybeInst::Split2(goto2) => panic!(2),
++ _ => unimplemented!(),
++ };
++ unimplemented!()
++ }
++}
++
++fn underscores_and_numbers() {
++ let _1 = 1; //~ERROR Consider a more descriptive name
++ let ____1 = 1; //~ERROR Consider a more descriptive name
++ let __1___2 = 12; //~ERROR Consider a more descriptive name
++ let _1_ok = 1;
++}
++
++fn issue2927() {
++ let args = 1;
++ format!("{:?}", 2);
++}
++
++fn issue3078() {
++ match "a" {
++ stringify!(a) => {},
++ _ => {},
++ }
++}
++
++struct Bar;
++
++impl Bar {
++ fn bar() {
++ let _1 = 1;
++ let ____1 = 1;
++ let __1___2 = 12;
++ let _1_ok = 1;
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:27:9
++ |
++LL | let _1 = 1; //~ERROR Consider a more descriptive name
++ | ^^
++ |
++ = note: `-D clippy::just-underscores-and-digits` implied by `-D warnings`
++
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:28:9
++ |
++LL | let ____1 = 1; //~ERROR Consider a more descriptive name
++ | ^^^^^
++
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:29:9
++ |
++LL | let __1___2 = 12; //~ERROR Consider a more descriptive name
++ | ^^^^^^^
++
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:49:13
++ |
++LL | let _1 = 1;
++ | ^^
++
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:50:13
++ |
++LL | let ____1 = 1;
++ | ^^^^^
++
++error: consider choosing a more descriptive name
++ --> $DIR/non_expressive_names.rs:51:13
++ |
++LL | let __1___2 = 12;
++ | ^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::nonminimal_bool)]
++
++fn main() {
++ let a: bool = unimplemented!();
++ let b: bool = unimplemented!();
++ let c: bool = unimplemented!();
++ let d: bool = unimplemented!();
++ let e: bool = unimplemented!();
++ let _ = !true;
++ let _ = !false;
++ let _ = !!a;
++ let _ = false || a;
++ // don't lint on cfgs
++ let _ = cfg!(you_shall_not_not_pass) && a;
++ let _ = a || !b || !c || !d || !e;
++ let _ = !(!a && b);
++ let _ = !(!a || b);
++ let _ = !a && !(b && c);
++}
++
++fn equality_stuff() {
++ let a: i32 = unimplemented!();
++ let b: i32 = unimplemented!();
++ let c: i32 = unimplemented!();
++ let d: i32 = unimplemented!();
++ let _ = a == b && c == 5 && a == b;
++ let _ = a == b || c == 5 || a == b;
++ let _ = a == b && c == 5 && b == a;
++ let _ = a != b || !(a != b || c == d);
++ let _ = a != b && !(a != b && c == d);
++}
++
++fn issue3847(a: u32, b: u32) -> bool {
++ const THRESHOLD: u32 = 1_000;
++
++ if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD {
++ return false;
++ }
++ true
++}
++
++fn issue4548() {
++ fn f(_i: u32, _j: u32) -> u32 {
++ unimplemented!();
++ }
++
++ let i = 0;
++ let j = 0;
++
++ if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
++}
--- /dev/null
--- /dev/null
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:10:13
++ |
++LL | let _ = !true;
++ | ^^^^^ help: try: `false`
++ |
++ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:11:13
++ |
++LL | let _ = !false;
++ | ^^^^^^ help: try: `true`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:12:13
++ |
++LL | let _ = !!a;
++ | ^^^ help: try: `a`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:13:13
++ |
++LL | let _ = false || a;
++ | ^^^^^^^^^^ help: try: `a`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:17:13
++ |
++LL | let _ = !(!a && b);
++ | ^^^^^^^^^^ help: try: `a || !b`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:18:13
++ |
++LL | let _ = !(!a || b);
++ | ^^^^^^^^^^ help: try: `a && !b`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:19:13
++ |
++LL | let _ = !a && !(b && c);
++ | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:27:13
++ |
++LL | let _ = a == b && c == 5 && a == b;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | let _ = a == b && c == 5;
++ | ^^^^^^^^^^^^^^^^
++LL | let _ = !(a != b || c != 5);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:28:13
++ |
++LL | let _ = a == b || c == 5 || a == b;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | let _ = a == b || c == 5;
++ | ^^^^^^^^^^^^^^^^
++LL | let _ = !(a != b && c != 5);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:29:13
++ |
++LL | let _ = a == b && c == 5 && b == a;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | let _ = a == b && c == 5;
++ | ^^^^^^^^^^^^^^^^
++LL | let _ = !(a != b || c != 5);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:30:13
++ |
++LL | let _ = a != b || !(a != b || c == d);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | let _ = a != b || c != d;
++ | ^^^^^^^^^^^^^^^^
++LL | let _ = !(a == b && c == d);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool.rs:31:13
++ |
++LL | let _ = a != b && !(a != b && c == d);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: try
++ |
++LL | let _ = a != b && c != d;
++ | ^^^^^^^^^^^^^^^^
++LL | let _ = !(a == b || c == d);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::nonminimal_bool)]
++
++fn methods_with_negation() {
++ let a: Option<i32> = unimplemented!();
++ let b: Result<i32, i32> = unimplemented!();
++ let _ = a.is_some();
++ let _ = !a.is_some();
++ let _ = a.is_none();
++ let _ = !a.is_none();
++ let _ = b.is_err();
++ let _ = !b.is_err();
++ let _ = b.is_ok();
++ let _ = !b.is_ok();
++ let c = false;
++ let _ = !(a.is_some() && !c);
++ let _ = !(a.is_some() || !c);
++ let _ = !(!c ^ c) || !a.is_some();
++ let _ = (!c ^ c) || !a.is_some();
++ let _ = !c ^ c || !a.is_some();
++}
++
++// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
++// clippy::nonminimal_bool should only check the built-in Result and Some type, not
++// any other types like the following.
++enum CustomResultOk<E> {
++ Ok,
++ Err(E),
++}
++enum CustomResultErr<E> {
++ Ok,
++ Err(E),
++}
++enum CustomSomeSome<T> {
++ Some(T),
++ None,
++}
++enum CustomSomeNone<T> {
++ Some(T),
++ None,
++}
++
++impl<E> CustomResultOk<E> {
++ pub fn is_ok(&self) -> bool {
++ true
++ }
++}
++
++impl<E> CustomResultErr<E> {
++ pub fn is_err(&self) -> bool {
++ true
++ }
++}
++
++impl<T> CustomSomeSome<T> {
++ pub fn is_some(&self) -> bool {
++ true
++ }
++}
++
++impl<T> CustomSomeNone<T> {
++ pub fn is_none(&self) -> bool {
++ true
++ }
++}
++
++fn dont_warn_for_custom_methods_with_negation() {
++ let res = CustomResultOk::Err("Error");
++ // Should not warn and suggest 'is_err()' because the type does not
++ // implement is_err().
++ if !res.is_ok() {}
++
++ let res = CustomResultErr::Err("Error");
++ // Should not warn and suggest 'is_ok()' because the type does not
++ // implement is_ok().
++ if !res.is_err() {}
++
++ let res = CustomSomeSome::Some("thing");
++ // Should not warn and suggest 'is_none()' because the type does not
++ // implement is_none().
++ if !res.is_some() {}
++
++ let res = CustomSomeNone::Some("thing");
++ // Should not warn and suggest 'is_some()' because the type does not
++ // implement is_some().
++ if !res.is_none() {}
++}
++
++// Only Built-in Result and Some types should suggest the negated alternative
++fn warn_for_built_in_methods_with_negation() {
++ let res: Result<usize, usize> = Ok(1);
++ if !res.is_ok() {}
++ if !res.is_err() {}
++
++ let res = Some(1);
++ if !res.is_some() {}
++ if !res.is_none() {}
++}
++
++#[allow(clippy::neg_cmp_op_on_partial_ord)]
++fn dont_warn_for_negated_partial_ord_comparison() {
++ let a: f64 = unimplemented!();
++ let b: f64 = unimplemented!();
++ let _ = !(a < b);
++ let _ = !(a <= b);
++ let _ = !(a > b);
++ let _ = !(a >= b);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:8:13
++ |
++LL | let _ = !a.is_some();
++ | ^^^^^^^^^^^^ help: try: `a.is_none()`
++ |
++ = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:10:13
++ |
++LL | let _ = !a.is_none();
++ | ^^^^^^^^^^^^ help: try: `a.is_some()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:12:13
++ |
++LL | let _ = !b.is_err();
++ | ^^^^^^^^^^^ help: try: `b.is_ok()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:14:13
++ |
++LL | let _ = !b.is_ok();
++ | ^^^^^^^^^^ help: try: `b.is_err()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:16:13
++ |
++LL | let _ = !(a.is_some() && !c);
++ | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:17:13
++ |
++LL | let _ = !(a.is_some() || !c);
++ | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:18:26
++ |
++LL | let _ = !(!c ^ c) || !a.is_some();
++ | ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:19:25
++ |
++LL | let _ = (!c ^ c) || !a.is_some();
++ | ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:20:23
++ |
++LL | let _ = !c ^ c || !a.is_some();
++ | ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:92:8
++ |
++LL | if !res.is_ok() {}
++ | ^^^^^^^^^^^^ help: try: `res.is_err()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:93:8
++ |
++LL | if !res.is_err() {}
++ | ^^^^^^^^^^^^^ help: try: `res.is_ok()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:96:8
++ |
++LL | if !res.is_some() {}
++ | ^^^^^^^^^^^^^^ help: try: `res.is_none()`
++
++error: this boolean expression can be simplified
++ --> $DIR/nonminimal_bool_methods.rs:97:8
++ |
++LL | if !res.is_none() {}
++ | ^^^^^^^^^^^^^^ help: try: `res.is_some()`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++use std::io;
++
++struct MyError(()); // doesn't implement Debug
++
++#[derive(Debug)]
++struct MyErrorWithParam<T> {
++ x: T,
++}
++
++fn main() {
++ let res: Result<i32, ()> = Ok(0);
++ let _ = res.unwrap();
++
++ res.ok().expect("disaster!");
++ // the following should not warn, since `expect` isn't implemented unless
++ // the error type implements `Debug`
++ let res2: Result<i32, MyError> = Ok(0);
++ res2.ok().expect("oh noes!");
++ let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0);
++ res3.ok().expect("whoof");
++ let res4: Result<u32, io::Error> = Ok(0);
++ res4.ok().expect("argh");
++ let res5: io::Result<u32> = Ok(0);
++ res5.ok().expect("oops");
++ let res6: Result<u32, &str> = Ok(0);
++ res6.ok().expect("meh");
++}
--- /dev/null
--- /dev/null
++error: called `ok().expect()` on a `Result` value
++ --> $DIR/ok_expect.rs:14:5
++ |
++LL | res.ok().expect("disaster!");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::ok-expect` implied by `-D warnings`
++ = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++ --> $DIR/ok_expect.rs:20:5
++ |
++LL | res3.ok().expect("whoof");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++ --> $DIR/ok_expect.rs:22:5
++ |
++LL | res4.ok().expect("argh");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++ --> $DIR/ok_expect.rs:24:5
++ |
++LL | res5.ok().expect("oops");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++ --> $DIR/ok_expect.rs:26:5
++ |
++LL | res6.ok().expect("meh");
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: you can call `expect()` directly on the `Result`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused_variables, clippy::blacklisted_name)]
++#![warn(clippy::op_ref)]
++#![allow(clippy::many_single_char_names)]
++use std::collections::HashSet;
++use std::ops::BitAnd;
++
++fn main() {
++ let tracked_fds: HashSet<i32> = HashSet::new();
++ let new_fds = HashSet::new();
++ let unwanted = &tracked_fds - &new_fds;
++
++ let foo = &5 - &6;
++
++ let bar = String::new();
++ let bar = "foo" == &bar;
++
++ let a = "a".to_string();
++ let b = "a";
++
++ if b < &a {
++ println!("OK");
++ }
++
++ struct X(i32);
++ impl BitAnd for X {
++ type Output = X;
++ fn bitand(self, rhs: X) -> X {
++ X(self.0 & rhs.0)
++ }
++ }
++ impl<'a> BitAnd<&'a X> for X {
++ type Output = X;
++ fn bitand(self, rhs: &'a X) -> X {
++ X(self.0 & rhs.0)
++ }
++ }
++ let x = X(1);
++ let y = X(2);
++ let z = x & &y;
++
++ #[derive(Copy, Clone)]
++ struct Y(i32);
++ impl BitAnd for Y {
++ type Output = Y;
++ fn bitand(self, rhs: Y) -> Y {
++ Y(self.0 & rhs.0)
++ }
++ }
++ impl<'a> BitAnd<&'a Y> for Y {
++ type Output = Y;
++ fn bitand(self, rhs: &'a Y) -> Y {
++ Y(self.0 & rhs.0)
++ }
++ }
++ let x = Y(1);
++ let y = Y(2);
++ let z = x & &y;
++}
--- /dev/null
--- /dev/null
++error: needlessly taken reference of both operands
++ --> $DIR/op_ref.rs:12:15
++ |
++LL | let foo = &5 - &6;
++ | ^^^^^^^
++ |
++ = note: `-D clippy::op-ref` implied by `-D warnings`
++help: use the values directly
++ |
++LL | let foo = 5 - 6;
++ | ^ ^
++
++error: taken reference of right operand
++ --> $DIR/op_ref.rs:57:13
++ |
++LL | let z = x & &y;
++ | ^^^^--
++ | |
++ | help: use the right value directly: `y`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++use std::fs::OpenOptions;
++
++#[allow(unused_must_use)]
++#[warn(clippy::nonsensical_open_options)]
++fn main() {
++ OpenOptions::new().read(true).truncate(true).open("foo.txt");
++ OpenOptions::new().append(true).truncate(true).open("foo.txt");
++
++ OpenOptions::new().read(true).read(false).open("foo.txt");
++ OpenOptions::new().create(true).create(false).open("foo.txt");
++ OpenOptions::new().write(true).write(false).open("foo.txt");
++ OpenOptions::new().append(true).append(false).open("foo.txt");
++ OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
++}
--- /dev/null
--- /dev/null
++error: file opened with `truncate` and `read`
++ --> $DIR/open_options.rs:6:5
++ |
++LL | OpenOptions::new().read(true).truncate(true).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::nonsensical-open-options` implied by `-D warnings`
++
++error: file opened with `append` and `truncate`
++ --> $DIR/open_options.rs:7:5
++ |
++LL | OpenOptions::new().append(true).truncate(true).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `read` is called more than once
++ --> $DIR/open_options.rs:9:5
++ |
++LL | OpenOptions::new().read(true).read(false).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `create` is called more than once
++ --> $DIR/open_options.rs:10:5
++ |
++LL | OpenOptions::new().create(true).create(false).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `write` is called more than once
++ --> $DIR/open_options.rs:11:5
++ |
++LL | OpenOptions::new().write(true).write(false).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `append` is called more than once
++ --> $DIR/open_options.rs:12:5
++ |
++LL | OpenOptions::new().append(true).append(false).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `truncate` is called more than once
++ --> $DIR/open_options.rs:13:5
++ |
++LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::option_and_then_some)]
++
++// need a main anyway, use it get rid of unused warnings too
++pub fn main() {
++ let x = Some(5);
++ // the easiest cases
++ let _ = x;
++ let _ = x.map(|o| o + 1);
++ // and an easy counter-example
++ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
++
++ // Different type
++ let x: Result<u32, &str> = Ok(1);
++ let _ = x.and_then(Ok);
++}
++
++pub fn foo() -> Option<String> {
++ let x = Some(String::from("hello"));
++ Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
++}
++
++pub fn example2(x: bool) -> Option<&'static str> {
++ Some("a").and_then(|s| Some(if x { s } else { return None }))
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![deny(clippy::option_and_then_some)]
++
++// need a main anyway, use it get rid of unused warnings too
++pub fn main() {
++ let x = Some(5);
++ // the easiest cases
++ let _ = x.and_then(Some);
++ let _ = x.and_then(|o| Some(o + 1));
++ // and an easy counter-example
++ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
++
++ // Different type
++ let x: Result<u32, &str> = Ok(1);
++ let _ = x.and_then(Ok);
++}
++
++pub fn foo() -> Option<String> {
++ let x = Some(String::from("hello"));
++ Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
++}
++
++pub fn example2(x: bool) -> Option<&'static str> {
++ Some("a").and_then(|s| Some(if x { s } else { return None }))
++}
--- /dev/null
--- /dev/null
++error: using `Option.and_then(Some)`, which is a no-op
++ --> $DIR/option_and_then_some.rs:8:13
++ |
++LL | let _ = x.and_then(Some);
++ | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x`
++ |
++note: the lint level is defined here
++ --> $DIR/option_and_then_some.rs:2:9
++ |
++LL | #![deny(clippy::option_and_then_some)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
++ --> $DIR/option_and_then_some.rs:9:13
++ |
++LL | let _ = x.and_then(|o| Some(o + 1));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports, clippy::redundant_clone)]
++#![warn(clippy::option_as_ref_deref)]
++
++use std::ffi::{CString, OsString};
++use std::ops::{Deref, DerefMut};
++use std::path::PathBuf;
++
++fn main() {
++ let mut opt = Some(String::from("123"));
++
++ let _ = opt.clone().as_deref().map(str::len);
++
++ #[rustfmt::skip]
++ let _ = opt.clone().as_deref()
++ .map(str::len);
++
++ let _ = opt.as_deref_mut();
++
++ let _ = opt.as_deref();
++ let _ = opt.as_deref();
++ let _ = opt.as_deref_mut();
++ let _ = opt.as_deref_mut();
++ let _ = Some(CString::new(vec![]).unwrap()).as_deref();
++ let _ = Some(OsString::new()).as_deref();
++ let _ = Some(PathBuf::new()).as_deref();
++ let _ = Some(Vec::<()>::new()).as_deref();
++ let _ = Some(Vec::<()>::new()).as_deref_mut();
++
++ let _ = opt.as_deref();
++ let _ = opt.clone().as_deref_mut().map(|x| x.len());
++
++ let vc = vec![String::new()];
++ let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
++
++ let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
++
++ let _ = opt.as_deref();
++ let _ = opt.as_deref_mut();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports, clippy::redundant_clone)]
++#![warn(clippy::option_as_ref_deref)]
++
++use std::ffi::{CString, OsString};
++use std::ops::{Deref, DerefMut};
++use std::path::PathBuf;
++
++fn main() {
++ let mut opt = Some(String::from("123"));
++
++ let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
++
++ #[rustfmt::skip]
++ let _ = opt.clone()
++ .as_ref().map(
++ Deref::deref
++ )
++ .map(str::len);
++
++ let _ = opt.as_mut().map(DerefMut::deref_mut);
++
++ let _ = opt.as_ref().map(String::as_str);
++ let _ = opt.as_ref().map(|x| x.as_str());
++ let _ = opt.as_mut().map(String::as_mut_str);
++ let _ = opt.as_mut().map(|x| x.as_mut_str());
++ let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
++ let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
++ let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
++ let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
++ let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
++
++ let _ = opt.as_ref().map(|x| x.deref());
++ let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
++
++ let vc = vec![String::new()];
++ let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
++
++ let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
++
++ let _ = opt.as_ref().map(|x| &**x);
++ let _ = opt.as_mut().map(|x| &mut **x);
++}
--- /dev/null
--- /dev/null
++error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:13:13
++ |
++LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
++ |
++ = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
++
++error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:16:13
++ |
++LL | let _ = opt.clone()
++ | _____________^
++LL | | .as_ref().map(
++LL | | Deref::deref
++LL | | )
++ | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
++
++error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:22:13
++ |
++LL | let _ = opt.as_mut().map(DerefMut::deref_mut);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:24:13
++ |
++LL | let _ = opt.as_ref().map(String::as_str);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:25:13
++ |
++LL | let _ = opt.as_ref().map(|x| x.as_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:26:13
++ |
++LL | let _ = opt.as_mut().map(String::as_mut_str);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:27:13
++ |
++LL | let _ = opt.as_mut().map(|x| x.as_mut_str());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:28:13
++ |
++LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
++
++error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:29:13
++ |
++LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
++
++error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:30:13
++ |
++LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
++
++error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:31:13
++ |
++LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
++
++error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:32:13
++ |
++LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
++
++error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:34:13
++ |
++LL | let _ = opt.as_ref().map(|x| x.deref());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:35:13
++ |
++LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
++
++error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++ --> $DIR/option_as_ref_deref.rs:42:13
++ |
++LL | let _ = opt.as_ref().map(|x| &**x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++ --> $DIR/option_as_ref_deref.rs:43:13
++ |
++LL | let _ = opt.as_mut().map(|x| &mut **x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: aborting due to 16 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:macro_rules.rs
++#![warn(clippy::option_env_unwrap)]
++
++#[macro_use]
++extern crate macro_rules;
++
++macro_rules! option_env_unwrap {
++ ($env: expr) => {
++ option_env!($env).unwrap()
++ };
++ ($env: expr, $message: expr) => {
++ option_env!($env).expect($message)
++ };
++}
++
++fn main() {
++ let _ = option_env!("PATH").unwrap();
++ let _ = option_env!("PATH").expect("environment variable PATH isn't set");
++ let _ = option_env_unwrap!("PATH");
++ let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
++ let _ = option_env_unwrap_external!("PATH");
++ let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
++}
--- /dev/null
--- /dev/null
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:17:13
++ |
++LL | let _ = option_env!("PATH").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::option-env-unwrap` implied by `-D warnings`
++ = help: consider using the `env!` macro instead
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:18:13
++ |
++LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using the `env!` macro instead
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:9:9
++ |
++LL | option_env!($env).unwrap()
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | let _ = option_env_unwrap!("PATH");
++ | -------------------------- in this macro invocation
++ |
++ = help: consider using the `env!` macro instead
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:12:9
++ |
++LL | option_env!($env).expect($message)
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
++ | ----------------------------------------------------------------- in this macro invocation
++ |
++ = help: consider using the `env!` macro instead
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:21:13
++ |
++LL | let _ = option_env_unwrap_external!("PATH");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using the `env!` macro instead
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++ --> $DIR/option_env_unwrap.rs:22:13
++ |
++LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using the `env!` macro instead
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::option_and_then_some)]
++
++fn main() {
++ let opt = Some(1);
++
++ // Check `OPTION_MAP_OR_NONE`.
++ // Single line case.
++ let _ = opt.and_then(|x| Some(x + 1));
++ // Multi-line case.
++ #[rustfmt::skip]
++ let _ = opt.and_then(|x| {
++ Some(x + 1)
++ });
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(clippy::option_and_then_some)]
++
++fn main() {
++ let opt = Some(1);
++
++ // Check `OPTION_MAP_OR_NONE`.
++ // Single line case.
++ let _ = opt.map_or(None, |x| Some(x + 1));
++ // Multi-line case.
++ #[rustfmt::skip]
++ let _ = opt.map_or(None, |x| {
++ Some(x + 1)
++ });
++}
--- /dev/null
--- /dev/null
++error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++ --> $DIR/option_map_or_none.rs:10:13
++ |
++LL | let _ = opt.map_or(None, |x| Some(x + 1));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))`
++ |
++ = note: `-D clippy::option-map-or-none` implied by `-D warnings`
++
++error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++ --> $DIR/option_map_or_none.rs:13:13
++ |
++LL | let _ = opt.map_or(None, |x| {
++ | _____________^
++LL | | Some(x + 1)
++LL | | });
++ | |_________________________^
++ |
++help: try using `and_then` instead
++ |
++LL | let _ = opt.and_then(|x| {
++LL | Some(x + 1)
++LL | });
++ |
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++fn option() -> Option<usize> {
++ Some(10)
++}
++
++struct HasOption {
++ field: Option<usize>,
++}
++
++impl HasOption {
++ fn do_option_nothing(self: &Self, value: usize) {}
++
++ fn do_option_plus_one(self: &Self, value: usize) -> usize {
++ value + 1
++ }
++}
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++ let x = HasOption { field: Some(10) };
++
++ x.field.map(plus_one);
++ let _ : Option<()> = x.field.map(do_nothing);
++
++ if let Some(x_field) = x.field { do_nothing(x_field) }
++
++ if let Some(x_field) = x.field { do_nothing(x_field) }
++
++ if let Some(x_field) = x.field { diverge(x_field) }
++
++ let captured = 10;
++ if let Some(value) = x.field { do_nothing(value + captured) };
++ let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
++
++ if let Some(value) = x.field { x.do_option_nothing(value + captured) }
++
++ if let Some(value) = x.field { x.do_option_plus_one(value + captured); }
++
++
++ if let Some(value) = x.field { do_nothing(value + captured) }
++
++ if let Some(value) = x.field { do_nothing(value + captured) }
++
++ if let Some(value) = x.field { do_nothing(value + captured); }
++
++ if let Some(value) = x.field { do_nothing(value + captured); }
++
++
++ if let Some(value) = x.field { diverge(value + captured) }
++
++ if let Some(value) = x.field { diverge(value + captured) }
++
++ if let Some(value) = x.field { diverge(value + captured); }
++
++ if let Some(value) = x.field { diverge(value + captured); }
++
++
++ x.field.map(|value| plus_one(value + captured));
++ x.field.map(|value| { plus_one(value + captured) });
++ if let Some(value) = x.field { let y = plus_one(value + captured); }
++
++ if let Some(value) = x.field { plus_one(value + captured); }
++
++ if let Some(value) = x.field { plus_one(value + captured); }
++
++
++ if let Some(ref value) = x.field { do_nothing(value + captured) }
++
++ if let Some(a) = option() { do_nothing(a) }}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++fn option() -> Option<usize> {
++ Some(10)
++}
++
++struct HasOption {
++ field: Option<usize>,
++}
++
++impl HasOption {
++ fn do_option_nothing(self: &Self, value: usize) {}
++
++ fn do_option_plus_one(self: &Self, value: usize) -> usize {
++ value + 1
++ }
++}
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++ let x = HasOption { field: Some(10) };
++
++ x.field.map(plus_one);
++ let _ : Option<()> = x.field.map(do_nothing);
++
++ x.field.map(do_nothing);
++
++ x.field.map(do_nothing);
++
++ x.field.map(diverge);
++
++ let captured = 10;
++ if let Some(value) = x.field { do_nothing(value + captured) };
++ let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
++
++ x.field.map(|value| x.do_option_nothing(value + captured));
++
++ x.field.map(|value| { x.do_option_plus_one(value + captured); });
++
++
++ x.field.map(|value| do_nothing(value + captured));
++
++ x.field.map(|value| { do_nothing(value + captured) });
++
++ x.field.map(|value| { do_nothing(value + captured); });
++
++ x.field.map(|value| { { do_nothing(value + captured); } });
++
++
++ x.field.map(|value| diverge(value + captured));
++
++ x.field.map(|value| { diverge(value + captured) });
++
++ x.field.map(|value| { diverge(value + captured); });
++
++ x.field.map(|value| { { diverge(value + captured); } });
++
++
++ x.field.map(|value| plus_one(value + captured));
++ x.field.map(|value| { plus_one(value + captured) });
++ x.field.map(|value| { let y = plus_one(value + captured); });
++
++ x.field.map(|value| { plus_one(value + captured); });
++
++ x.field.map(|value| { { plus_one(value + captured); } });
++
++
++ x.field.map(|ref value| { do_nothing(value + captured) });
++
++ option().map(do_nothing);}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:38:5
++ |
++LL | x.field.map(do_nothing);
++ | ^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
++ |
++ = note: `-D clippy::option-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:40:5
++ |
++LL | x.field.map(do_nothing);
++ | ^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:42:5
++ |
++LL | x.field.map(diverge);
++ | ^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:48:5
++ |
++LL | x.field.map(|value| x.do_option_nothing(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:50:5
++ |
++LL | x.field.map(|value| { x.do_option_plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:53:5
++ |
++LL | x.field.map(|value| do_nothing(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:55:5
++ |
++LL | x.field.map(|value| { do_nothing(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:57:5
++ |
++LL | x.field.map(|value| { do_nothing(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:59:5
++ |
++LL | x.field.map(|value| { { do_nothing(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:62:5
++ |
++LL | x.field.map(|value| diverge(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:64:5
++ |
++LL | x.field.map(|value| { diverge(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:66:5
++ |
++LL | x.field.map(|value| { diverge(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:68:5
++ |
++LL | x.field.map(|value| { { diverge(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:73:5
++ |
++LL | x.field.map(|value| { let y = plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:75:5
++ |
++LL | x.field.map(|value| { plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:77:5
++ |
++LL | x.field.map(|value| { { plus_one(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:80:5
++ |
++LL | x.field.map(|ref value| { do_nothing(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++ --> $DIR/option_map_unit_fn_fixable.rs:82:5
++ |
++LL | option().map(do_nothing);}
++ | ^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Some(a) = option() { do_nothing(a) }`
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++
++ x.field.map(|value| { do_nothing(value); do_nothing(value) });
++
++ x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++
++ // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
++ // proper suggestion for these cases
++ x.field.map(|value| {
++ do_nothing(value);
++ do_nothing(value)
++ });
++ x.field.map(|value| { do_nothing(value); do_nothing(value); });
++
++ // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them
++ Some(42).map(diverge);
++ "12".parse::<i32>().ok().map(diverge);
++ Some(plus_one(1)).map(do_nothing);
++
++ // Should suggest `if let Some(_y) ...` to not override the existing foo variable
++ let y = Some(42);
++ y.map(do_nothing);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error[E0425]: cannot find value `x` in this scope
++ --> $DIR/option_map_unit_fn_unfixable.rs:17:5
++ |
++LL | x.field.map(|value| { do_nothing(value); do_nothing(value) });
++ | ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++ --> $DIR/option_map_unit_fn_unfixable.rs:19:5
++ |
++LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++ | ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++ --> $DIR/option_map_unit_fn_unfixable.rs:23:5
++ |
++LL | x.field.map(|value| {
++ | ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++ --> $DIR/option_map_unit_fn_unfixable.rs:27:5
++ |
++LL | x.field.map(|value| { do_nothing(value); do_nothing(value); });
++ | ^ not found in this scope
++
++error: aborting due to 4 previous errors
++
++For more information about this error, try `rustc --explain E0425`.
--- /dev/null
--- /dev/null
++// FIXME: Add "run-rustfix" once it's supported for multipart suggestions
++// aux-build:option_helpers.rs
++
++#![warn(clippy::option_map_unwrap_or, clippy::option_map_unwrap_or_else)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use std::collections::HashMap;
++
++/// Checks implementation of the following lints:
++/// * `OPTION_MAP_UNWRAP_OR`
++/// * `OPTION_MAP_UNWRAP_OR_ELSE`
++#[rustfmt::skip]
++fn option_methods() {
++ let opt = Some(1);
++
++ // Check `OPTION_MAP_UNWRAP_OR`.
++ // Single line case.
++ let _ = opt.map(|x| x + 1)
++ // Should lint even though this call is on a separate line.
++ .unwrap_or(0);
++ // Multi-line cases.
++ let _ = opt.map(|x| {
++ x + 1
++ }
++ ).unwrap_or(0);
++ let _ = opt.map(|x| x + 1)
++ .unwrap_or({
++ 0
++ });
++ // Single line `map(f).unwrap_or(None)` case.
++ let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
++ // Multi-line `map(f).unwrap_or(None)` cases.
++ let _ = opt.map(|x| {
++ Some(x + 1)
++ }
++ ).unwrap_or(None);
++ let _ = opt
++ .map(|x| Some(x + 1))
++ .unwrap_or(None);
++ // macro case
++ let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
++
++ // Should not lint if not copyable
++ let id: String = "identifier".to_string();
++ let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
++ // ...but DO lint if the `unwrap_or` argument is not used in the `map`
++ let id: String = "identifier".to_string();
++ let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
++
++ // Check OPTION_MAP_UNWRAP_OR_ELSE
++ // single line case
++ let _ = opt.map(|x| x + 1)
++ // Should lint even though this call is on a separate line.
++ .unwrap_or_else(|| 0);
++ // Multi-line cases.
++ let _ = opt.map(|x| {
++ x + 1
++ }
++ ).unwrap_or_else(|| 0);
++ let _ = opt.map(|x| x + 1)
++ .unwrap_or_else(||
++ 0
++ );
++ // Macro case.
++ // Should not lint.
++ let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
++
++ // Issue #4144
++ {
++ let mut frequencies = HashMap::new();
++ let word = "foo";
++
++ frequencies
++ .get_mut(word)
++ .map(|count| {
++ *count += 1;
++ })
++ .unwrap_or_else(|| {
++ frequencies.insert(word.to_owned(), 1);
++ });
++ }
++}
++
++fn main() {
++ option_methods();
++}
--- /dev/null
--- /dev/null
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:20:13
++ |
++LL | let _ = opt.map(|x| x + 1)
++ | _____________^
++LL | | // Should lint even though this call is on a separate line.
++LL | | .unwrap_or(0);
++ | |_____________________^
++ |
++ = note: `-D clippy::option-map-unwrap-or` implied by `-D warnings`
++help: use `map_or(a, f)` instead
++ |
++LL | let _ = opt.map_or(0, |x| x + 1);
++ | ^^^^^^ ^^ --
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:24:13
++ |
++LL | let _ = opt.map(|x| {
++ | _____________^
++LL | | x + 1
++LL | | }
++LL | | ).unwrap_or(0);
++ | |__________________^
++ |
++help: use `map_or(a, f)` instead
++ |
++LL | let _ = opt.map_or(0, |x| {
++LL | x + 1
++LL | }
++LL | );
++ |
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:28:13
++ |
++LL | let _ = opt.map(|x| x + 1)
++ | _____________^
++LL | | .unwrap_or({
++LL | | 0
++LL | | });
++ | |__________^
++ |
++help: use `map_or(a, f)` instead
++ |
++LL | let _ = opt.map_or({
++LL | 0
++LL | }, |x| x + 1);
++ |
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++ --> $DIR/option_map_unwrap_or.rs:33:13
++ |
++LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `and_then(f)` instead
++ |
++LL | let _ = opt.and_then(|x| Some(x + 1));
++ | ^^^^^^^^ --
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++ --> $DIR/option_map_unwrap_or.rs:35:13
++ |
++LL | let _ = opt.map(|x| {
++ | _____________^
++LL | | Some(x + 1)
++LL | | }
++LL | | ).unwrap_or(None);
++ | |_____________________^
++ |
++help: use `and_then(f)` instead
++ |
++LL | let _ = opt.and_then(|x| {
++LL | Some(x + 1)
++LL | }
++LL | );
++ |
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++ --> $DIR/option_map_unwrap_or.rs:39:13
++ |
++LL | let _ = opt
++ | _____________^
++LL | | .map(|x| Some(x + 1))
++LL | | .unwrap_or(None);
++ | |________________________^
++ |
++help: use `and_then(f)` instead
++ |
++LL | .and_then(|x| Some(x + 1));
++ | ^^^^^^^^ --
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:50:13
++ |
++LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `map_or(a, f)` instead
++ |
++LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
++ | ^^^^^^ ^^^ --
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:54:13
++ |
++LL | let _ = opt.map(|x| x + 1)
++ | _____________^
++LL | | // Should lint even though this call is on a separate line.
++LL | | .unwrap_or_else(|| 0);
++ | |_____________________________^
++ |
++ = note: `-D clippy::option-map-unwrap-or-else` implied by `-D warnings`
++ = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:58:13
++ |
++LL | let _ = opt.map(|x| {
++ | _____________^
++LL | | x + 1
++LL | | }
++LL | | ).unwrap_or_else(|| 0);
++ | |__________________________^
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++ --> $DIR/option_map_unwrap_or.rs:62:13
++ |
++LL | let _ = opt.map(|x| x + 1)
++ | _____________^
++LL | | .unwrap_or_else(||
++LL | | 0
++LL | | );
++ | |_________^
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::option_option)]
++
++fn input(_: Option<Option<u8>>) {}
++
++fn output() -> Option<Option<u8>> {
++ None
++}
++
++fn output_nested() -> Vec<Option<Option<u8>>> {
++ vec![None]
++}
++
++// The lint only generates one warning for this
++fn output_nested_nested() -> Option<Option<Option<u8>>> {
++ None
++}
++
++struct Struct {
++ x: Option<Option<u8>>,
++}
++
++impl Struct {
++ fn struct_fn() -> Option<Option<u8>> {
++ None
++ }
++}
++
++trait Trait {
++ fn trait_fn() -> Option<Option<u8>>;
++}
++
++enum Enum {
++ Tuple(Option<Option<u8>>),
++ Struct { x: Option<Option<u8>> },
++}
++
++// The lint allows this
++type OptionOption = Option<Option<u32>>;
++
++// The lint allows this
++fn output_type_alias() -> OptionOption {
++ None
++}
++
++// The line allows this
++impl Trait for Struct {
++ fn trait_fn() -> Option<Option<u8>> {
++ None
++ }
++}
++
++fn main() {
++ input(None);
++ output();
++ output_nested();
++
++ // The lint allows this
++ let local: Option<Option<u8>> = None;
++
++ // The lint allows this
++ let expr = Some(Some(true));
++}
--- /dev/null
--- /dev/null
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:3:13
++ |
++LL | fn input(_: Option<Option<u8>>) {}
++ | ^^^^^^^^^^^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/option_option.rs:1:9
++ |
++LL | #![deny(clippy::option_option)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:5:16
++ |
++LL | fn output() -> Option<Option<u8>> {
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:9:27
++ |
++LL | fn output_nested() -> Vec<Option<Option<u8>>> {
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:14:30
++ |
++LL | fn output_nested_nested() -> Option<Option<Option<u8>>> {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:19:8
++ |
++LL | x: Option<Option<u8>>,
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:23:23
++ |
++LL | fn struct_fn() -> Option<Option<u8>> {
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:29:22
++ |
++LL | fn trait_fn() -> Option<Option<u8>>;
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:33:11
++ |
++LL | Tuple(Option<Option<u8>>),
++ | ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++ --> $DIR/option_option.rs:34:17
++ |
++LL | Struct { x: Option<Option<u8>> },
++ | ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::or_fun_call)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::time::Duration;
++
++/// Checks implementation of the `OR_FUN_CALL` lint.
++fn or_fun_call() {
++ struct Foo;
++
++ impl Foo {
++ fn new() -> Foo {
++ Foo
++ }
++ }
++
++ enum Enum {
++ A(i32),
++ }
++
++ fn make<T>() -> T {
++ unimplemented!();
++ }
++
++ let with_enum = Some(Enum::A(1));
++ with_enum.unwrap_or(Enum::A(5));
++
++ let with_const_fn = Some(Duration::from_secs(1));
++ with_const_fn.unwrap_or(Duration::from_secs(5));
++
++ let with_constructor = Some(vec![1]);
++ with_constructor.unwrap_or_else(make);
++
++ let with_new = Some(vec![1]);
++ with_new.unwrap_or_default();
++
++ let with_const_args = Some(vec![1]);
++ with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
++
++ let with_err: Result<_, ()> = Ok(vec![1]);
++ with_err.unwrap_or_else(|_| make());
++
++ let with_err_args: Result<_, ()> = Ok(vec![1]);
++ with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
++
++ let with_default_trait = Some(1);
++ with_default_trait.unwrap_or_default();
++
++ let with_default_type = Some(1);
++ with_default_type.unwrap_or_default();
++
++ let with_vec = Some(vec![1]);
++ with_vec.unwrap_or_default();
++
++ let without_default = Some(Foo);
++ without_default.unwrap_or_else(Foo::new);
++
++ let mut map = HashMap::<u64, String>::new();
++ map.entry(42).or_insert_with(String::new);
++
++ let mut btree = BTreeMap::<u64, String>::new();
++ btree.entry(42).or_insert_with(String::new);
++
++ let stringy = Some(String::from(""));
++ let _ = stringy.unwrap_or_else(|| "".to_owned());
++
++ let opt = Some(1);
++ let hello = "Hello";
++ let _ = opt.ok_or(format!("{} world.", hello));
++}
++
++struct Foo(u8);
++struct Bar(String, Duration);
++#[rustfmt::skip]
++fn test_or_with_ctors() {
++ let opt = Some(1);
++ let opt_opt = Some(Some(1));
++ // we also test for const promotion, this makes sure we don't hit that
++ let two = 2;
++
++ let _ = opt_opt.unwrap_or(Some(2));
++ let _ = opt_opt.unwrap_or(Some(two));
++ let _ = opt.ok_or(Some(2));
++ let _ = opt.ok_or(Some(two));
++ let _ = opt.ok_or(Foo(2));
++ let _ = opt.ok_or(Foo(two));
++ let _ = opt.or(Some(2));
++ let _ = opt.or(Some(two));
++
++ let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
++
++ let b = "b".to_string();
++ let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
++ .or(Some(Bar(b, Duration::from_secs(2))));
++}
++
++// Issue 4514 - early return
++fn f() -> Option<()> {
++ let a = Some(1);
++ let b = 1i32;
++
++ let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
++
++ Some(())
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::or_fun_call)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::time::Duration;
++
++/// Checks implementation of the `OR_FUN_CALL` lint.
++fn or_fun_call() {
++ struct Foo;
++
++ impl Foo {
++ fn new() -> Foo {
++ Foo
++ }
++ }
++
++ enum Enum {
++ A(i32),
++ }
++
++ fn make<T>() -> T {
++ unimplemented!();
++ }
++
++ let with_enum = Some(Enum::A(1));
++ with_enum.unwrap_or(Enum::A(5));
++
++ let with_const_fn = Some(Duration::from_secs(1));
++ with_const_fn.unwrap_or(Duration::from_secs(5));
++
++ let with_constructor = Some(vec![1]);
++ with_constructor.unwrap_or(make());
++
++ let with_new = Some(vec![1]);
++ with_new.unwrap_or(Vec::new());
++
++ let with_const_args = Some(vec![1]);
++ with_const_args.unwrap_or(Vec::with_capacity(12));
++
++ let with_err: Result<_, ()> = Ok(vec![1]);
++ with_err.unwrap_or(make());
++
++ let with_err_args: Result<_, ()> = Ok(vec![1]);
++ with_err_args.unwrap_or(Vec::with_capacity(12));
++
++ let with_default_trait = Some(1);
++ with_default_trait.unwrap_or(Default::default());
++
++ let with_default_type = Some(1);
++ with_default_type.unwrap_or(u64::default());
++
++ let with_vec = Some(vec![1]);
++ with_vec.unwrap_or(vec![]);
++
++ let without_default = Some(Foo);
++ without_default.unwrap_or(Foo::new());
++
++ let mut map = HashMap::<u64, String>::new();
++ map.entry(42).or_insert(String::new());
++
++ let mut btree = BTreeMap::<u64, String>::new();
++ btree.entry(42).or_insert(String::new());
++
++ let stringy = Some(String::from(""));
++ let _ = stringy.unwrap_or("".to_owned());
++
++ let opt = Some(1);
++ let hello = "Hello";
++ let _ = opt.ok_or(format!("{} world.", hello));
++}
++
++struct Foo(u8);
++struct Bar(String, Duration);
++#[rustfmt::skip]
++fn test_or_with_ctors() {
++ let opt = Some(1);
++ let opt_opt = Some(Some(1));
++ // we also test for const promotion, this makes sure we don't hit that
++ let two = 2;
++
++ let _ = opt_opt.unwrap_or(Some(2));
++ let _ = opt_opt.unwrap_or(Some(two));
++ let _ = opt.ok_or(Some(2));
++ let _ = opt.ok_or(Some(two));
++ let _ = opt.ok_or(Foo(2));
++ let _ = opt.ok_or(Foo(two));
++ let _ = opt.or(Some(2));
++ let _ = opt.or(Some(two));
++
++ let _ = Some("a".to_string()).or(Some("b".to_string()));
++
++ let b = "b".to_string();
++ let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
++ .or(Some(Bar(b, Duration::from_secs(2))));
++}
++
++// Issue 4514 - early return
++fn f() -> Option<()> {
++ let a = Some(1);
++ let b = 1i32;
++
++ let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
++
++ Some(())
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:35:22
++ |
++LL | with_constructor.unwrap_or(make());
++ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
++ |
++ = note: `-D clippy::or-fun-call` implied by `-D warnings`
++
++error: use of `unwrap_or` followed by a call to `new`
++ --> $DIR/or_fun_call.rs:38:5
++ |
++LL | with_new.unwrap_or(Vec::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:41:21
++ |
++LL | with_const_args.unwrap_or(Vec::with_capacity(12));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:44:14
++ |
++LL | with_err.unwrap_or(make());
++ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:47:19
++ |
++LL | with_err_args.unwrap_or(Vec::with_capacity(12));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
++
++error: use of `unwrap_or` followed by a call to `default`
++ --> $DIR/or_fun_call.rs:50:5
++ |
++LL | with_default_trait.unwrap_or(Default::default());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a call to `default`
++ --> $DIR/or_fun_call.rs:53:5
++ |
++LL | with_default_type.unwrap_or(u64::default());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a call to `new`
++ --> $DIR/or_fun_call.rs:56:5
++ |
++LL | with_vec.unwrap_or(vec![]);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:59:21
++ |
++LL | without_default.unwrap_or(Foo::new());
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
++
++error: use of `or_insert` followed by a function call
++ --> $DIR/or_fun_call.rs:62:19
++ |
++LL | map.entry(42).or_insert(String::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
++
++error: use of `or_insert` followed by a function call
++ --> $DIR/or_fun_call.rs:65:21
++ |
++LL | btree.entry(42).or_insert(String::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/or_fun_call.rs:68:21
++ |
++LL | let _ = stringy.unwrap_or("".to_owned());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
++
++error: use of `or` followed by a function call
++ --> $DIR/or_fun_call.rs:93:35
++ |
++LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
++
++error: aborting due to 13 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, const_err)]
++
++fn main() {
++ let x = [1, 2, 3, 4];
++
++ // issue 3102
++ let num = 1;
++ &x[num..10]; // should trigger out of bounds error
++ &x[10..num]; // should trigger out of bounds error
++}
--- /dev/null
--- /dev/null
++error: range is out of bounds
++ --> $DIR/issue-3102.rs:9:13
++ |
++LL | &x[num..10]; // should trigger out of bounds error
++ | ^^
++ |
++ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: range is out of bounds
++ --> $DIR/issue-3102.rs:10:8
++ |
++LL | &x[10..num]; // should trigger out of bounds error
++ | ^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)]
++
++fn main() {
++ let x = [1, 2, 3, 4];
++
++ &x[..=4];
++ &x[1..5];
++ &x[5..];
++ &x[..5];
++ &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
++ &x[0..=4];
++
++ &x[4..]; // Ok, should not produce stderr.
++ &x[..4]; // Ok, should not produce stderr.
++ &x[..]; // Ok, should not produce stderr.
++ &x[1..]; // Ok, should not produce stderr.
++ &x[2..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); // Ok, should not produce stderr.
++
++ &x[0..].get(..3); // Ok, should not produce stderr.
++ &x[0..3]; // Ok, should not produce stderr.
++}
--- /dev/null
--- /dev/null
++error: range is out of bounds
++ --> $DIR/simple.rs:7:11
++ |
++LL | &x[..=4];
++ | ^
++ |
++ = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: range is out of bounds
++ --> $DIR/simple.rs:8:11
++ |
++LL | &x[1..5];
++ | ^
++
++error: range is out of bounds
++ --> $DIR/simple.rs:9:8
++ |
++LL | &x[5..];
++ | ^
++
++error: range is out of bounds
++ --> $DIR/simple.rs:10:10
++ |
++LL | &x[..5];
++ | ^
++
++error: range is out of bounds
++ --> $DIR/simple.rs:11:8
++ |
++LL | &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
++ | ^
++
++error: range is out of bounds
++ --> $DIR/simple.rs:12:12
++ |
++LL | &x[0..=4];
++ | ^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_hir;
++extern crate rustc_lint;
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++
++declare_lint! {
++ pub TEST_LINT,
++ Warn,
++ ""
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
++ fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
++ let _ = expr.span.ctxt().outer_expn_data();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_hir;
++extern crate rustc_lint;
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++
++declare_lint! {
++ pub TEST_LINT,
++ Warn,
++ ""
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
++ fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
++ let _ = expr.span.ctxt().outer_expn().expn_data();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: usage of `outer_expn().expn_data()`
++ --> $DIR/outer_expn_data.rs:24:34
++ |
++LL | let _ = expr.span.ctxt().outer_expn().expn_data();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
++ |
++note: the lint level is defined here
++ --> $DIR/outer_expn_data.rs:3:9
++ |
++LL | #![deny(clippy::internal)]
++ | ^^^^^^^^^^^^^^^^
++ = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(clippy::many_single_char_names)]
++#![warn(clippy::overflow_check_conditional)]
++
++fn main() {
++ let a: u32 = 1;
++ let b: u32 = 2;
++ let c: u32 = 3;
++ if a + b < a {}
++ if a > a + b {}
++ if a + b < b {}
++ if b > a + b {}
++ if a - b > b {}
++ if b < a - b {}
++ if a - b > a {}
++ if a < a - b {}
++ if a + b < c {}
++ if c > a + b {}
++ if a - b < c {}
++ if c > a - b {}
++ let i = 1.1;
++ let j = 2.2;
++ if i + j < i {}
++ if i - j < i {}
++ if i > i + j {}
++ if i - j < i {}
++}
--- /dev/null
--- /dev/null
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:8:8
++ |
++LL | if a + b < a {}
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:9:8
++ |
++LL | if a > a + b {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:10:8
++ |
++LL | if a + b < b {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:11:8
++ |
++LL | if b > a + b {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:12:8
++ |
++LL | if a - b > b {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:13:8
++ |
++LL | if b < a - b {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:14:8
++ |
++LL | if a - b > a {}
++ | ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++ --> $DIR/overflow_check_conditional.rs:15:8
++ |
++LL | if a < a - b {}
++ | ^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::panic_params)]
++#![allow(clippy::assertions_on_constants)]
++fn missing() {
++ if true {
++ panic!("{}");
++ } else if false {
++ panic!("{:?}");
++ } else {
++ assert!(true, "here be missing values: {}");
++ }
++
++ panic!("{{{this}}}");
++}
++
++fn ok_single() {
++ panic!("foo bar");
++}
++
++fn ok_inner() {
++ // Test for #768
++ assert!("foo bar".contains(&format!("foo {}", "bar")));
++}
++
++fn ok_multiple() {
++ panic!("{}", "This is {ok}");
++}
++
++fn ok_bracket() {
++ match 42 {
++ 1337 => panic!("{so is this"),
++ 666 => panic!("so is this}"),
++ _ => panic!("}so is that{"),
++ }
++}
++
++const ONE: u32 = 1;
++
++fn ok_nomsg() {
++ assert!({ 1 == ONE });
++ assert!(if 1 == ONE { ONE == 1 } else { false });
++}
++
++fn ok_escaped() {
++ panic!("{{ why should this not be ok? }}");
++ panic!(" or {{ that ?");
++ panic!(" or }} this ?");
++ panic!(" {or {{ that ?");
++ panic!(" }or }} this ?");
++ panic!("{{ test }");
++ panic!("{case }}");
++}
++
++fn main() {
++ missing();
++ ok_single();
++ ok_multiple();
++ ok_bracket();
++ ok_inner();
++ ok_nomsg();
++ ok_escaped();
++}
--- /dev/null
--- /dev/null
++error: you probably are missing some parameter in your format string
++ --> $DIR/panic.rs:5:16
++ |
++LL | panic!("{}");
++ | ^^^^
++ |
++ = note: `-D clippy::panic-params` implied by `-D warnings`
++
++error: you probably are missing some parameter in your format string
++ --> $DIR/panic.rs:7:16
++ |
++LL | panic!("{:?}");
++ | ^^^^^^
++
++error: you probably are missing some parameter in your format string
++ --> $DIR/panic.rs:9:23
++ |
++LL | assert!(true, "here be missing values: {}");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you probably are missing some parameter in your format string
++ --> $DIR/panic.rs:12:12
++ |
++LL | panic!("{{{this}}}");
++ | ^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
++#![allow(clippy::assertions_on_constants)]
++
++fn panic() {
++ let a = 2;
++ panic!();
++ let b = a + 2;
++}
++
++fn todo() {
++ let a = 2;
++ todo!();
++ let b = a + 2;
++}
++
++fn unimplemented() {
++ let a = 2;
++ unimplemented!();
++ let b = a + 2;
++}
++
++fn unreachable() {
++ let a = 2;
++ unreachable!();
++ let b = a + 2;
++}
++
++fn main() {
++ panic();
++ todo();
++ unimplemented();
++ unreachable();
++}
--- /dev/null
--- /dev/null
++error: `panic` should not be present in production code
++ --> $DIR/panicking_macros.rs:6:5
++ |
++LL | panic!();
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::panic` implied by `-D warnings`
++
++error: `todo` should not be present in production code
++ --> $DIR/panicking_macros.rs:12:5
++ |
++LL | todo!();
++ | ^^^^^^^^
++ |
++ = note: `-D clippy::todo` implied by `-D warnings`
++
++error: `unimplemented` should not be present in production code
++ --> $DIR/panicking_macros.rs:18:5
++ |
++LL | unimplemented!();
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unimplemented` implied by `-D warnings`
++
++error: `unreachable` should not be present in production code
++ --> $DIR/panicking_macros.rs:24:5
++ |
++LL | unreachable!();
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unreachable` implied by `-D warnings`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++
++struct Foo;
++
++impl PartialEq for Foo {
++ fn eq(&self, _: &Foo) -> bool {
++ true
++ }
++ fn ne(&self, _: &Foo) -> bool {
++ false
++ }
++}
++
++struct Bar;
++
++impl PartialEq for Bar {
++ fn eq(&self, _: &Bar) -> bool {
++ true
++ }
++ #[allow(clippy::partialeq_ne_impl)]
++ fn ne(&self, _: &Bar) -> bool {
++ false
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: re-implementing `PartialEq::ne` is unnecessary
++ --> $DIR/partialeq_ne_impl.rs:9:5
++ |
++LL | / fn ne(&self, _: &Foo) -> bool {
++LL | | false
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++use std::path::PathBuf;
++
++#[warn(clippy::all, clippy::path_buf_push_overwrite)]
++fn main() {
++ let mut x = PathBuf::from("/foo");
++ x.push("bar");
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++use std::path::PathBuf;
++
++#[warn(clippy::all, clippy::path_buf_push_overwrite)]
++fn main() {
++ let mut x = PathBuf::from("/foo");
++ x.push("/bar");
++}
--- /dev/null
--- /dev/null
++error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
++ --> $DIR/path_buf_push_overwrite.rs:7:12
++ |
++LL | x.push("/bar");
++ | ^^^^^^ help: try: `"bar"`
++ |
++ = note: `-D clippy::path-buf-push-overwrite` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused)]
++#![warn(clippy::all)]
++
++fn main() {
++ let v = Some(true);
++ let s = [0, 1, 2, 3, 4];
++ match v {
++ Some(x) => (),
++ y => (),
++ }
++ match v {
++ Some(x) => (),
++ y @ None => (), // no error
++ }
++ match s {
++ [x, inside @ .., y] => (), // no error
++ [..] => (),
++ }
++
++ let mut mutv = vec![1, 2, 3];
++
++ // required "ref" left out in suggestion: #5271
++ match mutv {
++ ref mut x => {
++ x.push(4);
++ println!("vec: {:?}", x);
++ },
++ ref y if y == &vec![0] => (),
++ }
++
++ match mutv {
++ ref x => println!("vec: {:?}", x),
++ ref y if y == &vec![0] => (),
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unused)]
++#![warn(clippy::all)]
++
++fn main() {
++ let v = Some(true);
++ let s = [0, 1, 2, 3, 4];
++ match v {
++ Some(x) => (),
++ y @ _ => (),
++ }
++ match v {
++ Some(x) => (),
++ y @ None => (), // no error
++ }
++ match s {
++ [x, inside @ .., y] => (), // no error
++ [..] => (),
++ }
++
++ let mut mutv = vec![1, 2, 3];
++
++ // required "ref" left out in suggestion: #5271
++ match mutv {
++ ref mut x @ _ => {
++ x.push(4);
++ println!("vec: {:?}", x);
++ },
++ ref y if y == &vec![0] => (),
++ }
++
++ match mutv {
++ ref x @ _ => println!("vec: {:?}", x),
++ ref y if y == &vec![0] => (),
++ }
++}
--- /dev/null
--- /dev/null
++error: the `y @ _` pattern can be written as just `y`
++ --> $DIR/patterns.rs:10:9
++ |
++LL | y @ _ => (),
++ | ^^^^^ help: try: `y`
++ |
++ = note: `-D clippy::redundant-pattern` implied by `-D warnings`
++
++error: the `x @ _` pattern can be written as just `x`
++ --> $DIR/patterns.rs:25:9
++ |
++LL | ref mut x @ _ => {
++ | ^^^^^^^^^^^^^ help: try: `ref mut x`
++
++error: the `x @ _` pattern can be written as just `x`
++ --> $DIR/patterns.rs:33:9
++ |
++LL | ref x @ _ => println!("vec: {:?}", x),
++ | ^^^^^^^^^ help: try: `ref x`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::precedence)]
++#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
++#![allow(clippy::identity_op)]
++#![allow(clippy::eq_op)]
++
++macro_rules! trip {
++ ($a:expr) => {
++ match $a & 0b1111_1111u8 {
++ 0 => println!("a is zero ({})", $a),
++ _ => println!("a is {}", $a),
++ }
++ };
++}
++
++fn main() {
++ 1 << (2 + 3);
++ (1 + 2) << 3;
++ 4 >> (1 + 1);
++ (1 + 3) >> 2;
++ 1 ^ (1 - 1);
++ 3 | (2 - 1);
++ 3 & (5 - 2);
++ -(1i32.abs());
++ -(1f32.abs());
++
++ // These should not trigger an error
++ let _ = (-1i32).abs();
++ let _ = (-1f32).abs();
++ let _ = -(1i32).abs();
++ let _ = -(1f32).abs();
++ let _ = -(1i32.abs());
++ let _ = -(1f32.abs());
++
++ // Odd functions shoud not trigger an error
++ let _ = -1f64.asin();
++ let _ = -1f64.asinh();
++ let _ = -1f64.atan();
++ let _ = -1f64.atanh();
++ let _ = -1f64.cbrt();
++ let _ = -1f64.fract();
++ let _ = -1f64.round();
++ let _ = -1f64.signum();
++ let _ = -1f64.sin();
++ let _ = -1f64.sinh();
++ let _ = -1f64.tan();
++ let _ = -1f64.tanh();
++ let _ = -1f64.to_degrees();
++ let _ = -1f64.to_radians();
++
++ let b = 3;
++ trip!(b * 8);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::precedence)]
++#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
++#![allow(clippy::identity_op)]
++#![allow(clippy::eq_op)]
++
++macro_rules! trip {
++ ($a:expr) => {
++ match $a & 0b1111_1111u8 {
++ 0 => println!("a is zero ({})", $a),
++ _ => println!("a is {}", $a),
++ }
++ };
++}
++
++fn main() {
++ 1 << 2 + 3;
++ 1 + 2 << 3;
++ 4 >> 1 + 1;
++ 1 + 3 >> 2;
++ 1 ^ 1 - 1;
++ 3 | 2 - 1;
++ 3 & 5 - 2;
++ -1i32.abs();
++ -1f32.abs();
++
++ // These should not trigger an error
++ let _ = (-1i32).abs();
++ let _ = (-1f32).abs();
++ let _ = -(1i32).abs();
++ let _ = -(1f32).abs();
++ let _ = -(1i32.abs());
++ let _ = -(1f32.abs());
++
++ // Odd functions shoud not trigger an error
++ let _ = -1f64.asin();
++ let _ = -1f64.asinh();
++ let _ = -1f64.atan();
++ let _ = -1f64.atanh();
++ let _ = -1f64.cbrt();
++ let _ = -1f64.fract();
++ let _ = -1f64.round();
++ let _ = -1f64.signum();
++ let _ = -1f64.sin();
++ let _ = -1f64.sinh();
++ let _ = -1f64.tan();
++ let _ = -1f64.tanh();
++ let _ = -1f64.to_degrees();
++ let _ = -1f64.to_radians();
++
++ let b = 3;
++ trip!(b * 8);
++}
--- /dev/null
--- /dev/null
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:17:5
++ |
++LL | 1 << 2 + 3;
++ | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)`
++ |
++ = note: `-D clippy::precedence` implied by `-D warnings`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:18:5
++ |
++LL | 1 + 2 << 3;
++ | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:19:5
++ |
++LL | 4 >> 1 + 1;
++ | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:20:5
++ |
++LL | 1 + 3 >> 2;
++ | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:21:5
++ |
++LL | 1 ^ 1 - 1;
++ | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:22:5
++ |
++LL | 3 | 2 - 1;
++ | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)`
++
++error: operator precedence can trip the unwary
++ --> $DIR/precedence.rs:23:5
++ |
++LL | 3 & 5 - 2;
++ | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
++
++error: unary minus has lower precedence than method call
++ --> $DIR/precedence.rs:24:5
++ |
++LL | -1i32.abs();
++ | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())`
++
++error: unary minus has lower precedence than method call
++ --> $DIR/precedence.rs:25:5
++ |
++LL | -1f32.abs();
++ | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(clippy::print_literal, clippy::write_literal)]
++#![warn(clippy::print_stdout, clippy::use_debug)]
++
++use std::fmt::{Debug, Display, Formatter, Result};
++
++#[allow(dead_code)]
++struct Foo;
++
++impl Display for Foo {
++ fn fmt(&self, f: &mut Formatter) -> Result {
++ write!(f, "{:?}", 43.1415)
++ }
++}
++
++impl Debug for Foo {
++ fn fmt(&self, f: &mut Formatter) -> Result {
++ // ok, we can use `Debug` formatting in `Debug` implementations
++ write!(f, "{:?}", 42.718)
++ }
++}
++
++fn main() {
++ println!("Hello");
++ print!("Hello");
++
++ print!("Hello {}", "World");
++
++ print!("Hello {:?}", "World");
++
++ print!("Hello {:#?}", "#orld");
++
++ assert_eq!(42, 1337);
++
++ vec![1, 2];
++}
--- /dev/null
--- /dev/null
++error: use of `Debug`-based formatting
++ --> $DIR/print.rs:11:19
++ |
++LL | write!(f, "{:?}", 43.1415)
++ | ^^^^^^
++ |
++ = note: `-D clippy::use-debug` implied by `-D warnings`
++
++error: use of `println!`
++ --> $DIR/print.rs:23:5
++ |
++LL | println!("Hello");
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::print-stdout` implied by `-D warnings`
++
++error: use of `print!`
++ --> $DIR/print.rs:24:5
++ |
++LL | print!("Hello");
++ | ^^^^^^^^^^^^^^^
++
++error: use of `print!`
++ --> $DIR/print.rs:26:5
++ |
++LL | print!("Hello {}", "World");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `print!`
++ --> $DIR/print.rs:28:5
++ |
++LL | print!("Hello {:?}", "World");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `Debug`-based formatting
++ --> $DIR/print.rs:28:12
++ |
++LL | print!("Hello {:?}", "World");
++ | ^^^^^^^^^^^^
++
++error: use of `print!`
++ --> $DIR/print.rs:30:5
++ |
++LL | print!("Hello {:#?}", "#orld");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `Debug`-based formatting
++ --> $DIR/print.rs:30:12
++ |
++LL | print!("Hello {:#?}", "#orld");
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::print_literal)]
++
++fn main() {
++ // these should be fine
++ print!("Hello");
++ println!("Hello");
++ let world = "world";
++ println!("Hello {}", world);
++ println!("Hello {world}", world = world);
++ println!("3 in hex is {:X}", 3);
++ println!("2 + 1 = {:.4}", 3);
++ println!("2 + 1 = {:5.4}", 3);
++ println!("Debug test {:?}", "hello, world");
++ println!("{0:8} {1:>8}", "hello", "world");
++ println!("{1:8} {0:>8}", "hello", "world");
++ println!("{foo:8} {bar:>8}", foo = "hello", bar = "world");
++ println!("{bar:8} {foo:>8}", foo = "hello", bar = "world");
++ println!("{number:>width$}", number = 1, width = 6);
++ println!("{number:>0width$}", number = 1, width = 6);
++
++ // these should throw warnings
++ println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
++ print!("Hello {}", "world");
++ println!("Hello {} {}", world, "world");
++ println!("Hello {}", "world");
++ println!("10 / 4 is {}", 2.5);
++ println!("2 + 1 = {}", 3);
++
++ // positional args don't change the fact
++ // that we're using a literal -- this should
++ // throw a warning
++ println!("{0} {1}", "hello", "world");
++ println!("{1} {0}", "hello", "world");
++
++ // named args shouldn't change anything either
++ println!("{foo} {bar}", foo = "hello", bar = "world");
++ println!("{bar} {foo}", foo = "hello", bar = "world");
++}
--- /dev/null
--- /dev/null
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:22:71
++ |
++LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
++ | ^
++ |
++ = note: `-D clippy::print-literal` implied by `-D warnings`
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:23:24
++ |
++LL | print!("Hello {}", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:24:36
++ |
++LL | println!("Hello {} {}", world, "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:25:26
++ |
++LL | println!("Hello {}", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:26:30
++ |
++LL | println!("10 / 4 is {}", 2.5);
++ | ^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:27:28
++ |
++LL | println!("2 + 1 = {}", 3);
++ | ^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:32:25
++ |
++LL | println!("{0} {1}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:32:34
++ |
++LL | println!("{0} {1}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:33:25
++ |
++LL | println!("{1} {0}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:33:34
++ |
++LL | println!("{1} {0}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:36:35
++ |
++LL | println!("{foo} {bar}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:36:50
++ |
++LL | println!("{foo} {bar}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:37:35
++ |
++LL | println!("{bar} {foo}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/print_literal.rs:37:50
++ |
++LL | println!("{bar} {foo}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
++// // run-rustfix
++
++#![allow(clippy::print_literal)]
++#![warn(clippy::print_with_newline)]
++
++fn main() {
++ print!("Hello\n");
++ print!("Hello {}\n", "world");
++ print!("Hello {} {}\n", "world", "#2");
++ print!("{}\n", 1265);
++
++ // these are all fine
++ print!("");
++ print!("Hello");
++ println!("Hello");
++ println!("Hello\n");
++ println!("Hello {}\n", "world");
++ print!("Issue\n{}", 1265);
++ print!("{}", 1265);
++ print!("\n{}", 1275);
++ print!("\n\n");
++ print!("like eof\n\n");
++ print!("Hello {} {}\n\n", "world", "#2");
++ println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
++ println!("\nbla\n\n"); // #3126
++
++ // Escaping
++ print!("\\n"); // #3514
++ print!("\\\n"); // should fail
++ print!("\\\\n");
++
++ // Raw strings
++ print!(r"\n"); // #3778
++
++ // Literal newlines should also fail
++ print!(
++ "
++"
++ );
++ print!(
++ r"
++"
++ );
++
++ // Don't warn on CRLF (#4208)
++ print!("\r\n");
++ print!("foo\r\n");
++ print!("\\r\n"); //~ ERROR
++ print!("foo\rbar\n") // ~ ERROR
++}
--- /dev/null
--- /dev/null
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:8:5
++ |
++LL | print!("Hello/n");
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::print-with-newline` implied by `-D warnings`
++help: use `println!` instead
++ |
++LL | println!("Hello");
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:9:5
++ |
++LL | print!("Hello {}/n", "world");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("Hello {}", "world");
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:10:5
++ |
++LL | print!("Hello {} {}/n", "world", "#2");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("Hello {} {}", "world", "#2");
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:11:5
++ |
++LL | print!("{}/n", 1265);
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("{}", 1265);
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:30:5
++ |
++LL | print!("//n"); // should fail
++ | ^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("/"); // should fail
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:37:5
++ |
++LL | / print!(
++LL | | "
++LL | | "
++LL | | );
++ | |_____^
++ |
++help: use `println!` instead
++ |
++LL | println!(
++LL | ""
++ |
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:41:5
++ |
++LL | / print!(
++LL | | r"
++LL | | "
++LL | | );
++ | |_____^
++ |
++help: use `println!` instead
++ |
++LL | println!(
++LL | r""
++ |
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:49:5
++ |
++LL | print!("/r/n"); //~ ERROR
++ | ^^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("/r"); //~ ERROR
++ | ^^^^^^^ --
++
++error: using `print!()` with a format string that ends in a single newline
++ --> $DIR/print_with_newline.rs:50:5
++ |
++LL | print!("foo/rbar/n") // ~ ERROR
++ | ^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `println!` instead
++ |
++LL | println!("foo/rbar") // ~ ERROR
++ | ^^^^^^^ --
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::match_single_binding)]
++
++fn main() {
++ println!();
++ println!();
++
++ match "a" {
++ _ => println!(),
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(clippy::match_single_binding)]
++
++fn main() {
++ println!();
++ println!("");
++
++ match "a" {
++ _ => println!(""),
++ }
++}
--- /dev/null
--- /dev/null
++error: using `println!("")`
++ --> $DIR/println_empty_string.rs:6:5
++ |
++LL | println!("");
++ | ^^^^^^^^^^^^ help: replace it with: `println!()`
++ |
++ = note: `-D clippy::println-empty-string` implied by `-D warnings`
++
++error: using `println!("")`
++ --> $DIR/println_empty_string.rs:9:14
++ |
++LL | _ => println!(""),
++ | ^^^^^^^^^^^^ help: replace it with: `println!()`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++//! Check that we correctly lint procedural macros.
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++
++use proc_macro::TokenStream;
++
++#[allow(dead_code)]
++fn f() {
++ let _x = 3.14;
++}
++
++#[proc_macro]
++pub fn mybangmacro(t: TokenStream) -> TokenStream {
++ t
++}
++
++#[proc_macro_derive(MyDerivedTrait)]
++pub fn myderive(t: TokenStream) -> TokenStream {
++ t
++}
++
++#[proc_macro_attribute]
++pub fn myattribute(t: TokenStream, a: TokenStream) -> TokenStream {
++ t
++}
--- /dev/null
--- /dev/null
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++ --> $DIR/proc_macro.rs:10:14
++ |
++LL | let _x = 3.14;
++ | ^^^^
++ |
++ = note: `#[deny(clippy::approx_constant)]` on by default
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
++#![warn(clippy::ptr_arg)]
++
++use std::borrow::Cow;
++
++fn do_vec(x: &Vec<i64>) {
++ //Nothing here
++}
++
++fn do_vec_mut(x: &mut Vec<i64>) {
++ // no error here
++ //Nothing here
++}
++
++fn do_str(x: &String) {
++ //Nothing here either
++}
++
++fn do_str_mut(x: &mut String) {
++ // no error here
++ //Nothing here either
++}
++
++fn main() {}
++
++trait Foo {
++ type Item;
++ fn do_vec(x: &Vec<i64>);
++ fn do_item(x: &Self::Item);
++}
++
++struct Bar;
++
++// no error, in trait impl (#425)
++impl Foo for Bar {
++ type Item = Vec<u8>;
++ fn do_vec(x: &Vec<i64>) {}
++ fn do_item(x: &Vec<u8>) {}
++}
++
++fn cloned(x: &Vec<u8>) -> Vec<u8> {
++ let e = x.clone();
++ let f = e.clone(); // OK
++ let g = x;
++ let h = g.clone(); // Alas, we cannot reliably detect this without following data.
++ let i = (e).clone();
++ x.clone()
++}
++
++fn str_cloned(x: &String) -> String {
++ let a = x.clone();
++ let b = x.clone();
++ let c = b.clone();
++ let d = a.clone().clone().clone();
++ x.clone()
++}
++
++fn false_positive_capacity(x: &Vec<u8>, y: &String) {
++ let a = x.capacity();
++ let b = y.clone();
++ let c = y.as_str();
++}
++
++fn false_positive_capacity_too(x: &String) -> String {
++ if x.capacity() > 1024 {
++ panic!("Too large!");
++ }
++ x.clone()
++}
++
++#[allow(dead_code)]
++fn test_cow_with_ref(c: &Cow<[i32]>) {}
++
++#[allow(dead_code)]
++fn test_cow(c: Cow<[i32]>) {
++ let _c = c;
++}
++
++trait Foo2 {
++ fn do_string(&self);
++}
++
++// no error for &self references where self is of type String (#2293)
++impl Foo2 for String {
++ fn do_string(&self) {}
++}
--- /dev/null
--- /dev/null
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++ --> $DIR/ptr_arg.rs:6:14
++ |
++LL | fn do_vec(x: &Vec<i64>) {
++ | ^^^^^^^^^ help: change this to: `&[i64]`
++ |
++ = note: `-D clippy::ptr-arg` implied by `-D warnings`
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++ --> $DIR/ptr_arg.rs:15:14
++ |
++LL | fn do_str(x: &String) {
++ | ^^^^^^^ help: change this to: `&str`
++
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++ --> $DIR/ptr_arg.rs:28:18
++ |
++LL | fn do_vec(x: &Vec<i64>);
++ | ^^^^^^^^^ help: change this to: `&[i64]`
++
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++ --> $DIR/ptr_arg.rs:41:14
++ |
++LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
++ | ^^^^^^^^
++ |
++help: change this to
++ |
++LL | fn cloned(x: &[u8]) -> Vec<u8> {
++ | ^^^^^
++help: change `x.clone()` to
++ |
++LL | let e = x.to_owned();
++ | ^^^^^^^^^^^^
++help: change `x.clone()` to
++ |
++LL | x.to_owned()
++ |
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++ --> $DIR/ptr_arg.rs:50:18
++ |
++LL | fn str_cloned(x: &String) -> String {
++ | ^^^^^^^
++ |
++help: change this to
++ |
++LL | fn str_cloned(x: &str) -> String {
++ | ^^^^
++help: change `x.clone()` to
++ |
++LL | let a = x.to_string();
++ | ^^^^^^^^^^^^^
++help: change `x.clone()` to
++ |
++LL | let b = x.to_string();
++ | ^^^^^^^^^^^^^
++help: change `x.clone()` to
++ |
++LL | x.to_string()
++ |
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++ --> $DIR/ptr_arg.rs:58:44
++ |
++LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
++ | ^^^^^^^
++ |
++help: change this to
++ |
++LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) {
++ | ^^^^
++help: change `y.clone()` to
++ |
++LL | let b = y.to_string();
++ | ^^^^^^^^^^^^^
++help: change `y.as_str()` to
++ |
++LL | let c = y;
++ | ^
++
++error: using a reference to `Cow` is not recommended.
++ --> $DIR/ptr_arg.rs:72:25
++ |
++LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
++ | ^^^^^^^^^^^ help: change this to: `&[i32]`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn main() {
++ let vec = vec![b'a', b'b', b'c'];
++ let ptr = vec.as_ptr();
++
++ let offset_u8 = 1_u8;
++ let offset_usize = 1_usize;
++ let offset_isize = 1_isize;
++
++ unsafe {
++ ptr.add(offset_usize);
++ ptr.offset(offset_isize as isize);
++ ptr.offset(offset_u8 as isize);
++
++ ptr.wrapping_add(offset_usize);
++ ptr.wrapping_offset(offset_isize as isize);
++ ptr.wrapping_offset(offset_u8 as isize);
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++fn main() {
++ let vec = vec![b'a', b'b', b'c'];
++ let ptr = vec.as_ptr();
++
++ let offset_u8 = 1_u8;
++ let offset_usize = 1_usize;
++ let offset_isize = 1_isize;
++
++ unsafe {
++ ptr.offset(offset_usize as isize);
++ ptr.offset(offset_isize as isize);
++ ptr.offset(offset_u8 as isize);
++
++ ptr.wrapping_offset(offset_usize as isize);
++ ptr.wrapping_offset(offset_isize as isize);
++ ptr.wrapping_offset(offset_u8 as isize);
++ }
++}
--- /dev/null
--- /dev/null
++error: use of `offset` with a `usize` casted to an `isize`
++ --> $DIR/ptr_offset_with_cast.rs:12:9
++ |
++LL | ptr.offset(offset_usize as isize);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
++ |
++ = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings`
++
++error: use of `wrapping_offset` with a `usize` casted to an `isize`
++ --> $DIR/ptr_offset_with_cast.rs:16:9
++ |
++LL | ptr.wrapping_offset(offset_usize as isize);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unreachable_code)]
++
++fn some_func(a: Option<u32>) -> Option<u32> {
++ a?;
++
++ a
++}
++
++fn some_other_func(a: Option<u32>) -> Option<u32> {
++ if a.is_none() {
++ return None;
++ } else {
++ return Some(0);
++ }
++ unreachable!()
++}
++
++pub enum SeemsOption<T> {
++ Some(T),
++ None,
++}
++
++impl<T> SeemsOption<T> {
++ pub fn is_none(&self) -> bool {
++ match *self {
++ SeemsOption::None => true,
++ SeemsOption::Some(_) => false,
++ }
++ }
++}
++
++fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
++ if a.is_none() {
++ return SeemsOption::None;
++ }
++
++ a
++}
++
++pub struct CopyStruct {
++ pub opt: Option<u32>,
++}
++
++impl CopyStruct {
++ #[rustfmt::skip]
++ pub fn func(&self) -> Option<u32> {
++ (self.opt)?;
++
++ self.opt?;
++
++ let _ = Some(self.opt?);
++
++ let _ = self.opt?;
++
++ self.opt
++ }
++}
++
++#[derive(Clone)]
++pub struct MoveStruct {
++ pub opt: Option<Vec<u32>>,
++}
++
++impl MoveStruct {
++ pub fn ref_func(&self) -> Option<Vec<u32>> {
++ self.opt.as_ref()?;
++
++ self.opt.clone()
++ }
++
++ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
++ self.opt.as_ref()?;
++
++ self.opt
++ }
++
++ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
++ self.opt.as_ref()?;
++ Some(Vec::new())
++ }
++
++ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
++ let v: &Vec<_> = self.opt.as_ref()?;
++
++ Some(v.clone())
++ }
++
++ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
++ let v = self.opt?;
++
++ Some(v)
++ }
++}
++
++fn func() -> Option<i32> {
++ fn f() -> Option<String> {
++ Some(String::new())
++ }
++
++ f()?;
++
++ Some(0)
++}
++
++fn main() {
++ some_func(Some(42));
++ some_func(None);
++ some_other_func(Some(42));
++
++ let copy_struct = CopyStruct { opt: Some(54) };
++ copy_struct.func();
++
++ let move_struct = MoveStruct {
++ opt: Some(vec![42, 1337]),
++ };
++ move_struct.ref_func();
++ move_struct.clone().mov_func_reuse();
++ move_struct.mov_func_no_use();
++
++ let so = SeemsOption::Some(45);
++ returns_something_similar_to_option(so);
++
++ func();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(unreachable_code)]
++
++fn some_func(a: Option<u32>) -> Option<u32> {
++ if a.is_none() {
++ return None;
++ }
++
++ a
++}
++
++fn some_other_func(a: Option<u32>) -> Option<u32> {
++ if a.is_none() {
++ return None;
++ } else {
++ return Some(0);
++ }
++ unreachable!()
++}
++
++pub enum SeemsOption<T> {
++ Some(T),
++ None,
++}
++
++impl<T> SeemsOption<T> {
++ pub fn is_none(&self) -> bool {
++ match *self {
++ SeemsOption::None => true,
++ SeemsOption::Some(_) => false,
++ }
++ }
++}
++
++fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
++ if a.is_none() {
++ return SeemsOption::None;
++ }
++
++ a
++}
++
++pub struct CopyStruct {
++ pub opt: Option<u32>,
++}
++
++impl CopyStruct {
++ #[rustfmt::skip]
++ pub fn func(&self) -> Option<u32> {
++ if (self.opt).is_none() {
++ return None;
++ }
++
++ if self.opt.is_none() {
++ return None
++ }
++
++ let _ = if self.opt.is_none() {
++ return None;
++ } else {
++ self.opt
++ };
++
++ let _ = if let Some(x) = self.opt {
++ x
++ } else {
++ return None;
++ };
++
++ self.opt
++ }
++}
++
++#[derive(Clone)]
++pub struct MoveStruct {
++ pub opt: Option<Vec<u32>>,
++}
++
++impl MoveStruct {
++ pub fn ref_func(&self) -> Option<Vec<u32>> {
++ if self.opt.is_none() {
++ return None;
++ }
++
++ self.opt.clone()
++ }
++
++ pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
++ if self.opt.is_none() {
++ return None;
++ }
++
++ self.opt
++ }
++
++ pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
++ if self.opt.is_none() {
++ return None;
++ }
++ Some(Vec::new())
++ }
++
++ pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
++ let v: &Vec<_> = if let Some(ref v) = self.opt {
++ v
++ } else {
++ return None;
++ };
++
++ Some(v.clone())
++ }
++
++ pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
++ let v = if let Some(v) = self.opt {
++ v
++ } else {
++ return None;
++ };
++
++ Some(v)
++ }
++}
++
++fn func() -> Option<i32> {
++ fn f() -> Option<String> {
++ Some(String::new())
++ }
++
++ if f().is_none() {
++ return None;
++ }
++
++ Some(0)
++}
++
++fn main() {
++ some_func(Some(42));
++ some_func(None);
++ some_other_func(Some(42));
++
++ let copy_struct = CopyStruct { opt: Some(54) };
++ copy_struct.func();
++
++ let move_struct = MoveStruct {
++ opt: Some(vec![42, 1337]),
++ };
++ move_struct.ref_func();
++ move_struct.clone().mov_func_reuse();
++ move_struct.mov_func_no_use();
++
++ let so = SeemsOption::Some(45);
++ returns_something_similar_to_option(so);
++
++ func();
++}
--- /dev/null
--- /dev/null
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:5:5
++ |
++LL | / if a.is_none() {
++LL | | return None;
++LL | | }
++ | |_____^ help: replace it with: `a?;`
++ |
++ = note: `-D clippy::question-mark` implied by `-D warnings`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:50:9
++ |
++LL | / if (self.opt).is_none() {
++LL | | return None;
++LL | | }
++ | |_________^ help: replace it with: `(self.opt)?;`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:54:9
++ |
++LL | / if self.opt.is_none() {
++LL | | return None
++LL | | }
++ | |_________^ help: replace it with: `self.opt?;`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:58:17
++ |
++LL | let _ = if self.opt.is_none() {
++ | _________________^
++LL | | return None;
++LL | | } else {
++LL | | self.opt
++LL | | };
++ | |_________^ help: replace it with: `Some(self.opt?)`
++
++error: this if-let-else may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:64:17
++ |
++LL | let _ = if let Some(x) = self.opt {
++ | _________________^
++LL | | x
++LL | | } else {
++LL | | return None;
++LL | | };
++ | |_________^ help: replace it with: `self.opt?`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:81:9
++ |
++LL | / if self.opt.is_none() {
++LL | | return None;
++LL | | }
++ | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:89:9
++ |
++LL | / if self.opt.is_none() {
++LL | | return None;
++LL | | }
++ | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:97:9
++ |
++LL | / if self.opt.is_none() {
++LL | | return None;
++LL | | }
++ | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this if-let-else may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:104:26
++ |
++LL | let v: &Vec<_> = if let Some(ref v) = self.opt {
++ | __________________________^
++LL | | v
++LL | | } else {
++LL | | return None;
++LL | | };
++ | |_________^ help: replace it with: `self.opt.as_ref()?`
++
++error: this if-let-else may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:114:17
++ |
++LL | let v = if let Some(v) = self.opt {
++ | _________________^
++LL | | v
++LL | | } else {
++LL | | return None;
++LL | | };
++ | |_________^ help: replace it with: `self.opt?`
++
++error: this block may be rewritten with the `?` operator
++ --> $DIR/question_mark.rs:129:5
++ |
++LL | / if f().is_none() {
++LL | | return None;
++LL | | }
++ | |_____^ help: replace it with: `f()?;`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::range_zip_with_len)]
++fn main() {
++ let v1 = vec![1, 2, 3];
++ let v2 = vec![4, 5];
++ let _x = v1.iter().zip(0..v1.len());
++ let _y = v1.iter().zip(0..v2.len()); // No error
++}
++
++#[allow(unused)]
++fn no_panic_with_fake_range_types() {
++ struct Range {
++ foo: i32,
++ }
++
++ let _ = Range { foo: 0 };
++}
--- /dev/null
--- /dev/null
++error: It is more idiomatic to use `v1.iter().enumerate()`
++ --> $DIR/range.rs:5:14
++ |
++LL | let _x = v1.iter().zip(0..v1.len());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::range-zip-with-len` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_parens)]
++
++fn f() -> usize {
++ 42
++}
++
++#[warn(clippy::range_plus_one)]
++fn main() {
++ for _ in 0..2 {}
++ for _ in 0..=2 {}
++
++ for _ in 0..=3 {}
++ for _ in 0..=3 + 1 {}
++
++ for _ in 0..=5 {}
++ for _ in 0..=1 + 5 {}
++
++ for _ in 1..=1 {}
++ for _ in 1..=1 + 1 {}
++
++ for _ in 0..13 + 13 {}
++ for _ in 0..=13 - 7 {}
++
++ for _ in 0..=f() {}
++ for _ in 0..=(1 + f()) {}
++
++ let _ = ..11 - 1;
++ let _ = ..11;
++ let _ = ..11;
++ let _ = (1..=11);
++ let _ = ((f() + 1)..=f());
++
++ const ONE: usize = 1;
++ // integer consts are linted, too
++ for _ in 1..=ONE {}
++
++ let mut vec: Vec<()> = std::vec::Vec::new();
++ vec.drain(..);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_parens)]
++
++fn f() -> usize {
++ 42
++}
++
++#[warn(clippy::range_plus_one)]
++fn main() {
++ for _ in 0..2 {}
++ for _ in 0..=2 {}
++
++ for _ in 0..3 + 1 {}
++ for _ in 0..=3 + 1 {}
++
++ for _ in 0..1 + 5 {}
++ for _ in 0..=1 + 5 {}
++
++ for _ in 1..1 + 1 {}
++ for _ in 1..=1 + 1 {}
++
++ for _ in 0..13 + 13 {}
++ for _ in 0..=13 - 7 {}
++
++ for _ in 0..(1 + f()) {}
++ for _ in 0..=(1 + f()) {}
++
++ let _ = ..11 - 1;
++ let _ = ..=11 - 1;
++ let _ = ..=(11 - 1);
++ let _ = (1..11 + 1);
++ let _ = (f() + 1)..(f() + 1);
++
++ const ONE: usize = 1;
++ // integer consts are linted, too
++ for _ in 1..ONE + ONE {}
++
++ let mut vec: Vec<()> = std::vec::Vec::new();
++ vec.drain(..);
++}
--- /dev/null
--- /dev/null
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:14:14
++ |
++LL | for _ in 0..3 + 1 {}
++ | ^^^^^^^^ help: use: `0..=3`
++ |
++ = note: `-D clippy::range-plus-one` implied by `-D warnings`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:17:14
++ |
++LL | for _ in 0..1 + 5 {}
++ | ^^^^^^^^ help: use: `0..=5`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:20:14
++ |
++LL | for _ in 1..1 + 1 {}
++ | ^^^^^^^^ help: use: `1..=1`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:26:14
++ |
++LL | for _ in 0..(1 + f()) {}
++ | ^^^^^^^^^^^^ help: use: `0..=f()`
++
++error: an exclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:30:13
++ |
++LL | let _ = ..=11 - 1;
++ | ^^^^^^^^^ help: use: `..11`
++ |
++ = note: `-D clippy::range-minus-one` implied by `-D warnings`
++
++error: an exclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:31:13
++ |
++LL | let _ = ..=(11 - 1);
++ | ^^^^^^^^^^^ help: use: `..11`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:32:13
++ |
++LL | let _ = (1..11 + 1);
++ | ^^^^^^^^^^^ help: use: `(1..=11)`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:33:13
++ |
++LL | let _ = (f() + 1)..(f() + 1);
++ | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
++
++error: an inclusive range would be more readable
++ --> $DIR/range_plus_minus_one.rs:37:14
++ |
++LL | for _ in 1..ONE + ONE {}
++ | ^^^^^^^^^^^^ help: use: `1..=ONE`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
++
++use std::boxed::Box;
++use std::rc::Rc;
++
++pub struct MyStruct {}
++
++pub struct SubT<T> {
++ foo: T,
++}
++
++pub enum MyEnum {
++ One,
++ Two,
++}
++
++// Rc<&T>
++
++pub fn test1<T>(foo: &T) {}
++
++pub fn test2(foo: &MyStruct) {}
++
++pub fn test3(foo: &MyEnum) {}
++
++pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
++
++// Rc<Rc<T>>
++
++pub fn test5(a: Rc<bool>) {}
++
++// Rc<Box<T>>
++
++pub fn test6(a: Box<bool>) {}
++
++// Box<&T>
++
++pub fn test7<T>(foo: &T) {}
++
++pub fn test8(foo: &MyStruct) {}
++
++pub fn test9(foo: &MyEnum) {}
++
++pub fn test10_neg(foo: Box<SubT<&usize>>) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
++
++use std::boxed::Box;
++use std::rc::Rc;
++
++pub struct MyStruct {}
++
++pub struct SubT<T> {
++ foo: T,
++}
++
++pub enum MyEnum {
++ One,
++ Two,
++}
++
++// Rc<&T>
++
++pub fn test1<T>(foo: Rc<&T>) {}
++
++pub fn test2(foo: Rc<&MyStruct>) {}
++
++pub fn test3(foo: Rc<&MyEnum>) {}
++
++pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
++
++// Rc<Rc<T>>
++
++pub fn test5(a: Rc<Rc<bool>>) {}
++
++// Rc<Box<T>>
++
++pub fn test6(a: Rc<Box<bool>>) {}
++
++// Box<&T>
++
++pub fn test7<T>(foo: Box<&T>) {}
++
++pub fn test8(foo: Box<&MyStruct>) {}
++
++pub fn test9(foo: Box<&MyEnum>) {}
++
++pub fn test10_neg(foo: Box<SubT<&usize>>) {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: usage of `Rc<&T>`
++ --> $DIR/redundant_allocation.rs:22:22
++ |
++LL | pub fn test1<T>(foo: Rc<&T>) {}
++ | ^^^^^^ help: try: `&T`
++ |
++ = note: `-D clippy::redundant-allocation` implied by `-D warnings`
++
++error: usage of `Rc<&T>`
++ --> $DIR/redundant_allocation.rs:24:19
++ |
++LL | pub fn test2(foo: Rc<&MyStruct>) {}
++ | ^^^^^^^^^^^^^ help: try: `&MyStruct`
++
++error: usage of `Rc<&T>`
++ --> $DIR/redundant_allocation.rs:26:19
++ |
++LL | pub fn test3(foo: Rc<&MyEnum>) {}
++ | ^^^^^^^^^^^ help: try: `&MyEnum`
++
++error: usage of `Rc<Rc<T>>`
++ --> $DIR/redundant_allocation.rs:32:17
++ |
++LL | pub fn test5(a: Rc<Rc<bool>>) {}
++ | ^^^^^^^^^^^^ help: try: `Rc<bool>`
++
++error: usage of `Rc<Box<T>>`
++ --> $DIR/redundant_allocation.rs:36:17
++ |
++LL | pub fn test6(a: Rc<Box<bool>>) {}
++ | ^^^^^^^^^^^^^ help: try: `Box<bool>`
++
++error: usage of `Box<&T>`
++ --> $DIR/redundant_allocation.rs:40:22
++ |
++LL | pub fn test7<T>(foo: Box<&T>) {}
++ | ^^^^^^^ help: try: `&T`
++
++error: usage of `Box<&T>`
++ --> $DIR/redundant_allocation.rs:42:19
++ |
++LL | pub fn test8(foo: Box<&MyStruct>) {}
++ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
++
++error: usage of `Box<&T>`
++ --> $DIR/redundant_allocation.rs:44:19
++ |
++LL | pub fn test9(foo: Box<&MyEnum>) {}
++ | ^^^^^^^^^^^^ help: try: `&MyEnum`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++// rustfix-only-machine-applicable
++
++use std::ffi::OsString;
++use std::path::Path;
++
++fn main() {
++ let _s = ["lorem", "ipsum"].join(" ");
++
++ let s = String::from("foo");
++ let _s = s;
++
++ let s = String::from("foo");
++ let _s = s;
++
++ let s = String::from("foo");
++ let _s = s;
++
++ let _s = Path::new("/a/b/").join("c");
++
++ let _s = Path::new("/a/b/").join("c");
++
++ let _s = OsString::new();
++
++ let _s = OsString::new();
++
++ // Check that lint level works
++ #[allow(clippy::redundant_clone)]
++ let _s = String::new().to_string();
++
++ let tup = (String::from("foo"),);
++ let _t = tup.0;
++
++ let tup_ref = &(String::from("foo"),);
++ let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
++
++ {
++ let x = String::new();
++ let y = &x;
++
++ let _x = x.clone(); // ok; `x` is borrowed by `y`
++
++ let _ = y.len();
++ }
++
++ let x = (String::new(),);
++ let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
++
++ with_branch(Alpha, true);
++ cannot_double_move(Alpha);
++ cannot_move_from_type_with_drop();
++ borrower_propagation();
++ not_consumed();
++ issue_5405();
++}
++
++#[derive(Clone)]
++struct Alpha;
++fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
++ if b {
++ (a.clone(), a)
++ } else {
++ (Alpha, a)
++ }
++}
++
++fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
++ (a.clone(), a)
++}
++
++struct TypeWithDrop {
++ x: String,
++}
++
++impl Drop for TypeWithDrop {
++ fn drop(&mut self) {}
++}
++
++fn cannot_move_from_type_with_drop() -> String {
++ let s = TypeWithDrop { x: String::new() };
++ s.x.clone() // removing this `clone()` summons E0509
++}
++
++fn borrower_propagation() {
++ let s = String::new();
++ let t = String::new();
++
++ {
++ fn b() -> bool {
++ unimplemented!()
++ }
++ let _u = if b() { &s } else { &t };
++
++ // ok; `s` and `t` are possibly borrowed
++ let _s = s.clone();
++ let _t = t.clone();
++ }
++
++ {
++ let _u = || s.len();
++ let _v = [&t; 32];
++ let _s = s.clone(); // ok
++ let _t = t.clone(); // ok
++ }
++
++ {
++ let _u = {
++ let u = Some(&s);
++ let _ = s.clone(); // ok
++ u
++ };
++ let _s = s.clone(); // ok
++ }
++
++ {
++ use std::convert::identity as id;
++ let _u = id(id(&s));
++ let _s = s.clone(); // ok, `u` borrows `s`
++ }
++
++ let _s = s;
++ let _t = t;
++
++ #[derive(Clone)]
++ struct Foo {
++ x: usize,
++ }
++
++ {
++ let f = Foo { x: 123 };
++ let _x = Some(f.x);
++ let _f = f;
++ }
++
++ {
++ let f = Foo { x: 123 };
++ let _x = &f.x;
++ let _f = f.clone(); // ok
++ }
++}
++
++fn not_consumed() {
++ let x = std::path::PathBuf::from("home");
++ let y = x.join("matthias");
++ // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
++ // redundant. (It also does not consume the PathBuf)
++
++ println!("x: {:?}, y: {:?}", x, y);
++
++ let mut s = String::new();
++ s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
++ s.push_str("bar");
++ assert_eq!(s, "bar");
++
++ let t = Some(s);
++ // OK
++ if let Some(x) = t.clone() {
++ println!("{}", x);
++ }
++ if let Some(x) = t {
++ println!("{}", x);
++ }
++}
++
++#[allow(clippy::clone_on_copy)]
++fn issue_5405() {
++ let a: [String; 1] = [String::from("foo")];
++ let _b: String = a[0].clone();
++
++ let c: [usize; 2] = [2, 3];
++ let _d: usize = c[1].clone();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// rustfix-only-machine-applicable
++
++use std::ffi::OsString;
++use std::path::Path;
++
++fn main() {
++ let _s = ["lorem", "ipsum"].join(" ").to_string();
++
++ let s = String::from("foo");
++ let _s = s.clone();
++
++ let s = String::from("foo");
++ let _s = s.to_string();
++
++ let s = String::from("foo");
++ let _s = s.to_owned();
++
++ let _s = Path::new("/a/b/").join("c").to_owned();
++
++ let _s = Path::new("/a/b/").join("c").to_path_buf();
++
++ let _s = OsString::new().to_owned();
++
++ let _s = OsString::new().to_os_string();
++
++ // Check that lint level works
++ #[allow(clippy::redundant_clone)]
++ let _s = String::new().to_string();
++
++ let tup = (String::from("foo"),);
++ let _t = tup.0.clone();
++
++ let tup_ref = &(String::from("foo"),);
++ let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
++
++ {
++ let x = String::new();
++ let y = &x;
++
++ let _x = x.clone(); // ok; `x` is borrowed by `y`
++
++ let _ = y.len();
++ }
++
++ let x = (String::new(),);
++ let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
++
++ with_branch(Alpha, true);
++ cannot_double_move(Alpha);
++ cannot_move_from_type_with_drop();
++ borrower_propagation();
++ not_consumed();
++ issue_5405();
++}
++
++#[derive(Clone)]
++struct Alpha;
++fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
++ if b {
++ (a.clone(), a.clone())
++ } else {
++ (Alpha, a)
++ }
++}
++
++fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
++ (a.clone(), a)
++}
++
++struct TypeWithDrop {
++ x: String,
++}
++
++impl Drop for TypeWithDrop {
++ fn drop(&mut self) {}
++}
++
++fn cannot_move_from_type_with_drop() -> String {
++ let s = TypeWithDrop { x: String::new() };
++ s.x.clone() // removing this `clone()` summons E0509
++}
++
++fn borrower_propagation() {
++ let s = String::new();
++ let t = String::new();
++
++ {
++ fn b() -> bool {
++ unimplemented!()
++ }
++ let _u = if b() { &s } else { &t };
++
++ // ok; `s` and `t` are possibly borrowed
++ let _s = s.clone();
++ let _t = t.clone();
++ }
++
++ {
++ let _u = || s.len();
++ let _v = [&t; 32];
++ let _s = s.clone(); // ok
++ let _t = t.clone(); // ok
++ }
++
++ {
++ let _u = {
++ let u = Some(&s);
++ let _ = s.clone(); // ok
++ u
++ };
++ let _s = s.clone(); // ok
++ }
++
++ {
++ use std::convert::identity as id;
++ let _u = id(id(&s));
++ let _s = s.clone(); // ok, `u` borrows `s`
++ }
++
++ let _s = s.clone();
++ let _t = t.clone();
++
++ #[derive(Clone)]
++ struct Foo {
++ x: usize,
++ }
++
++ {
++ let f = Foo { x: 123 };
++ let _x = Some(f.x);
++ let _f = f.clone();
++ }
++
++ {
++ let f = Foo { x: 123 };
++ let _x = &f.x;
++ let _f = f.clone(); // ok
++ }
++}
++
++fn not_consumed() {
++ let x = std::path::PathBuf::from("home");
++ let y = x.clone().join("matthias");
++ // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
++ // redundant. (It also does not consume the PathBuf)
++
++ println!("x: {:?}, y: {:?}", x, y);
++
++ let mut s = String::new();
++ s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
++ s.push_str("bar");
++ assert_eq!(s, "bar");
++
++ let t = Some(s);
++ // OK
++ if let Some(x) = t.clone() {
++ println!("{}", x);
++ }
++ if let Some(x) = t {
++ println!("{}", x);
++ }
++}
++
++#[allow(clippy::clone_on_copy)]
++fn issue_5405() {
++ let a: [String; 1] = [String::from("foo")];
++ let _b: String = a[0].clone();
++
++ let c: [usize; 2] = [2, 3];
++ let _d: usize = c[1].clone();
++}
--- /dev/null
--- /dev/null
++error: redundant clone
++ --> $DIR/redundant_clone.rs:8:42
++ |
++LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
++ | ^^^^^^^^^^^^ help: remove this
++ |
++ = note: `-D clippy::redundant-clone` implied by `-D warnings`
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:8:14
++ |
++LL | let _s = ["lorem", "ipsum"].join(" ").to_string();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:11:15
++ |
++LL | let _s = s.clone();
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:11:14
++ |
++LL | let _s = s.clone();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:14:15
++ |
++LL | let _s = s.to_string();
++ | ^^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:14:14
++ |
++LL | let _s = s.to_string();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:17:15
++ |
++LL | let _s = s.to_owned();
++ | ^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:17:14
++ |
++LL | let _s = s.to_owned();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:19:42
++ |
++LL | let _s = Path::new("/a/b/").join("c").to_owned();
++ | ^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:19:14
++ |
++LL | let _s = Path::new("/a/b/").join("c").to_owned();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:21:42
++ |
++LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
++ | ^^^^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:21:14
++ |
++LL | let _s = Path::new("/a/b/").join("c").to_path_buf();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:23:29
++ |
++LL | let _s = OsString::new().to_owned();
++ | ^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:23:14
++ |
++LL | let _s = OsString::new().to_owned();
++ | ^^^^^^^^^^^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:25:29
++ |
++LL | let _s = OsString::new().to_os_string();
++ | ^^^^^^^^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:25:14
++ |
++LL | let _s = OsString::new().to_os_string();
++ | ^^^^^^^^^^^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:32:19
++ |
++LL | let _t = tup.0.clone();
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:32:14
++ |
++LL | let _t = tup.0.clone();
++ | ^^^^^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:61:22
++ |
++LL | (a.clone(), a.clone())
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:61:21
++ |
++LL | (a.clone(), a.clone())
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:121:15
++ |
++LL | let _s = s.clone();
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:121:14
++ |
++LL | let _s = s.clone();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:122:15
++ |
++LL | let _t = t.clone();
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:122:14
++ |
++LL | let _t = t.clone();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:132:19
++ |
++LL | let _f = f.clone();
++ | ^^^^^^^^ help: remove this
++ |
++note: this value is dropped without further use
++ --> $DIR/redundant_clone.rs:132:18
++ |
++LL | let _f = f.clone();
++ | ^
++
++error: redundant clone
++ --> $DIR/redundant_clone.rs:144:14
++ |
++LL | let y = x.clone().join("matthias");
++ | ^^^^^^^^ help: remove this
++ |
++note: cloned value is neither consumed nor mutated
++ --> $DIR/redundant_clone.rs:144:13
++ |
++LL | let y = x.clone().join("matthias");
++ | ^^^^^^^^^
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++// non rustfixable, see redundant_closure_call_fixable.rs
++
++#![warn(clippy::redundant_closure_call)]
++
++fn main() {
++ let mut i = 1;
++ let mut k = (|m| m + 1)(i);
++
++ k = (|a, b| a * b)(1, 5);
++
++ let closure = || 32;
++ i = closure();
++
++ let closure = |i| i + 1;
++ i = closure(3);
++
++ i = closure(4);
++
++ #[allow(clippy::needless_return)]
++ (|| return 2)();
++ (|| -> Option<i32> { None? })();
++ (|| -> Result<i32, i32> { Err(2)? })();
++}
--- /dev/null
--- /dev/null
++error: Closure called just once immediately after it was declared
++ --> $DIR/redundant_closure_call.rs:12:5
++ |
++LL | i = closure();
++ | ^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
++
++error: Closure called just once immediately after it was declared
++ --> $DIR/redundant_closure_call.rs:15:5
++ |
++LL | i = closure(3);
++ | ^^^^^^^^^^^^^^
++
++error: Try not to call a closure in the expression where it is declared.
++ --> $DIR/redundant_closure_call.rs:7:17
++ |
++LL | let mut k = (|m| m + 1)(i);
++ | ^^^^^^^^^^^^^^
++
++error: Try not to call a closure in the expression where it is declared.
++ --> $DIR/redundant_closure_call.rs:9:9
++ |
++LL | k = (|a, b| a * b)(1, 5);
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::redundant_closure_call)]
++#![allow(unused)]
++
++fn main() {
++ let a = 42;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::redundant_closure_call)]
++#![allow(unused)]
++
++fn main() {
++ let a = (|| 42)();
++}
--- /dev/null
--- /dev/null
++error: Try not to call a closure in the expression where it is declared.
++ --> $DIR/redundant_closure_call_fixable.rs:7:13
++ |
++LL | let a = (|| 42)();
++ | ^^^^^^^^^ help: Try doing something like: : `42`
++ |
++ = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::redundant_field_names)]
++#![allow(clippy::no_effect, dead_code, unused_variables)]
++
++#[macro_use]
++extern crate derive_new;
++
++use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
++
++mod foo {
++ pub const BAR: u8 = 0;
++}
++
++struct Person {
++ gender: u8,
++ age: u8,
++ name: u8,
++ buzz: u64,
++ foo: u8,
++}
++
++#[derive(new)]
++pub struct S {
++ v: String,
++}
++
++fn main() {
++ let gender: u8 = 42;
++ let age = 0;
++ let fizz: u64 = 0;
++ let name: u8 = 0;
++
++ let me = Person {
++ gender,
++ age,
++
++ name, //should be ok
++ buzz: fizz, //should be ok
++ foo: foo::BAR, //should be ok
++ };
++
++ // Range expressions
++ let (start, end) = (0, 0);
++
++ let _ = start..;
++ let _ = ..end;
++ let _ = start..end;
++
++ let _ = ..=end;
++ let _ = start..=end;
++
++ // Issue #2799
++ let _: Vec<_> = (start..end).collect();
++
++ // hand-written Range family structs are linted
++ let _ = RangeFrom { start };
++ let _ = RangeTo { end };
++ let _ = Range { start, end };
++ let _ = RangeInclusive::new(start, end);
++ let _ = RangeToInclusive { end };
++}
++
++fn issue_3476() {
++ fn foo<T>() {}
++
++ struct S {
++ foo: fn(),
++ }
++
++ S { foo: foo::<i32> };
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::redundant_field_names)]
++#![allow(clippy::no_effect, dead_code, unused_variables)]
++
++#[macro_use]
++extern crate derive_new;
++
++use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
++
++mod foo {
++ pub const BAR: u8 = 0;
++}
++
++struct Person {
++ gender: u8,
++ age: u8,
++ name: u8,
++ buzz: u64,
++ foo: u8,
++}
++
++#[derive(new)]
++pub struct S {
++ v: String,
++}
++
++fn main() {
++ let gender: u8 = 42;
++ let age = 0;
++ let fizz: u64 = 0;
++ let name: u8 = 0;
++
++ let me = Person {
++ gender: gender,
++ age: age,
++
++ name, //should be ok
++ buzz: fizz, //should be ok
++ foo: foo::BAR, //should be ok
++ };
++
++ // Range expressions
++ let (start, end) = (0, 0);
++
++ let _ = start..;
++ let _ = ..end;
++ let _ = start..end;
++
++ let _ = ..=end;
++ let _ = start..=end;
++
++ // Issue #2799
++ let _: Vec<_> = (start..end).collect();
++
++ // hand-written Range family structs are linted
++ let _ = RangeFrom { start: start };
++ let _ = RangeTo { end: end };
++ let _ = Range { start: start, end: end };
++ let _ = RangeInclusive::new(start, end);
++ let _ = RangeToInclusive { end: end };
++}
++
++fn issue_3476() {
++ fn foo<T>() {}
++
++ struct S {
++ foo: fn(),
++ }
++
++ S { foo: foo::<i32> };
++}
--- /dev/null
--- /dev/null
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:34:9
++ |
++LL | gender: gender,
++ | ^^^^^^^^^^^^^^ help: replace it with: `gender`
++ |
++ = note: `-D clippy::redundant-field-names` implied by `-D warnings`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:35:9
++ |
++LL | age: age,
++ | ^^^^^^^^ help: replace it with: `age`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:56:25
++ |
++LL | let _ = RangeFrom { start: start };
++ | ^^^^^^^^^^^^ help: replace it with: `start`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:57:23
++ |
++LL | let _ = RangeTo { end: end };
++ | ^^^^^^^^ help: replace it with: `end`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:58:21
++ |
++LL | let _ = Range { start: start, end: end };
++ | ^^^^^^^^^^^^ help: replace it with: `start`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:58:35
++ |
++LL | let _ = Range { start: start, end: end };
++ | ^^^^^^^^ help: replace it with: `end`
++
++error: redundant field names in struct initialization
++ --> $DIR/redundant_field_names.rs:60:32
++ |
++LL | let _ = RangeToInclusive { end: end };
++ | ^^^^^^^^ help: replace it with: `end`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all)]
++#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
++
++fn main() {
++ if Ok::<i32, i32>(42).is_ok() {}
++
++ if Err::<i32, i32>(42).is_err() {}
++
++ if None::<()>.is_none() {}
++
++ if Some(42).is_some() {}
++
++ if Some(42).is_some() {
++ foo();
++ } else {
++ bar();
++ }
++
++ while Some(42).is_some() {}
++
++ while Some(42).is_none() {}
++
++ while None::<()>.is_none() {}
++
++ while Ok::<i32, i32>(10).is_ok() {}
++
++ while Ok::<i32, i32>(10).is_err() {}
++
++ let mut v = vec![1, 2, 3];
++ while v.pop().is_some() {
++ foo();
++ }
++
++ if Ok::<i32, i32>(42).is_ok() {}
++
++ if Err::<i32, i32>(42).is_err() {}
++
++ if None::<i32>.is_none() {}
++
++ if Some(42).is_some() {}
++
++ if let Ok(x) = Ok::<i32, i32>(42) {
++ println!("{}", x);
++ }
++
++ Ok::<i32, i32>(42).is_ok();
++
++ Ok::<i32, i32>(42).is_err();
++
++ Err::<i32, i32>(42).is_err();
++
++ Err::<i32, i32>(42).is_ok();
++
++ Some(42).is_some();
++
++ None::<()>.is_none();
++
++ let _ = None::<()>.is_none();
++
++ let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false };
++
++ let opt = Some(false);
++ let x = if opt.is_some() { true } else { false };
++ takes_bool(x);
++
++ issue5504();
++
++ let _ = if gen_opt().is_some() {
++ 1
++ } else if gen_opt().is_none() {
++ 2
++ } else if gen_res().is_ok() {
++ 3
++ } else if gen_res().is_err() {
++ 4
++ } else {
++ 5
++ };
++}
++
++fn gen_opt() -> Option<()> {
++ None
++}
++
++fn gen_res() -> Result<(), ()> {
++ Ok(())
++}
++
++fn takes_bool(_: bool) {}
++
++fn foo() {}
++
++fn bar() {}
++
++macro_rules! m {
++ () => {
++ Some(42u32)
++ };
++}
++
++fn issue5504() {
++ fn result_opt() -> Result<Option<i32>, i32> {
++ Err(42)
++ }
++
++ fn try_result_opt() -> Result<i32, i32> {
++ while r#try!(result_opt()).is_some() {}
++ if r#try!(result_opt()).is_some() {}
++ Ok(42)
++ }
++
++ try_result_opt();
++
++ if m!().is_some() {}
++ while m!().is_some() {}
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all)]
++#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
++
++fn main() {
++ 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 Some(_) = Some(42) {
++ foo();
++ } else {
++ bar();
++ }
++
++ while let Some(_) = Some(42) {}
++
++ while let None = Some(42) {}
++
++ while let None = None::<()> {}
++
++ while let Ok(_) = Ok::<i32, i32>(10) {}
++
++ while let Err(_) = Ok::<i32, i32>(10) {}
++
++ let mut v = vec![1, 2, 3];
++ while let Some(_) = v.pop() {
++ foo();
++ }
++
++ if Ok::<i32, i32>(42).is_ok() {}
++
++ if Err::<i32, i32>(42).is_err() {}
++
++ if None::<i32>.is_none() {}
++
++ if Some(42).is_some() {}
++
++ if let Ok(x) = Ok::<i32, i32>(42) {
++ println!("{}", x);
++ }
++
++ match Ok::<i32, i32>(42) {
++ Ok(_) => true,
++ Err(_) => false,
++ };
++
++ match Ok::<i32, i32>(42) {
++ Ok(_) => false,
++ Err(_) => true,
++ };
++
++ match Err::<i32, i32>(42) {
++ Ok(_) => false,
++ Err(_) => true,
++ };
++
++ match Err::<i32, i32>(42) {
++ Ok(_) => true,
++ Err(_) => false,
++ };
++
++ match Some(42) {
++ Some(_) => true,
++ None => false,
++ };
++
++ match None::<()> {
++ Some(_) => false,
++ None => true,
++ };
++
++ let _ = match None::<()> {
++ Some(_) => false,
++ None => true,
++ };
++
++ let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
++
++ let opt = Some(false);
++ let x = if let Some(_) = opt { true } else { false };
++ takes_bool(x);
++
++ issue5504();
++
++ let _ = if let Some(_) = gen_opt() {
++ 1
++ } else if let None = gen_opt() {
++ 2
++ } else if let Ok(_) = gen_res() {
++ 3
++ } else if let Err(_) = gen_res() {
++ 4
++ } else {
++ 5
++ };
++}
++
++fn gen_opt() -> Option<()> {
++ None
++}
++
++fn gen_res() -> Result<(), ()> {
++ Ok(())
++}
++
++fn takes_bool(_: bool) {}
++
++fn foo() {}
++
++fn bar() {}
++
++macro_rules! m {
++ () => {
++ Some(42u32)
++ };
++}
++
++fn issue5504() {
++ fn result_opt() -> Result<Option<i32>, i32> {
++ Err(42)
++ }
++
++ fn try_result_opt() -> Result<i32, i32> {
++ while let Some(_) = r#try!(result_opt()) {}
++ if let Some(_) = r#try!(result_opt()) {}
++ Ok(42)
++ }
++
++ try_result_opt();
++
++ if let Some(_) = m!() {}
++ while let Some(_) = m!() {}
++}
--- /dev/null
--- /dev/null
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:8:12
++ |
++LL | if let Ok(_) = Ok::<i32, i32>(42) {}
++ | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
++ |
++ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
++
++error: redundant pattern matching, consider using `is_err()`
++ --> $DIR/redundant_pattern_matching.rs:10:12
++ |
++LL | if let Err(_) = Err::<i32, i32>(42) {}
++ | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:12:12
++ |
++LL | if let None = None::<()> {}
++ | -------^^^^------------- help: try this: `if None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:14:12
++ |
++LL | if let Some(_) = Some(42) {}
++ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:16:12
++ |
++LL | if let Some(_) = Some(42) {
++ | -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:22:15
++ |
++LL | while let Some(_) = Some(42) {}
++ | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:24:15
++ |
++LL | while let None = Some(42) {}
++ | ----------^^^^----------- help: try this: `while Some(42).is_none()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:26:15
++ |
++LL | while let None = None::<()> {}
++ | ----------^^^^------------- help: try this: `while None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:28:15
++ |
++LL | while let Ok(_) = Ok::<i32, i32>(10) {}
++ | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++ --> $DIR/redundant_pattern_matching.rs:30:15
++ |
++LL | while let Err(_) = Ok::<i32, i32>(10) {}
++ | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:33:15
++ |
++LL | while let Some(_) = v.pop() {
++ | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
++
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:49:5
++ |
++LL | / match Ok::<i32, i32>(42) {
++LL | | Ok(_) => true,
++LL | | Err(_) => false,
++LL | | };
++ | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++ --> $DIR/redundant_pattern_matching.rs:54:5
++ |
++LL | / match Ok::<i32, i32>(42) {
++LL | | Ok(_) => false,
++LL | | Err(_) => true,
++LL | | };
++ | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_err()`
++ --> $DIR/redundant_pattern_matching.rs:59:5
++ |
++LL | / match Err::<i32, i32>(42) {
++LL | | Ok(_) => false,
++LL | | Err(_) => true,
++LL | | };
++ | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:64:5
++ |
++LL | / match Err::<i32, i32>(42) {
++LL | | Ok(_) => true,
++LL | | Err(_) => false,
++LL | | };
++ | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:69:5
++ |
++LL | / match Some(42) {
++LL | | Some(_) => true,
++LL | | None => false,
++LL | | };
++ | |_____^ help: try this: `Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:74:5
++ |
++LL | / match None::<()> {
++LL | | Some(_) => false,
++LL | | None => true,
++LL | | };
++ | |_____^ help: try this: `None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:79:13
++ |
++LL | let _ = match None::<()> {
++ | _____________^
++LL | | Some(_) => false,
++LL | | None => true,
++LL | | };
++ | |_____^ help: try this: `None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:84:20
++ |
++LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
++ | -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:87:20
++ |
++LL | let x = if let Some(_) = opt { true } else { false };
++ | -------^^^^^^^------ help: try this: `if opt.is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:92:20
++ |
++LL | let _ = if let Some(_) = gen_opt() {
++ | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++ --> $DIR/redundant_pattern_matching.rs:94:19
++ |
++LL | } else if let None = gen_opt() {
++ | -------^^^^------------ help: try this: `if gen_opt().is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++ --> $DIR/redundant_pattern_matching.rs:96:19
++ |
++LL | } else if let Ok(_) = gen_res() {
++ | -------^^^^^------------ help: try this: `if gen_res().is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++ --> $DIR/redundant_pattern_matching.rs:98:19
++ |
++LL | } else if let Err(_) = gen_res() {
++ | -------^^^^^^------------ help: try this: `if gen_res().is_err()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:131:19
++ |
++LL | while let Some(_) = r#try!(result_opt()) {}
++ | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:132:16
++ |
++LL | if let Some(_) = r#try!(result_opt()) {}
++ | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:138:12
++ |
++LL | if let Some(_) = m!() {}
++ | -------^^^^^^^------- help: try this: `if m!().is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++ --> $DIR/redundant_pattern_matching.rs:139:15
++ |
++LL | while let Some(_) = m!() {}
++ | ----------^^^^^^^------- help: try this: `while m!().is_some()`
++
++error: aborting due to 28 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::redundant_pub_crate)]
++
++mod m1 {
++ fn f() {}
++ pub fn g() {} // private due to m1
++ pub fn h() {}
++
++ mod m1_1 {
++ fn f() {}
++ pub fn g() {} // private due to m1_1 and m1
++ pub fn h() {}
++ }
++
++ pub mod m1_2 {
++ // ^ private due to m1
++ fn f() {}
++ pub fn g() {} // private due to m1_2 and m1
++ pub fn h() {}
++ }
++
++ pub mod m1_3 {
++ fn f() {}
++ pub fn g() {} // private due to m1
++ pub fn h() {}
++ }
++}
++
++pub(crate) mod m2 {
++ fn f() {}
++ pub fn g() {} // already crate visible due to m2
++ pub fn h() {}
++
++ mod m2_1 {
++ fn f() {}
++ pub fn g() {} // private due to m2_1
++ pub fn h() {}
++ }
++
++ pub mod m2_2 {
++ // ^ already crate visible due to m2
++ fn f() {}
++ pub fn g() {} // already crate visible due to m2_2 and m2
++ pub fn h() {}
++ }
++
++ pub mod m2_3 {
++ fn f() {}
++ pub fn g() {} // already crate visible due to m2
++ pub fn h() {}
++ }
++}
++
++pub mod m3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m3 is exported
++ pub fn h() {}
++
++ mod m3_1 {
++ fn f() {}
++ pub fn g() {} // private due to m3_1
++ pub fn h() {}
++ }
++
++ pub(crate) mod m3_2 {
++ // ^ ok
++ fn f() {}
++ pub fn g() {} // already crate visible due to m3_2
++ pub fn h() {}
++ }
++
++ pub mod m3_3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m3 and m3_3 are exported
++ pub fn h() {}
++ }
++}
++
++mod m4 {
++ fn f() {}
++ pub fn g() {} // private: not re-exported by `pub use m4::*`
++ pub fn h() {}
++
++ mod m4_1 {
++ fn f() {}
++ pub fn g() {} // private due to m4_1
++ pub fn h() {}
++ }
++
++ pub mod m4_2 {
++ // ^ private: not re-exported by `pub use m4::*`
++ fn f() {}
++ pub fn g() {} // private due to m4_2
++ pub fn h() {}
++ }
++
++ pub mod m4_3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
++ pub fn h() {}
++ }
++}
++
++pub use m4::*;
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::redundant_pub_crate)]
++
++mod m1 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m1
++ pub fn h() {}
++
++ mod m1_1 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m1_1 and m1
++ pub fn h() {}
++ }
++
++ pub(crate) mod m1_2 {
++ // ^ private due to m1
++ fn f() {}
++ pub(crate) fn g() {} // private due to m1_2 and m1
++ pub fn h() {}
++ }
++
++ pub mod m1_3 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m1
++ pub fn h() {}
++ }
++}
++
++pub(crate) mod m2 {
++ fn f() {}
++ pub(crate) fn g() {} // already crate visible due to m2
++ pub fn h() {}
++
++ mod m2_1 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m2_1
++ pub fn h() {}
++ }
++
++ pub(crate) mod m2_2 {
++ // ^ already crate visible due to m2
++ fn f() {}
++ pub(crate) fn g() {} // already crate visible due to m2_2 and m2
++ pub fn h() {}
++ }
++
++ pub mod m2_3 {
++ fn f() {}
++ pub(crate) fn g() {} // already crate visible due to m2
++ pub fn h() {}
++ }
++}
++
++pub mod m3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m3 is exported
++ pub fn h() {}
++
++ mod m3_1 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m3_1
++ pub fn h() {}
++ }
++
++ pub(crate) mod m3_2 {
++ // ^ ok
++ fn f() {}
++ pub(crate) fn g() {} // already crate visible due to m3_2
++ pub fn h() {}
++ }
++
++ pub mod m3_3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m3 and m3_3 are exported
++ pub fn h() {}
++ }
++}
++
++mod m4 {
++ fn f() {}
++ pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
++ pub fn h() {}
++
++ mod m4_1 {
++ fn f() {}
++ pub(crate) fn g() {} // private due to m4_1
++ pub fn h() {}
++ }
++
++ pub(crate) mod m4_2 {
++ // ^ private: not re-exported by `pub use m4::*`
++ fn f() {}
++ pub(crate) fn g() {} // private due to m4_2
++ pub fn h() {}
++ }
++
++ pub mod m4_3 {
++ fn f() {}
++ pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
++ pub fn h() {}
++ }
++}
++
++pub use m4::*;
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:7:5
++ |
++LL | pub(crate) fn g() {} // private due to m1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++ |
++ = note: `-D clippy::redundant-pub-crate` implied by `-D warnings`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:12:9
++ |
++LL | pub(crate) fn g() {} // private due to m1_1 and m1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) module inside private module
++ --> $DIR/redundant_pub_crate.rs:16:5
++ |
++LL | pub(crate) mod m1_2 {
++ | ----------^^^^^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:19:9
++ |
++LL | pub(crate) fn g() {} // private due to m1_2 and m1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:25:9
++ |
++LL | pub(crate) fn g() {} // private due to m1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:32:5
++ |
++LL | pub(crate) fn g() {} // already crate visible due to m2
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:37:9
++ |
++LL | pub(crate) fn g() {} // private due to m2_1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) module inside private module
++ --> $DIR/redundant_pub_crate.rs:41:5
++ |
++LL | pub(crate) mod m2_2 {
++ | ----------^^^^^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:44:9
++ |
++LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:50:9
++ |
++LL | pub(crate) fn g() {} // already crate visible due to m2
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:62:9
++ |
++LL | pub(crate) fn g() {} // private due to m3_1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:69:9
++ |
++LL | pub(crate) fn g() {} // already crate visible due to m3_2
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:82:5
++ |
++LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:87:9
++ |
++LL | pub(crate) fn g() {} // private due to m4_1
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) module inside private module
++ --> $DIR/redundant_pub_crate.rs:91:5
++ |
++LL | pub(crate) mod m4_2 {
++ | ----------^^^^^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: pub(crate) function inside private module
++ --> $DIR/redundant_pub_crate.rs:94:9
++ |
++LL | pub(crate) fn g() {} // private due to m4_2
++ | ----------^^^^^
++ | |
++ | help: consider using: `pub`
++
++error: aborting due to 16 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++
++#[derive(Debug)]
++struct Foo {}
++
++const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
++
++const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
++
++const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++const VAR_SIX: &u8 = &5;
++
++const VAR_HEIGHT: &Foo = &Foo {};
++
++const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
++
++static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SIX: &u8 = &5;
++
++static STATIC_VAR_HEIGHT: &Foo = &Foo {};
++
++static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++fn main() {
++ let false_positive: &'static str = "test";
++}
++
++trait Bar {
++ const TRAIT_VAR: &'static str;
++}
++
++impl Foo {
++ const IMPL_VAR: &'static str = "var";
++}
++
++impl Bar for Foo {
++ const TRAIT_VAR: &'static str = "foo";
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused)]
++
++#[derive(Debug)]
++struct Foo {}
++
++const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
++
++const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++
++const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++const VAR_SIX: &'static u8 = &5;
++
++const VAR_HEIGHT: &'static Foo = &Foo {};
++
++const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
++
++static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SIX: &'static u8 = &5;
++
++static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
++
++static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++fn main() {
++ let false_positive: &'static str = "test";
++}
++
++trait Bar {
++ const TRAIT_VAR: &'static str;
++}
++
++impl Foo {
++ const IMPL_VAR: &'static str = "var";
++}
++
++impl Bar for Foo {
++ const TRAIT_VAR: &'static str = "foo";
++}
--- /dev/null
--- /dev/null
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:8:17
++ |
++LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++ |
++ = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:12:21
++ |
++LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:14:32
++ |
++LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:14:47
++ |
++LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:16:17
++ |
++LL | const VAR_SIX: &'static u8 = &5;
++ | -^^^^^^^--- help: consider removing `'static`: `&u8`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:18:20
++ |
++LL | const VAR_HEIGHT: &'static Foo = &Foo {};
++ | -^^^^^^^---- help: consider removing `'static`: `&Foo`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:20:19
++ |
++LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++ | -^^^^^^^----- help: consider removing `'static`: `&[u8]`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:22:19
++ |
++LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++ | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:24:19
++ |
++LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++ | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:26:25
++ |
++LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:30:29
++ |
++LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:32:25
++ |
++LL | static STATIC_VAR_SIX: &'static u8 = &5;
++ | -^^^^^^^--- help: consider removing `'static`: `&u8`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:34:28
++ |
++LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
++ | -^^^^^^^---- help: consider removing `'static`: `&Foo`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:36:27
++ |
++LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
++ | -^^^^^^^----- help: consider removing `'static`: `&[u8]`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:38:27
++ |
++LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++ | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes.rs:40:27
++ |
++LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++ | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
++
++error: aborting due to 16 previous errors
++
--- /dev/null
--- /dev/null
++// these are rustfixable, but run-rustfix tests cannot handle them
++
++const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++
++const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++
++static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:3:18
++ |
++LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++ | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
++ |
++ = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:3:30
++ |
++LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:5:29
++ |
++LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++ | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
++
++error: Constants have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:5:39
++ |
++LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:7:40
++ |
++LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:7:55
++ |
++LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:9:26
++ |
++LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++ | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:9:38
++ |
++LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:11:37
++ |
++LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++ | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
++
++error: Statics have by default a `'static` lifetime
++ --> $DIR/redundant_static_lifetimes_multiple.rs:11:47
++ |
++LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++ | -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused)]
++#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)]
++
++extern crate regex;
++
++use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet};
++use regex::{Regex, RegexBuilder, RegexSet};
++
++const OPENING_PAREN: &str = "(";
++const NOT_A_REAL_REGEX: &str = "foobar";
++
++fn syntax_error() {
++ let pipe_in_wrong_position = Regex::new("|");
++ let pipe_in_wrong_position_builder = RegexBuilder::new("|");
++ let wrong_char_ranice = Regex::new("[z-a]");
++ let some_unicode = Regex::new("[é-è]");
++
++ let some_regex = Regex::new(OPENING_PAREN);
++
++ let binary_pipe_in_wrong_position = BRegex::new("|");
++ let some_binary_regex = BRegex::new(OPENING_PAREN);
++ let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
++
++ let closing_paren = ")";
++ let not_linted = Regex::new(closing_paren);
++
++ let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
++ let bset = BRegexSet::new(&[
++ r"[a-z]+@[a-z]+\.(com|org|net)",
++ r"[a-z]+\.(com|org|net)",
++ r".", // regression test
++ ]);
++
++ let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
++ let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
++
++ let raw_string_error = Regex::new(r"[...\/...]");
++ let raw_string_error = Regex::new(r#"[...\/...]"#);
++}
++
++fn trivial_regex() {
++ let trivial_eq = Regex::new("^foobar$");
++
++ let trivial_eq_builder = RegexBuilder::new("^foobar$");
++
++ let trivial_starts_with = Regex::new("^foobar");
++
++ let trivial_ends_with = Regex::new("foobar$");
++
++ let trivial_contains = Regex::new("foobar");
++
++ let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
++
++ let trivial_backslash = Regex::new("a\\.b");
++
++ // unlikely corner cases
++ let trivial_empty = Regex::new("");
++
++ let trivial_empty = Regex::new("^");
++
++ let trivial_empty = Regex::new("^$");
++
++ let binary_trivial_empty = BRegex::new("^$");
++
++ // non-trivial regexes
++ let non_trivial_dot = Regex::new("a.b");
++ let non_trivial_dot_builder = RegexBuilder::new("a.b");
++ let non_trivial_eq = Regex::new("^foo|bar$");
++ let non_trivial_starts_with = Regex::new("^foo|bar");
++ let non_trivial_ends_with = Regex::new("^foo|bar");
++ let non_trivial_ends_with = Regex::new("foo|bar");
++ let non_trivial_binary = BRegex::new("foo|bar");
++ let non_trivial_binary_builder = BRegexBuilder::new("foo|bar");
++}
++
++fn main() {
++ syntax_error();
++ trivial_regex();
++}
--- /dev/null
--- /dev/null
++error: trivial regex
++ --> $DIR/regex.rs:13:45
++ |
++LL | let pipe_in_wrong_position = Regex::new("|");
++ | ^^^
++ |
++ = note: `-D clippy::trivial-regex` implied by `-D warnings`
++ = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++ --> $DIR/regex.rs:14:60
++ |
++LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|");
++ | ^^^
++ |
++ = help: the regex is unlikely to be useful as it is
++
++error: regex syntax error: invalid character class range, the start must be <= the end
++ --> $DIR/regex.rs:15:42
++ |
++LL | let wrong_char_ranice = Regex::new("[z-a]");
++ | ^^^
++ |
++ = note: `-D clippy::invalid-regex` implied by `-D warnings`
++
++error: regex syntax error: invalid character class range, the start must be <= the end
++ --> $DIR/regex.rs:16:37
++ |
++LL | let some_unicode = Regex::new("[é-è]");
++ | ^^^
++
++error: regex syntax error on position 0: unclosed group
++ --> $DIR/regex.rs:18:33
++ |
++LL | let some_regex = Regex::new(OPENING_PAREN);
++ | ^^^^^^^^^^^^^
++
++error: trivial regex
++ --> $DIR/regex.rs:20:53
++ |
++LL | let binary_pipe_in_wrong_position = BRegex::new("|");
++ | ^^^
++ |
++ = help: the regex is unlikely to be useful as it is
++
++error: regex syntax error on position 0: unclosed group
++ --> $DIR/regex.rs:21:41
++ |
++LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
++ | ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++ --> $DIR/regex.rs:22:56
++ |
++LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
++ | ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++ --> $DIR/regex.rs:34:37
++ |
++LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
++ | ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++ --> $DIR/regex.rs:35:39
++ |
++LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
++ | ^^^^^^^^^^^^^
++
++error: regex syntax error: unrecognized escape sequence
++ --> $DIR/regex.rs:37:45
++ |
++LL | let raw_string_error = Regex::new(r"[...//...]");
++ | ^^
++
++error: regex syntax error: unrecognized escape sequence
++ --> $DIR/regex.rs:38:46
++ |
++LL | let raw_string_error = Regex::new(r#"[...//...]"#);
++ | ^^
++
++error: trivial regex
++ --> $DIR/regex.rs:42:33
++ |
++LL | let trivial_eq = Regex::new("^foobar$");
++ | ^^^^^^^^^^
++ |
++ = help: consider using `==` on `str`s
++
++error: trivial regex
++ --> $DIR/regex.rs:44:48
++ |
++LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
++ | ^^^^^^^^^^
++ |
++ = help: consider using `==` on `str`s
++
++error: trivial regex
++ --> $DIR/regex.rs:46:42
++ |
++LL | let trivial_starts_with = Regex::new("^foobar");
++ | ^^^^^^^^^
++ |
++ = help: consider using `str::starts_with`
++
++error: trivial regex
++ --> $DIR/regex.rs:48:40
++ |
++LL | let trivial_ends_with = Regex::new("foobar$");
++ | ^^^^^^^^^
++ |
++ = help: consider using `str::ends_with`
++
++error: trivial regex
++ --> $DIR/regex.rs:50:39
++ |
++LL | let trivial_contains = Regex::new("foobar");
++ | ^^^^^^^^
++ |
++ = help: consider using `str::contains`
++
++error: trivial regex
++ --> $DIR/regex.rs:52:39
++ |
++LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
++ | ^^^^^^^^^^^^^^^^
++ |
++ = help: consider using `str::contains`
++
++error: trivial regex
++ --> $DIR/regex.rs:54:40
++ |
++LL | let trivial_backslash = Regex::new("a/.b");
++ | ^^^^^^^
++ |
++ = help: consider using `str::contains`
++
++error: trivial regex
++ --> $DIR/regex.rs:57:36
++ |
++LL | let trivial_empty = Regex::new("");
++ | ^^
++ |
++ = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++ --> $DIR/regex.rs:59:36
++ |
++LL | let trivial_empty = Regex::new("^");
++ | ^^^
++ |
++ = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++ --> $DIR/regex.rs:61:36
++ |
++LL | let trivial_empty = Regex::new("^$");
++ | ^^^^
++ |
++ = help: consider using `str::is_empty`
++
++error: trivial regex
++ --> $DIR/regex.rs:63:44
++ |
++LL | let binary_trivial_empty = BRegex::new("^$");
++ | ^^^^
++ |
++ = help: consider using `str::is_empty`
++
++error: aborting due to 23 previous errors
++
--- /dev/null
--- /dev/null
++//! Test for Clippy lint renames.
++// run-rustfix
++
++#![allow(dead_code)]
++// allow the new lint name here, to test if the new name works
++#![allow(clippy::module_name_repetitions)]
++#![allow(clippy::new_without_default)]
++#![allow(clippy::redundant_static_lifetimes)]
++// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::cognitive_complexity)]
++
++#[warn(clippy::module_name_repetitions)]
++fn main() {}
++
++#[warn(clippy::new_without_default)]
++struct Foo;
++
++#[warn(clippy::redundant_static_lifetimes)]
++fn foo() {}
--- /dev/null
--- /dev/null
++//! Test for Clippy lint renames.
++// run-rustfix
++
++#![allow(dead_code)]
++// allow the new lint name here, to test if the new name works
++#![allow(clippy::module_name_repetitions)]
++#![allow(clippy::new_without_default)]
++#![allow(clippy::redundant_static_lifetimes)]
++// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::cyclomatic_complexity)]
++
++#[warn(clippy::stutter)]
++fn main() {}
++
++#[warn(clippy::new_without_default_derive)]
++struct Foo;
++
++#[warn(clippy::const_static_lifetime)]
++fn foo() {}
--- /dev/null
--- /dev/null
++error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
++ --> $DIR/rename.rs:10:9
++ |
++LL | #![warn(clippy::cyclomatic_complexity)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
++ |
++ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
++ --> $DIR/rename.rs:12:8
++ |
++LL | #[warn(clippy::stutter)]
++ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
++
++error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
++ --> $DIR/rename.rs:15:8
++ |
++LL | #[warn(clippy::new_without_default_derive)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
++
++error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
++ --> $DIR/rename.rs:18:8
++ |
++LL | #[warn(clippy::const_static_lifetime)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
++
++error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
++ --> $DIR/rename.rs:10:9
++ |
++LL | #![warn(clippy::cyclomatic_complexity)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[clippy::cognitive_complexity = "1"]
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[clippy::cyclomatic_complexity = "1"]
++fn main() {}
--- /dev/null
--- /dev/null
++error: Usage of deprecated attribute
++ --> $DIR/renamed_builtin_attr.rs:3:11
++ |
++LL | #[clippy::cyclomatic_complexity = "1"]
++ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(deprecated, invalid_value)]
++#![warn(clippy::all)]
++
++use std::mem;
++
++fn might_panic<X>(x: X) -> X {
++ // in practice this would be a possibly-panicky operation
++ x
++}
++
++fn main() {
++ let mut v = vec![0i32; 4];
++ // the following is UB if `might_panic` panics
++ unsafe {
++ let taken_v = mem::replace(&mut v, mem::uninitialized());
++ let new_v = might_panic(taken_v);
++ std::mem::forget(mem::replace(&mut v, new_v));
++ }
++
++ unsafe {
++ let taken_v = mem::replace(&mut v, mem::zeroed());
++ let new_v = might_panic(taken_v);
++ std::mem::forget(mem::replace(&mut v, new_v));
++ }
++
++ // this is silly but OK, because usize is a primitive type
++ let mut u: usize = 42;
++ let uref = &mut u;
++ let taken_u = unsafe { mem::replace(uref, mem::zeroed()) };
++ *uref = taken_u + 1;
++
++ // this is still not OK, because uninit
++ let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
++ *uref = taken_u + 1;
++}
--- /dev/null
--- /dev/null
++error: replacing with `mem::uninitialized()`
++ --> $DIR/repl_uninit.rs:15:23
++ |
++LL | let taken_v = mem::replace(&mut v, mem::uninitialized());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings`
++ = help: consider using the `take_mut` crate instead
++
++error: replacing with `mem::zeroed()`
++ --> $DIR/repl_uninit.rs:21:23
++ |
++LL | let taken_v = mem::replace(&mut v, mem::zeroed());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using a default value or the `take_mut` crate instead
++
++error: replacing with `mem::uninitialized()`
++ --> $DIR/repl_uninit.rs:33:28
++ |
++LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using the `take_mut` crate instead
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::rest_pat_in_fully_bound_structs)]
++
++struct A {
++ a: i32,
++ b: i64,
++ c: &'static str,
++}
++
++macro_rules! foo {
++ ($param:expr) => {
++ match $param {
++ A { a: 0, b: 0, c: "", .. } => {},
++ _ => {},
++ }
++ };
++}
++
++fn main() {
++ let a_struct = A { a: 5, b: 42, c: "A" };
++
++ match a_struct {
++ A { a: 5, b: 42, c: "", .. } => {}, // Lint
++ A { a: 0, b: 0, c: "", .. } => {}, // Lint
++ _ => {},
++ }
++
++ match a_struct {
++ A { a: 5, b: 42, .. } => {},
++ A { a: 0, b: 0, c: "", .. } => {}, // Lint
++ _ => {},
++ }
++
++ // No lint
++ match a_struct {
++ A { a: 5, .. } => {},
++ A { a: 0, b: 0, .. } => {},
++ _ => {},
++ }
++
++ // No lint
++ foo!(a_struct);
++}
--- /dev/null
--- /dev/null
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++ --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9
++ |
++LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings`
++ = help: consider removing `..` from this binding
++
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++ --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9
++ |
++LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider removing `..` from this binding
++
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++ --> $DIR/rest_pat_in_fully_bound_structs.rs:29:9
++ |
++LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider removing `..` from this binding
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::result_map_or_into_option)]
++
++fn main() {
++ let opt: Result<u32, &str> = Ok(1);
++ let _ = opt.ok();
++
++ let rewrap = |s: u32| -> Option<u32> { Some(s) };
++
++ // A non-Some `f` arg should not emit the lint
++ let opt: Result<u32, &str> = Ok(1);
++ let _ = opt.map_or(None, rewrap);
++
++ // A non-Some `f` closure where the argument is not used as the
++ // return should not emit the lint
++ let opt: Result<u32, &str> = Ok(1);
++ opt.map_or(None, |_x| Some(1));
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::result_map_or_into_option)]
++
++fn main() {
++ let opt: Result<u32, &str> = Ok(1);
++ let _ = opt.map_or(None, Some);
++
++ let rewrap = |s: u32| -> Option<u32> { Some(s) };
++
++ // A non-Some `f` arg should not emit the lint
++ let opt: Result<u32, &str> = Ok(1);
++ let _ = opt.map_or(None, rewrap);
++
++ // A non-Some `f` closure where the argument is not used as the
++ // return should not emit the lint
++ let opt: Result<u32, &str> = Ok(1);
++ opt.map_or(None, |_x| Some(1));
++}
--- /dev/null
--- /dev/null
++error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
++ --> $DIR/result_map_or_into_option.rs:7:13
++ |
++LL | let _ = opt.map_or(None, Some);
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()`
++ |
++ = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::result_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++struct HasResult {
++ field: Result<usize, usize>,
++}
++
++impl HasResult {
++ fn do_result_nothing(self: &Self, value: usize) {}
++
++ fn do_result_plus_one(self: &Self, value: usize) -> usize {
++ value + 1
++ }
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++ let x = HasResult { field: Ok(10) };
++
++ x.field.map(plus_one);
++ let _: Result<(), usize> = x.field.map(do_nothing);
++
++ if let Ok(x_field) = x.field { do_nothing(x_field) }
++
++ if let Ok(x_field) = x.field { do_nothing(x_field) }
++
++ if let Ok(x_field) = x.field { diverge(x_field) }
++
++ let captured = 10;
++ if let Ok(value) = x.field { do_nothing(value + captured) };
++ let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
++
++ if let Ok(value) = x.field { x.do_result_nothing(value + captured) }
++
++ if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }
++
++
++ if let Ok(value) = x.field { do_nothing(value + captured) }
++
++ if let Ok(value) = x.field { do_nothing(value + captured) }
++
++ if let Ok(value) = x.field { do_nothing(value + captured); }
++
++ if let Ok(value) = x.field { do_nothing(value + captured); }
++
++
++ if let Ok(value) = x.field { diverge(value + captured) }
++
++ if let Ok(value) = x.field { diverge(value + captured) }
++
++ if let Ok(value) = x.field { diverge(value + captured); }
++
++ if let Ok(value) = x.field { diverge(value + captured); }
++
++
++ x.field.map(|value| plus_one(value + captured));
++ x.field.map(|value| { plus_one(value + captured) });
++ if let Ok(value) = x.field { let y = plus_one(value + captured); }
++
++ if let Ok(value) = x.field { plus_one(value + captured); }
++
++ if let Ok(value) = x.field { plus_one(value + captured); }
++
++
++ if let Ok(ref value) = x.field { do_nothing(value + captured) }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::result_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++struct HasResult {
++ field: Result<usize, usize>,
++}
++
++impl HasResult {
++ fn do_result_nothing(self: &Self, value: usize) {}
++
++ fn do_result_plus_one(self: &Self, value: usize) -> usize {
++ value + 1
++ }
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++ let x = HasResult { field: Ok(10) };
++
++ x.field.map(plus_one);
++ let _: Result<(), usize> = x.field.map(do_nothing);
++
++ x.field.map(do_nothing);
++
++ x.field.map(do_nothing);
++
++ x.field.map(diverge);
++
++ let captured = 10;
++ if let Ok(value) = x.field { do_nothing(value + captured) };
++ let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
++
++ x.field.map(|value| x.do_result_nothing(value + captured));
++
++ x.field.map(|value| { x.do_result_plus_one(value + captured); });
++
++
++ x.field.map(|value| do_nothing(value + captured));
++
++ x.field.map(|value| { do_nothing(value + captured) });
++
++ x.field.map(|value| { do_nothing(value + captured); });
++
++ x.field.map(|value| { { do_nothing(value + captured); } });
++
++
++ x.field.map(|value| diverge(value + captured));
++
++ x.field.map(|value| { diverge(value + captured) });
++
++ x.field.map(|value| { diverge(value + captured); });
++
++ x.field.map(|value| { { diverge(value + captured); } });
++
++
++ x.field.map(|value| plus_one(value + captured));
++ x.field.map(|value| { plus_one(value + captured) });
++ x.field.map(|value| { let y = plus_one(value + captured); });
++
++ x.field.map(|value| { plus_one(value + captured); });
++
++ x.field.map(|value| { { plus_one(value + captured); } });
++
++
++ x.field.map(|ref value| { do_nothing(value + captured) });
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:35:5
++ |
++LL | x.field.map(do_nothing);
++ | ^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
++ |
++ = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:37:5
++ |
++LL | x.field.map(do_nothing);
++ | ^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:39:5
++ |
++LL | x.field.map(diverge);
++ | ^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:45:5
++ |
++LL | x.field.map(|value| x.do_result_nothing(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:47:5
++ |
++LL | x.field.map(|value| { x.do_result_plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:50:5
++ |
++LL | x.field.map(|value| do_nothing(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:52:5
++ |
++LL | x.field.map(|value| { do_nothing(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:54:5
++ |
++LL | x.field.map(|value| { do_nothing(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:56:5
++ |
++LL | x.field.map(|value| { { do_nothing(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:59:5
++ |
++LL | x.field.map(|value| diverge(value + captured));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:61:5
++ |
++LL | x.field.map(|value| { diverge(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:63:5
++ |
++LL | x.field.map(|value| { diverge(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:65:5
++ |
++LL | x.field.map(|value| { { diverge(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:70:5
++ |
++LL | x.field.map(|value| { let y = plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:72:5
++ |
++LL | x.field.map(|value| { plus_one(value + captured); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:74:5
++ |
++LL | x.field.map(|value| { { plus_one(value + captured); } });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_fixable.rs:77:5
++ |
++LL | x.field.map(|ref value| { do_nothing(value + captured) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }`
++
++error: aborting due to 17 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::result_map_unit_fn)]
++#![feature(never_type)]
++#![allow(unused)]
++
++struct HasResult {
++ field: Result<usize, usize>,
++}
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++ panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++ value + 1
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++ let x = HasResult { field: Ok(10) };
++
++ x.field.map(|value| { do_nothing(value); do_nothing(value) });
++
++ x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++
++ // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
++ // proper suggestion for these cases
++ x.field.map(|value| {
++ do_nothing(value);
++ do_nothing(value)
++ });
++ x.field.map(|value| { do_nothing(value); do_nothing(value); });
++
++ // The following should suggest `if let Ok(_X) ...` as it's difficult to generate a proper let variable name for them
++ let res: Result<!, usize> = Ok(42).map(diverge);
++ "12".parse::<i32>().map(diverge);
++
++ let res: Result<(), usize> = Ok(plus_one(1)).map(do_nothing);
++
++ // Should suggest `if let Ok(_y) ...` to not override the existing foo variable
++ let y: Result<usize, usize> = Ok(42);
++ y.map(do_nothing);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:23:5
++ |
++LL | x.field.map(|value| { do_nothing(value); do_nothing(value) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { ... }`
++ |
++ = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:25:5
++ |
++LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { ... }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:29:5
++ |
++LL | x.field.map(|value| {
++ | _____^
++ | |_____|
++ | ||
++LL | || do_nothing(value);
++LL | || do_nothing(value)
++LL | || });
++ | ||______^- help: try this: `if let Ok(value) = x.field { ... }`
++ | |_______|
++ |
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:33:5
++ |
++LL | x.field.map(|value| { do_nothing(value); do_nothing(value); });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(value) = x.field { ... }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:37:5
++ |
++LL | "12".parse::<i32>().map(diverge);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(a) = "12".parse::<i32>() { diverge(a) }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++ --> $DIR/result_map_unit_fn_unfixable.rs:43:5
++ |
++LL | y.map(do_nothing);
++ | ^^^^^^^^^^^^^^^^^-
++ | |
++ | help: try this: `if let Ok(_y) = y { do_nothing(_y) }`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:option_helpers.rs
++
++//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE`
++
++#![warn(clippy::result_map_unwrap_or_else)]
++
++#[macro_use]
++extern crate option_helpers;
++
++fn result_methods() {
++ let res: Result<i32, ()> = Ok(1);
++
++ // Check RESULT_MAP_UNWRAP_OR_ELSE
++ // single line case
++ let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
++ // multi line cases
++ let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++ let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++ // macro case
++ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++ --> $DIR/result_map_unwrap_or_else.rs:15:13
++ |
++LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings`
++ = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++ --> $DIR/result_map_unwrap_or_else.rs:17:13
++ |
++LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++ --> $DIR/result_map_unwrap_or_else.rs:18:13
++ |
++LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::same_functions_in_if_condition)]
++#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
++#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
++
++fn function() -> bool {
++ true
++}
++
++fn fn_arg(_arg: u8) -> bool {
++ true
++}
++
++struct Struct;
++
++impl Struct {
++ fn method(&self) -> bool {
++ true
++ }
++ fn method_arg(&self, _arg: u8) -> bool {
++ true
++ }
++}
++
++fn ifs_same_cond_fn() {
++ let a = 0;
++ let obj = Struct;
++
++ if function() {
++ } else if function() {
++ //~ ERROR ifs same condition
++ }
++
++ if fn_arg(a) {
++ } else if fn_arg(a) {
++ //~ ERROR ifs same condition
++ }
++
++ if obj.method() {
++ } else if obj.method() {
++ //~ ERROR ifs same condition
++ }
++
++ if obj.method_arg(a) {
++ } else if obj.method_arg(a) {
++ //~ ERROR ifs same condition
++ }
++
++ let mut v = vec![1];
++ if v.pop() == None {
++ //~ ERROR ifs same condition
++ } else if v.pop() == None {
++ }
++
++ if v.len() == 42 {
++ //~ ERROR ifs same condition
++ } else if v.len() == 42 {
++ }
++
++ if v.len() == 1 {
++ // ok, different conditions
++ } else if v.len() == 2 {
++ }
++
++ if fn_arg(0) {
++ // ok, different arguments.
++ } else if fn_arg(1) {
++ }
++
++ if obj.method_arg(0) {
++ // ok, different arguments.
++ } else if obj.method_arg(1) {
++ }
++
++ if a == 1 {
++ // ok, warning is on `ifs_same_cond` behalf.
++ } else if a == 1 {
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:29:15
++ |
++LL | } else if function() {
++ | ^^^^^^^^^^
++ |
++ = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:28:8
++ |
++LL | if function() {
++ | ^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:34:15
++ |
++LL | } else if fn_arg(a) {
++ | ^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:33:8
++ |
++LL | if fn_arg(a) {
++ | ^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:39:15
++ |
++LL | } else if obj.method() {
++ | ^^^^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:38:8
++ |
++LL | if obj.method() {
++ | ^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:44:15
++ |
++LL | } else if obj.method_arg(a) {
++ | ^^^^^^^^^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:43:8
++ |
++LL | if obj.method_arg(a) {
++ | ^^^^^^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:51:15
++ |
++LL | } else if v.pop() == None {
++ | ^^^^^^^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:49:8
++ |
++LL | if v.pop() == None {
++ | ^^^^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++ --> $DIR/same_functions_in_if_condition.rs:56:15
++ |
++LL | } else if v.len() == 42 {
++ | ^^^^^^^^^^^^^
++ |
++note: same as this
++ --> $DIR/same_functions_in_if_condition.rs:54:8
++ |
++LL | if v.len() == 42 {
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::serde_api_misuse)]
++#![allow(dead_code)]
++
++extern crate serde;
++
++struct A;
++
++impl<'de> serde::de::Visitor<'de> for A {
++ type Value = ();
++
++ fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
++ unimplemented!()
++ }
++
++ fn visit_str<E>(self, _v: &str) -> Result<Self::Value, E>
++ where
++ E: serde::de::Error,
++ {
++ unimplemented!()
++ }
++
++ fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++ where
++ E: serde::de::Error,
++ {
++ unimplemented!()
++ }
++}
++
++struct B;
++
++impl<'de> serde::de::Visitor<'de> for B {
++ type Value = ();
++
++ fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
++ unimplemented!()
++ }
++
++ fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++ where
++ E: serde::de::Error,
++ {
++ unimplemented!()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you should not implement `visit_string` without also implementing `visit_str`
++ --> $DIR/serde.rs:39:5
++ |
++LL | / fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++LL | | where
++LL | | E: serde::de::Error,
++LL | | {
++LL | | unimplemented!()
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::serde-api-misuse` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(
++ clippy::all,
++ clippy::pedantic,
++ clippy::shadow_same,
++ clippy::shadow_reuse,
++ clippy::shadow_unrelated
++)]
++#![allow(
++ unused_parens,
++ unused_variables,
++ clippy::missing_docs_in_private_items,
++ clippy::single_match
++)]
++
++fn id<T>(x: T) -> T {
++ x
++}
++
++#[must_use]
++fn first(x: (isize, isize)) -> isize {
++ x.0
++}
++
++fn main() {
++ let mut x = 1;
++ let x = &mut x;
++ let x = { x };
++ let x = (&*x);
++ let x = { *x + 1 };
++ let x = id(x);
++ let x = (1, x);
++ let x = first(x);
++ let y = 1;
++ let x = y;
++
++ let x;
++ x = 42;
++
++ let o = Some(1_u8);
++
++ if let Some(p) = o {
++ assert_eq!(1, p);
++ }
++ match o {
++ Some(p) => p, // no error, because the p above is in its own scope
++ None => 0,
++ };
++
++ match (x, o) {
++ (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice
++ _ => (),
++ }
++}
--- /dev/null
--- /dev/null
++error: `x` is shadowed by itself in `&mut x`
++ --> $DIR/shadow.rs:26:5
++ |
++LL | let x = &mut x;
++ | ^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::shadow-same` implied by `-D warnings`
++note: previous binding is here
++ --> $DIR/shadow.rs:25:13
++ |
++LL | let mut x = 1;
++ | ^
++
++error: `x` is shadowed by itself in `{ x }`
++ --> $DIR/shadow.rs:27:5
++ |
++LL | let x = { x };
++ | ^^^^^^^^^^^^^^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:26:9
++ |
++LL | let x = &mut x;
++ | ^
++
++error: `x` is shadowed by itself in `(&*x)`
++ --> $DIR/shadow.rs:28:5
++ |
++LL | let x = (&*x);
++ | ^^^^^^^^^^^^^^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:27:9
++ |
++LL | let x = { x };
++ | ^
++
++error: `x` is shadowed by `{ *x + 1 }` which reuses the original value
++ --> $DIR/shadow.rs:29:9
++ |
++LL | let x = { *x + 1 };
++ | ^
++ |
++ = note: `-D clippy::shadow-reuse` implied by `-D warnings`
++note: initialization happens here
++ --> $DIR/shadow.rs:29:13
++ |
++LL | let x = { *x + 1 };
++ | ^^^^^^^^^^
++note: previous binding is here
++ --> $DIR/shadow.rs:28:9
++ |
++LL | let x = (&*x);
++ | ^
++
++error: `x` is shadowed by `id(x)` which reuses the original value
++ --> $DIR/shadow.rs:30:9
++ |
++LL | let x = id(x);
++ | ^
++ |
++note: initialization happens here
++ --> $DIR/shadow.rs:30:13
++ |
++LL | let x = id(x);
++ | ^^^^^
++note: previous binding is here
++ --> $DIR/shadow.rs:29:9
++ |
++LL | let x = { *x + 1 };
++ | ^
++
++error: `x` is shadowed by `(1, x)` which reuses the original value
++ --> $DIR/shadow.rs:31:9
++ |
++LL | let x = (1, x);
++ | ^
++ |
++note: initialization happens here
++ --> $DIR/shadow.rs:31:13
++ |
++LL | let x = (1, x);
++ | ^^^^^^
++note: previous binding is here
++ --> $DIR/shadow.rs:30:9
++ |
++LL | let x = id(x);
++ | ^
++
++error: `x` is shadowed by `first(x)` which reuses the original value
++ --> $DIR/shadow.rs:32:9
++ |
++LL | let x = first(x);
++ | ^
++ |
++note: initialization happens here
++ --> $DIR/shadow.rs:32:13
++ |
++LL | let x = first(x);
++ | ^^^^^^^^
++note: previous binding is here
++ --> $DIR/shadow.rs:31:9
++ |
++LL | let x = (1, x);
++ | ^
++
++error: `x` is shadowed by `y`
++ --> $DIR/shadow.rs:34:9
++ |
++LL | let x = y;
++ | ^
++ |
++ = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
++note: initialization happens here
++ --> $DIR/shadow.rs:34:13
++ |
++LL | let x = y;
++ | ^
++note: previous binding is here
++ --> $DIR/shadow.rs:32:9
++ |
++LL | let x = first(x);
++ | ^
++
++error: `x` shadows a previous declaration
++ --> $DIR/shadow.rs:36:5
++ |
++LL | let x;
++ | ^^^^^^
++ |
++note: previous binding is here
++ --> $DIR/shadow.rs:34:9
++ |
++LL | let x = y;
++ | ^
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::short_circuit_statement)]
++#![allow(clippy::nonminimal_bool)]
++
++fn main() {
++ if f() { g(); }
++ if !f() { g(); }
++ if !(1 == 2) { g(); }
++}
++
++fn f() -> bool {
++ true
++}
++
++fn g() -> bool {
++ false
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::short_circuit_statement)]
++#![allow(clippy::nonminimal_bool)]
++
++fn main() {
++ f() && g();
++ f() || g();
++ 1 == 2 || g();
++}
++
++fn f() -> bool {
++ true
++}
++
++fn g() -> bool {
++ false
++}
--- /dev/null
--- /dev/null
++error: boolean short circuit operator in statement may be clearer using an explicit test
++ --> $DIR/short_circuit_statement.rs:7:5
++ |
++LL | f() && g();
++ | ^^^^^^^^^^^ help: replace it with: `if f() { g(); }`
++ |
++ = note: `-D clippy::short-circuit-statement` implied by `-D warnings`
++
++error: boolean short circuit operator in statement may be clearer using an explicit test
++ --> $DIR/short_circuit_statement.rs:8:5
++ |
++LL | f() || g();
++ | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }`
++
++error: boolean short circuit operator in statement may be clearer using an explicit test
++ --> $DIR/short_circuit_statement.rs:9:5
++ |
++LL | 1 == 2 || g();
++ | ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::similar_names)]
++#![allow(unused, clippy::println_empty_string)]
++
++struct Foo {
++ apple: i32,
++ bpple: i32,
++}
++
++fn main() {
++ let specter: i32;
++ let spectre: i32;
++
++ let apple: i32;
++
++ let bpple: i32;
++
++ let cpple: i32;
++
++ let a_bar: i32;
++ let b_bar: i32;
++ let c_bar: i32;
++
++ let items = [5];
++ for item in &items {
++ loop {}
++ }
++
++ let foo_x: i32;
++ let foo_y: i32;
++
++ let rhs: i32;
++ let lhs: i32;
++
++ let bla_rhs: i32;
++ let bla_lhs: i32;
++
++ let blubrhs: i32;
++ let blublhs: i32;
++
++ let blubx: i32;
++ let bluby: i32;
++
++ let cake: i32;
++ let cakes: i32;
++ let coke: i32;
++
++ match 5 {
++ cheese @ 1 => {},
++ rabbit => panic!(),
++ }
++ let cheese: i32;
++ match (42, 43) {
++ (cheese1, 1) => {},
++ (cheese2, 2) => panic!(),
++ _ => println!(""),
++ }
++ let ipv4: i32;
++ let ipv6: i32;
++ let abcd1: i32;
++ let abdc2: i32;
++ let xyz1abc: i32;
++ let xyz2abc: i32;
++ let xyzeabc: i32;
++
++ let parser: i32;
++ let parsed: i32;
++ let parsee: i32;
++
++ let setter: i32;
++ let getter: i32;
++ let tx1: i32;
++ let rx1: i32;
++ let tx_cake: i32;
++ let rx_cake: i32;
++}
++
++fn foo() {
++ let Foo { apple, bpple } = unimplemented!();
++ let Foo {
++ apple: spring,
++ bpple: sprang,
++ } = unimplemented!();
++}
++
++// false positive similar_names (#3057, #2651)
++// clippy claimed total_reg_src_size and total_size and
++// numb_reg_src_checkouts and total_bin_size were similar
++#[derive(Debug, Clone)]
++pub(crate) struct DirSizes {
++ pub(crate) total_size: u64,
++ pub(crate) numb_bins: u64,
++ pub(crate) total_bin_size: u64,
++ pub(crate) total_reg_size: u64,
++ pub(crate) total_git_db_size: u64,
++ pub(crate) total_git_repos_bare_size: u64,
++ pub(crate) numb_git_repos_bare_repos: u64,
++ pub(crate) numb_git_checkouts: u64,
++ pub(crate) total_git_chk_size: u64,
++ pub(crate) total_reg_cache_size: u64,
++ pub(crate) total_reg_src_size: u64,
++ pub(crate) numb_reg_cache_entries: u64,
++ pub(crate) numb_reg_src_checkouts: u64,
++}
--- /dev/null
--- /dev/null
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:15:9
++ |
++LL | let bpple: i32;
++ | ^^^^^
++ |
++ = note: `-D clippy::similar-names` implied by `-D warnings`
++note: existing binding defined here
++ --> $DIR/similar_names.rs:13:9
++ |
++LL | let apple: i32;
++ | ^^^^^
++help: separate the discriminating character by an underscore like: `b_pple`
++ --> $DIR/similar_names.rs:15:9
++ |
++LL | let bpple: i32;
++ | ^^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:17:9
++ |
++LL | let cpple: i32;
++ | ^^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:13:9
++ |
++LL | let apple: i32;
++ | ^^^^^
++help: separate the discriminating character by an underscore like: `c_pple`
++ --> $DIR/similar_names.rs:17:9
++ |
++LL | let cpple: i32;
++ | ^^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:41:9
++ |
++LL | let bluby: i32;
++ | ^^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:40:9
++ |
++LL | let blubx: i32;
++ | ^^^^^
++help: separate the discriminating character by an underscore like: `blub_y`
++ --> $DIR/similar_names.rs:41:9
++ |
++LL | let bluby: i32;
++ | ^^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:45:9
++ |
++LL | let coke: i32;
++ | ^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:43:9
++ |
++LL | let cake: i32;
++ | ^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:63:9
++ |
++LL | let xyzeabc: i32;
++ | ^^^^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:61:9
++ |
++LL | let xyz1abc: i32;
++ | ^^^^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:67:9
++ |
++LL | let parsee: i32;
++ | ^^^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:65:9
++ |
++LL | let parser: i32;
++ | ^^^^^^
++help: separate the discriminating character by an underscore like: `parse_e`
++ --> $DIR/similar_names.rs:67:9
++ |
++LL | let parsee: i32;
++ | ^^^^^^
++
++error: binding's name is too similar to existing binding
++ --> $DIR/similar_names.rs:81:16
++ |
++LL | bpple: sprang,
++ | ^^^^^^
++ |
++note: existing binding defined here
++ --> $DIR/similar_names.rs:80:16
++ |
++LL | apple: spring,
++ | ^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_must_use)]
++
++use std::collections::HashSet;
++
++fn main() {
++ let x = "foo";
++ x.split('x');
++ x.split("xx");
++ x.split('x');
++
++ let y = "x";
++ x.split(y);
++ // Not yet testing for multi-byte characters
++ // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
++ // should have done this but produced an ICE
++ //
++ // We may not want to suggest changing these anyway
++ // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
++ x.split("ß");
++ x.split("ℝ");
++ x.split("💣");
++ // Can't use this lint for unicode code points which don't fit in a char
++ x.split("❤️");
++ x.contains('x');
++ x.starts_with('x');
++ x.ends_with('x');
++ x.find('x');
++ x.rfind('x');
++ x.rsplit('x');
++ x.split_terminator('x');
++ x.rsplit_terminator('x');
++ x.splitn(0, 'x');
++ x.rsplitn(0, 'x');
++ x.matches('x');
++ x.rmatches('x');
++ x.match_indices('x');
++ x.rmatch_indices('x');
++ x.trim_start_matches('x');
++ x.trim_end_matches('x');
++ // Make sure we escape characters correctly.
++ x.split('\n');
++ x.split('\'');
++ x.split('\'');
++
++ let h = HashSet::<String>::new();
++ h.contains("X"); // should not warn
++
++ x.replace(";", ",").split(','); // issue #2978
++ x.starts_with('\x03'); // issue #2996
++
++ // Issue #3204
++ const S: &str = "#";
++ x.find(S);
++
++ // Raw string
++ x.split('a');
++ x.split('a');
++ x.split('a');
++ x.split('\'');
++ x.split('#');
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_must_use)]
++
++use std::collections::HashSet;
++
++fn main() {
++ let x = "foo";
++ x.split("x");
++ x.split("xx");
++ x.split('x');
++
++ let y = "x";
++ x.split(y);
++ // Not yet testing for multi-byte characters
++ // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
++ // should have done this but produced an ICE
++ //
++ // We may not want to suggest changing these anyway
++ // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
++ x.split("ß");
++ x.split("ℝ");
++ x.split("💣");
++ // Can't use this lint for unicode code points which don't fit in a char
++ x.split("❤️");
++ x.contains("x");
++ x.starts_with("x");
++ x.ends_with("x");
++ x.find("x");
++ x.rfind("x");
++ x.rsplit("x");
++ x.split_terminator("x");
++ x.rsplit_terminator("x");
++ x.splitn(0, "x");
++ x.rsplitn(0, "x");
++ x.matches("x");
++ x.rmatches("x");
++ x.match_indices("x");
++ x.rmatch_indices("x");
++ x.trim_start_matches("x");
++ x.trim_end_matches("x");
++ // Make sure we escape characters correctly.
++ x.split("\n");
++ x.split("'");
++ x.split("\'");
++
++ let h = HashSet::<String>::new();
++ h.contains("X"); // should not warn
++
++ x.replace(";", ",").split(","); // issue #2978
++ x.starts_with("\x03"); // issue #2996
++
++ // Issue #3204
++ const S: &str = "#";
++ x.find(S);
++
++ // Raw string
++ x.split(r"a");
++ x.split(r#"a"#);
++ x.split(r###"a"###);
++ x.split(r###"'"###);
++ x.split(r###"#"###);
++}
--- /dev/null
--- /dev/null
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:9:13
++ |
++LL | x.split("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++ |
++ = note: `-D clippy::single-char-pattern` implied by `-D warnings`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:26:16
++ |
++LL | x.contains("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:27:19
++ |
++LL | x.starts_with("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:28:17
++ |
++LL | x.ends_with("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:29:12
++ |
++LL | x.find("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:30:13
++ |
++LL | x.rfind("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:31:14
++ |
++LL | x.rsplit("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:32:24
++ |
++LL | x.split_terminator("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:33:25
++ |
++LL | x.rsplit_terminator("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:34:17
++ |
++LL | x.splitn(0, "x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:35:18
++ |
++LL | x.rsplitn(0, "x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:36:15
++ |
++LL | x.matches("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:37:16
++ |
++LL | x.rmatches("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:38:21
++ |
++LL | x.match_indices("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:39:22
++ |
++LL | x.rmatch_indices("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:40:26
++ |
++LL | x.trim_start_matches("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:41:24
++ |
++LL | x.trim_end_matches("x");
++ | ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:43:13
++ |
++LL | x.split("/n");
++ | ^^^^ help: try using a `char` instead: `'/n'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:44:13
++ |
++LL | x.split("'");
++ | ^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:45:13
++ |
++LL | x.split("/'");
++ | ^^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:50:31
++ |
++LL | x.replace(";", ",").split(","); // issue #2978
++ | ^^^ help: try using a `char` instead: `','`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:51:19
++ |
++LL | x.starts_with("/x03"); // issue #2996
++ | ^^^^^^ help: try using a `char` instead: `'/x03'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:58:13
++ |
++LL | x.split(r"a");
++ | ^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:59:13
++ |
++LL | x.split(r#"a"#);
++ | ^^^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:60:13
++ |
++LL | x.split(r###"a"###);
++ | ^^^^^^^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:61:13
++ |
++LL | x.split(r###"'"###);
++ | ^^^^^^^^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++ --> $DIR/single_char_pattern.rs:62:13
++ |
++LL | x.split(r###"#"###);
++ | ^^^^^^^^^^ help: try using a `char` instead: `'#'`
++
++error: aborting due to 27 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++#![warn(clippy::single_component_path_imports)]
++#![allow(unused_imports)]
++
++
++use serde as edres;
++pub use serde;
++
++macro_rules! m {
++ () => {
++ use regex;
++ };
++}
++
++fn main() {
++ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++
++ // False positive #5154, shouldn't trigger lint.
++ m!();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++#![warn(clippy::single_component_path_imports)]
++#![allow(unused_imports)]
++
++use regex;
++use serde as edres;
++pub use serde;
++
++macro_rules! m {
++ () => {
++ use regex;
++ };
++}
++
++fn main() {
++ regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++
++ // False positive #5154, shouldn't trigger lint.
++ m!();
++}
--- /dev/null
--- /dev/null
++error: this import is redundant
++ --> $DIR/single_component_path_imports.rs:6:1
++ |
++LL | use regex;
++ | ^^^^^^^^^^ help: remove it entirely
++ |
++ = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::single_match)]
++
++fn dummy() {}
++
++fn single_match() {
++ let x = Some(1u8);
++
++ match x {
++ Some(y) => {
++ println!("{:?}", y);
++ },
++ _ => (),
++ };
++
++ let x = Some(1u8);
++ match x {
++ // Note the missing block braces.
++ // We suggest `if let Some(y) = x { .. }` because the macro
++ // is expanded before we can do anything.
++ Some(y) => println!("{:?}", y),
++ _ => (),
++ }
++
++ let z = (1u8, 1u8);
++ match z {
++ (2..=3, 7..=9) => dummy(),
++ _ => {},
++ };
++
++ // Not linted (pattern guards used)
++ match x {
++ Some(y) if y == 0 => println!("{:?}", y),
++ _ => (),
++ }
++
++ // Not linted (no block with statements in the single arm)
++ match z {
++ (2..=3, 7..=9) => println!("{:?}", z),
++ _ => println!("nope"),
++ }
++}
++
++enum Foo {
++ Bar,
++ Baz(u8),
++}
++use std::borrow::Cow;
++use Foo::*;
++
++fn single_match_know_enum() {
++ let x = Some(1u8);
++ let y: Result<_, i8> = Ok(1i8);
++
++ match x {
++ Some(y) => dummy(),
++ None => (),
++ };
++
++ match y {
++ Ok(y) => dummy(),
++ Err(..) => (),
++ };
++
++ let c = Cow::Borrowed("");
++
++ match c {
++ Cow::Borrowed(..) => dummy(),
++ Cow::Owned(..) => (),
++ };
++
++ let z = Foo::Bar;
++ // no warning
++ match z {
++ Bar => println!("42"),
++ Baz(_) => (),
++ }
++
++ match z {
++ Baz(_) => println!("42"),
++ Bar => (),
++ }
++}
++
++macro_rules! single_match {
++ ($num:literal) => {
++ match $num {
++ 15 => println!("15"),
++ _ => (),
++ }
++ };
++}
++
++fn main() {
++ single_match!(5);
++}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:8:5
++ |
++LL | / match x {
++LL | | Some(y) => {
++LL | | println!("{:?}", y);
++LL | | },
++LL | | _ => (),
++LL | | };
++ | |_____^
++ |
++ = note: `-D clippy::single-match` implied by `-D warnings`
++help: try this
++ |
++LL | if let Some(y) = x {
++LL | println!("{:?}", y);
++LL | };
++ |
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:16:5
++ |
++LL | / match x {
++LL | | // Note the missing block braces.
++LL | | // We suggest `if let Some(y) = x { .. }` because the macro
++LL | | // is expanded before we can do anything.
++LL | | Some(y) => println!("{:?}", y),
++LL | | _ => (),
++LL | | }
++ | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:25:5
++ |
++LL | / match z {
++LL | | (2..=3, 7..=9) => dummy(),
++LL | | _ => {},
++LL | | };
++ | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:54:5
++ |
++LL | / match x {
++LL | | Some(y) => dummy(),
++LL | | None => (),
++LL | | };
++ | |_____^ help: try this: `if let Some(y) = x { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:59:5
++ |
++LL | / match y {
++LL | | Ok(y) => dummy(),
++LL | | Err(..) => (),
++LL | | };
++ | |_____^ help: try this: `if let Ok(y) = y { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match.rs:66:5
++ |
++LL | / match c {
++LL | | Cow::Borrowed(..) => dummy(),
++LL | | Cow::Owned(..) => (),
++LL | | };
++ | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::single_match_else)]
++
++enum ExprNode {
++ ExprAddrOf,
++ Butterflies,
++ Unicorns,
++}
++
++static NODE: ExprNode = ExprNode::Unicorns;
++
++fn unwrap_addr() -> Option<&'static ExprNode> {
++ match ExprNode::Butterflies {
++ ExprNode::ExprAddrOf => Some(&NODE),
++ _ => {
++ let x = 5;
++ None
++ },
++ }
++}
++
++macro_rules! unwrap_addr {
++ ($expression:expr) => {
++ match $expression {
++ ExprNode::ExprAddrOf => Some(&NODE),
++ _ => {
++ let x = 5;
++ None
++ },
++ }
++ };
++}
++
++fn main() {
++ unwrap_addr!(ExprNode::Unicorns);
++}
--- /dev/null
--- /dev/null
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++ --> $DIR/single_match_else.rs:12:5
++ |
++LL | / match ExprNode::Butterflies {
++LL | | ExprNode::ExprAddrOf => Some(&NODE),
++LL | | _ => {
++LL | | let x = 5;
++LL | | None
++LL | | },
++LL | | }
++ | |_____^
++ |
++ = note: `-D clippy::single-match-else` implied by `-D warnings`
++help: try this
++ |
++LL | if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
++LL | let x = 5;
++LL | None
++LL | }
++ |
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// aux-build:option_helpers.rs
++
++#![warn(clippy::skip_while_next)]
++#![allow(clippy::blacklisted_name)]
++
++extern crate option_helpers;
++use option_helpers::IteratorFalsePositives;
++
++#[rustfmt::skip]
++fn skip_while_next() {
++ let v = vec![3, 2, 1, 0, -1, -2, -3];
++
++ // Single-line case.
++ let _ = v.iter().skip_while(|&x| *x < 0).next();
++
++ // Multi-line case.
++ let _ = v.iter().skip_while(|&x| {
++ *x < 0
++ }
++ ).next();
++
++ // Check that hat we don't lint if the caller is not an `Iterator`.
++ let foo = IteratorFalsePositives { foo: 0 };
++ let _ = foo.skip_while().next();
++}
++
++fn main() {
++ skip_while_next();
++}
--- /dev/null
--- /dev/null
++error: called `skip_while(p).next()` on an `Iterator`
++ --> $DIR/skip_while_next.rs:14:13
++ |
++LL | let _ = v.iter().skip_while(|&x| *x < 0).next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::skip-while-next` implied by `-D warnings`
++ = help: this is more succinctly expressed by calling `.find(!p)` instead
++
++error: called `skip_while(p).next()` on an `Iterator`
++ --> $DIR/skip_while_next.rs:17:13
++ |
++LL | let _ = v.iter().skip_while(|&x| {
++ | _____________^
++LL | | *x < 0
++LL | | }
++LL | | ).next();
++ | |___________________________^
++ |
++ = help: this is more succinctly expressed by calling `.find(!p)` instead
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++use std::iter::repeat;
++
++fn main() {
++ resize_vector();
++ extend_vector();
++ mixed_extend_resize_vector();
++}
++
++fn extend_vector() {
++ // Extend with constant expression
++ let len = 300;
++ let mut vec1 = Vec::with_capacity(len);
++ vec1.extend(repeat(0).take(len));
++
++ // Extend with len expression
++ let mut vec2 = Vec::with_capacity(len - 10);
++ vec2.extend(repeat(0).take(len - 10));
++
++ // Extend with mismatching expression should not be warned
++ let mut vec3 = Vec::with_capacity(24322);
++ vec3.extend(repeat(0).take(2));
++}
++
++fn mixed_extend_resize_vector() {
++ // Mismatching len
++ let mut mismatching_len = Vec::with_capacity(30);
++ mismatching_len.extend(repeat(0).take(40));
++
++ // Slow initialization
++ let mut resized_vec = Vec::with_capacity(30);
++ resized_vec.resize(30, 0);
++
++ let mut extend_vec = Vec::with_capacity(30);
++ extend_vec.extend(repeat(0).take(30));
++}
++
++fn resize_vector() {
++ // Resize with constant expression
++ let len = 300;
++ let mut vec1 = Vec::with_capacity(len);
++ vec1.resize(len, 0);
++
++ // Resize mismatch len
++ let mut vec2 = Vec::with_capacity(200);
++ vec2.resize(10, 0);
++
++ // Resize with len expression
++ let mut vec3 = Vec::with_capacity(len - 10);
++ vec3.resize(len - 10, 0);
++
++ // Reinitialization should be warned
++ vec1 = Vec::with_capacity(10);
++ vec1.resize(10, 0);
++}
++
++fn do_stuff(vec: &mut Vec<u8>) {}
++
++fn extend_vector_with_manipulations_between() {
++ let len = 300;
++ let mut vec1: Vec<u8> = Vec::with_capacity(len);
++ do_stuff(&mut vec1);
++ vec1.extend(repeat(0).take(len));
++}
--- /dev/null
--- /dev/null
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:13:5
++ |
++LL | let mut vec1 = Vec::with_capacity(len);
++ | ----------------------- help: consider replace allocation with: `vec![0; len]`
++LL | vec1.extend(repeat(0).take(len));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::slow-vector-initialization` implied by `-D warnings`
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:17:5
++ |
++LL | let mut vec2 = Vec::with_capacity(len - 10);
++ | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
++LL | vec2.extend(repeat(0).take(len - 10));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:31:5
++ |
++LL | let mut resized_vec = Vec::with_capacity(30);
++ | ---------------------- help: consider replace allocation with: `vec![0; 30]`
++LL | resized_vec.resize(30, 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:34:5
++ |
++LL | let mut extend_vec = Vec::with_capacity(30);
++ | ---------------------- help: consider replace allocation with: `vec![0; 30]`
++LL | extend_vec.extend(repeat(0).take(30));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:41:5
++ |
++LL | let mut vec1 = Vec::with_capacity(len);
++ | ----------------------- help: consider replace allocation with: `vec![0; len]`
++LL | vec1.resize(len, 0);
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:49:5
++ |
++LL | let mut vec3 = Vec::with_capacity(len - 10);
++ | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
++LL | vec3.resize(len - 10, 0);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++ --> $DIR/slow_vector_initialization.rs:53:5
++ |
++LL | vec1 = Vec::with_capacity(10);
++ | ---------------------- help: consider replace allocation with: `vec![0; 10]`
++LL | vec1.resize(10, 0);
++ | ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code, unused_must_use)]
++
++fn main() {}
++
++#[allow(clippy::unnecessary_operation)]
++fn starts_with() {
++ "".starts_with(' ');
++ !"".starts_with(' ');
++}
++
++fn chars_cmp_with_unwrap() {
++ let s = String::from("foo");
++ if s.starts_with('f') {
++ // s.starts_with('f')
++ // Nothing here
++ }
++ if s.ends_with('o') {
++ // s.ends_with('o')
++ // Nothing here
++ }
++ if s.ends_with('o') {
++ // s.ends_with('o')
++ // Nothing here
++ }
++ if !s.starts_with('f') {
++ // !s.starts_with('f')
++ // Nothing here
++ }
++ if !s.ends_with('o') {
++ // !s.ends_with('o')
++ // Nothing here
++ }
++ if !s.ends_with('o') {
++ // !s.ends_with('o')
++ // Nothing here
++ }
++}
++
++#[allow(clippy::unnecessary_operation)]
++fn ends_with() {
++ "".ends_with(' ');
++ !"".ends_with(' ');
++ "".ends_with(' ');
++ !"".ends_with(' ');
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![allow(dead_code, unused_must_use)]
++
++fn main() {}
++
++#[allow(clippy::unnecessary_operation)]
++fn starts_with() {
++ "".chars().next() == Some(' ');
++ Some(' ') != "".chars().next();
++}
++
++fn chars_cmp_with_unwrap() {
++ let s = String::from("foo");
++ if s.chars().next().unwrap() == 'f' {
++ // s.starts_with('f')
++ // Nothing here
++ }
++ if s.chars().next_back().unwrap() == 'o' {
++ // s.ends_with('o')
++ // Nothing here
++ }
++ if s.chars().last().unwrap() == 'o' {
++ // s.ends_with('o')
++ // Nothing here
++ }
++ if s.chars().next().unwrap() != 'f' {
++ // !s.starts_with('f')
++ // Nothing here
++ }
++ if s.chars().next_back().unwrap() != 'o' {
++ // !s.ends_with('o')
++ // Nothing here
++ }
++ if s.chars().last().unwrap() != 'o' {
++ // !s.ends_with('o')
++ // Nothing here
++ }
++}
++
++#[allow(clippy::unnecessary_operation)]
++fn ends_with() {
++ "".chars().last() == Some(' ');
++ Some(' ') != "".chars().last();
++ "".chars().next_back() == Some(' ');
++ Some(' ') != "".chars().next_back();
++}
--- /dev/null
--- /dev/null
++error: you should use the `starts_with` method
++ --> $DIR/starts_ends_with.rs:8:5
++ |
++LL | "".chars().next() == Some(' ');
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with(' ')`
++ |
++ = note: `-D clippy::chars-next-cmp` implied by `-D warnings`
++
++error: you should use the `starts_with` method
++ --> $DIR/starts_ends_with.rs:9:5
++ |
++LL | Some(' ') != "".chars().next();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')`
++
++error: you should use the `starts_with` method
++ --> $DIR/starts_ends_with.rs:14:8
++ |
++LL | if s.chars().next().unwrap() == 'f' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:18:8
++ |
++LL | if s.chars().next_back().unwrap() == 'o' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
++ |
++ = note: `-D clippy::chars-last-cmp` implied by `-D warnings`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:22:8
++ |
++LL | if s.chars().last().unwrap() == 'o' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
++
++error: you should use the `starts_with` method
++ --> $DIR/starts_ends_with.rs:26:8
++ |
++LL | if s.chars().next().unwrap() != 'f' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:30:8
++ |
++LL | if s.chars().next_back().unwrap() != 'o' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:34:8
++ |
++LL | if s.chars().last().unwrap() != 'o' {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:42:5
++ |
++LL | "".chars().last() == Some(' ');
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:43:5
++ |
++LL | Some(' ') != "".chars().last();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:44:5
++ |
++LL | "".chars().next_back() == Some(' ');
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++ --> $DIR/starts_ends_with.rs:45:5
++ |
++LL | Some(' ') != "".chars().next_back();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++// aux-build:macro_rules.rs
++
++#[macro_use]
++extern crate macro_rules;
++
++#[warn(clippy::string_add)]
++#[allow(clippy::string_add_assign, unused)]
++fn main() {
++ // ignores assignment distinction
++ let mut x = "".to_owned();
++
++ for _ in 1..3 {
++ x = x + ".";
++ }
++
++ let y = "".to_owned();
++ let z = y + "...";
++
++ assert_eq!(&x, &z);
++
++ let mut x = 1;
++ x = x + 1;
++ assert_eq!(2, x);
++
++ string_add!();
++}
--- /dev/null
--- /dev/null
++error: manual implementation of an assign operation
++ --> $DIR/string_add.rs:13:9
++ |
++LL | x = x + ".";
++ | ^^^^^^^^^^^ help: replace it with: `x += "."`
++ |
++ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: you added something to a string. Consider using `String::push_str()` instead
++ --> $DIR/string_add.rs:13:13
++ |
++LL | x = x + ".";
++ | ^^^^^^^
++ |
++ = note: `-D clippy::string-add` implied by `-D warnings`
++
++error: you added something to a string. Consider using `String::push_str()` instead
++ --> $DIR/string_add.rs:17:13
++ |
++LL | let z = y + "...";
++ | ^^^^^^^^^
++
++error: manual implementation of an assign operation
++ --> $DIR/string_add.rs:22:5
++ |
++LL | x = x + 1;
++ | ^^^^^^^^^ help: replace it with: `x += 1`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(clippy::string_add, unused)]
++#[warn(clippy::string_add_assign)]
++fn main() {
++ // ignores assignment distinction
++ let mut x = "".to_owned();
++
++ for _ in 1..3 {
++ x += ".";
++ }
++
++ let y = "".to_owned();
++ let z = y + "...";
++
++ assert_eq!(&x, &z);
++
++ let mut x = 1;
++ x += 1;
++ assert_eq!(2, x);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[allow(clippy::string_add, unused)]
++#[warn(clippy::string_add_assign)]
++fn main() {
++ // ignores assignment distinction
++ let mut x = "".to_owned();
++
++ for _ in 1..3 {
++ x = x + ".";
++ }
++
++ let y = "".to_owned();
++ let z = y + "...";
++
++ assert_eq!(&x, &z);
++
++ let mut x = 1;
++ x = x + 1;
++ assert_eq!(2, x);
++}
--- /dev/null
--- /dev/null
++error: you assigned the result of adding something to this string. Consider using `String::push_str()` instead
++ --> $DIR/string_add_assign.rs:10:9
++ |
++LL | x = x + ".";
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::string-add-assign` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++ --> $DIR/string_add_assign.rs:10:9
++ |
++LL | x = x + ".";
++ | ^^^^^^^^^^^ help: replace it with: `x += "."`
++ |
++ = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++ --> $DIR/string_add_assign.rs:19:5
++ |
++LL | x = x + 1;
++ | ^^^^^^^^^ help: replace it with: `x += 1`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[derive(Copy, Clone)]
++struct HasChars;
++
++impl HasChars {
++ fn chars(self) -> std::str::Chars<'static> {
++ "HasChars".chars()
++ }
++}
++
++fn main() {
++ let abc = "abc";
++ let def = String::from("def");
++ let mut s = String::new();
++
++ s.push_str(abc);
++ s.push_str(abc);
++
++ s.push_str("abc");
++ s.push_str("abc");
++
++ s.push_str(&def);
++ s.push_str(&def);
++
++ s.extend(abc.chars().skip(1));
++ s.extend("abc".chars().skip(1));
++ s.extend(['a', 'b', 'c'].iter());
++
++ let f = HasChars;
++ s.extend(f.chars());
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#[derive(Copy, Clone)]
++struct HasChars;
++
++impl HasChars {
++ fn chars(self) -> std::str::Chars<'static> {
++ "HasChars".chars()
++ }
++}
++
++fn main() {
++ let abc = "abc";
++ let def = String::from("def");
++ let mut s = String::new();
++
++ s.push_str(abc);
++ s.extend(abc.chars());
++
++ s.push_str("abc");
++ s.extend("abc".chars());
++
++ s.push_str(&def);
++ s.extend(def.chars());
++
++ s.extend(abc.chars().skip(1));
++ s.extend("abc".chars().skip(1));
++ s.extend(['a', 'b', 'c'].iter());
++
++ let f = HasChars;
++ s.extend(f.chars());
++}
--- /dev/null
--- /dev/null
++error: calling `.extend(_.chars())`
++ --> $DIR/string_extend.rs:18:5
++ |
++LL | s.extend(abc.chars());
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)`
++ |
++ = note: `-D clippy::string-extend-chars` implied by `-D warnings`
++
++error: calling `.extend(_.chars())`
++ --> $DIR/string_extend.rs:21:5
++ |
++LL | s.extend("abc".chars());
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")`
++
++error: calling `.extend(_.chars())`
++ --> $DIR/string_extend.rs:24:5
++ |
++LL | s.extend(def.chars());
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::string_lit_as_bytes)]
++
++fn str_lit_as_bytes() {
++ let bs = b"hello there";
++
++ let bs = br###"raw string with 3# plus " ""###;
++
++ // no warning, because these cannot be written as byte string literals:
++ let ubs = "☃".as_bytes();
++ let ubs = "hello there! this is a very long string".as_bytes();
++
++ let strify = stringify!(foobar).as_bytes();
++
++ let includestr = include_bytes!("entry_unfixable.rs");
++
++ let _ = b"string with newline\t\n";
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::string_lit_as_bytes)]
++
++fn str_lit_as_bytes() {
++ let bs = "hello there".as_bytes();
++
++ let bs = r###"raw string with 3# plus " ""###.as_bytes();
++
++ // no warning, because these cannot be written as byte string literals:
++ let ubs = "☃".as_bytes();
++ let ubs = "hello there! this is a very long string".as_bytes();
++
++ let strify = stringify!(foobar).as_bytes();
++
++ let includestr = include_str!("entry_unfixable.rs").as_bytes();
++
++ let _ = "string with newline\t\n".as_bytes();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: calling `as_bytes()` on a string literal
++ --> $DIR/string_lit_as_bytes.rs:7:14
++ |
++LL | let bs = "hello there".as_bytes();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"`
++ |
++ = note: `-D clippy::string-lit-as-bytes` implied by `-D warnings`
++
++error: calling `as_bytes()` on a string literal
++ --> $DIR/string_lit_as_bytes.rs:9:14
++ |
++LL | let bs = r###"raw string with 3# plus " ""###.as_bytes();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###`
++
++error: calling `as_bytes()` on `include_str!(..)`
++ --> $DIR/string_lit_as_bytes.rs:17:22
++ |
++LL | let includestr = include_str!("entry_unfixable.rs").as_bytes();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
++
++error: calling `as_bytes()` on a string literal
++ --> $DIR/string_lit_as_bytes.rs:19:13
++ |
++LL | let _ = "string with newline/t/n".as_bytes();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::struct_excessive_bools)]
++
++macro_rules! foo {
++ () => {
++ struct MacroFoo {
++ a: bool,
++ b: bool,
++ c: bool,
++ d: bool,
++ }
++ };
++}
++
++foo!();
++
++struct Foo {
++ a: bool,
++ b: bool,
++ c: bool,
++}
++
++struct BadFoo {
++ a: bool,
++ b: bool,
++ c: bool,
++ d: bool,
++}
++
++#[repr(C)]
++struct Bar {
++ a: bool,
++ b: bool,
++ c: bool,
++ d: bool,
++}
++
++fn main() {
++ struct FooFoo {
++ a: bool,
++ b: bool,
++ c: bool,
++ d: bool,
++ }
++}
--- /dev/null
--- /dev/null
++error: more than 3 bools in a struct
++ --> $DIR/struct_excessive_bools.rs:22:1
++ |
++LL | / struct BadFoo {
++LL | | a: bool,
++LL | | b: bool,
++LL | | c: bool,
++LL | | d: bool,
++LL | | }
++ | |_^
++ |
++ = note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
++ = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: more than 3 bools in a struct
++ --> $DIR/struct_excessive_bools.rs:38:5
++ |
++LL | / struct FooFoo {
++LL | | a: bool,
++LL | | b: bool,
++LL | | c: bool,
++LL | | d: bool,
++LL | | }
++ | |_____^
++ |
++ = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::suspicious_arithmetic_impl)]
++use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub};
++
++#[derive(Copy, Clone)]
++struct Foo(u32);
++
++impl Add for Foo {
++ type Output = Foo;
++
++ fn add(self, other: Self) -> Self {
++ Foo(self.0 - other.0)
++ }
++}
++
++impl AddAssign for Foo {
++ fn add_assign(&mut self, other: Foo) {
++ *self = *self - other;
++ }
++}
++
++impl BitOrAssign for Foo {
++ fn bitor_assign(&mut self, other: Foo) {
++ let idx = other.0;
++ self.0 |= 1 << idx; // OK: BinOpKind::Shl part of AssignOp as child node
++ }
++}
++
++impl MulAssign for Foo {
++ fn mul_assign(&mut self, other: Foo) {
++ self.0 /= other.0;
++ }
++}
++
++impl DivAssign for Foo {
++ fn div_assign(&mut self, other: Foo) {
++ self.0 /= other.0; // OK: BinOpKind::Div == DivAssign
++ }
++}
++
++impl Mul for Foo {
++ type Output = Foo;
++
++ fn mul(self, other: Foo) -> Foo {
++ Foo(self.0 * other.0 % 42) // OK: BinOpKind::Rem part of BiExpr as parent node
++ }
++}
++
++impl Sub for Foo {
++ type Output = Foo;
++
++ fn sub(self, other: Self) -> Self {
++ Foo(self.0 * other.0 - 42) // OK: BinOpKind::Mul part of BiExpr as child node
++ }
++}
++
++impl Div for Foo {
++ type Output = Foo;
++
++ fn div(self, other: Self) -> Self {
++ Foo(do_nothing(self.0 + other.0) / 42) // OK: BinOpKind::Add part of BiExpr as child node
++ }
++}
++
++struct Bar(i32);
++
++impl Add for Bar {
++ type Output = Bar;
++
++ fn add(self, other: Self) -> Self {
++ Bar(self.0 & !other.0) // OK: UnNot part of BiExpr as child node
++ }
++}
++
++impl Sub for Bar {
++ type Output = Bar;
++
++ fn sub(self, other: Self) -> Self {
++ if self.0 <= other.0 {
++ Bar(-(self.0 & other.0)) // OK: UnNeg part of BiExpr as parent node
++ } else {
++ Bar(0)
++ }
++ }
++}
++
++fn main() {}
++
++fn do_nothing(x: u32) -> u32 {
++ x
++}
--- /dev/null
--- /dev/null
++error: Suspicious use of binary operator in `Add` impl
++ --> $DIR/suspicious_arithmetic_impl.rs:11: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:17:23
++ |
++LL | *self = *self - other;
++ | ^
++ |
++ = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
++
++error: Suspicious use of binary operator in `MulAssign` impl
++ --> $DIR/suspicious_arithmetic_impl.rs:30:16
++ |
++LL | self.0 /= other.0;
++ | ^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::suspicious_map)]
++
++fn main() {
++ let _ = (0..3).map(|x| x + 2).count();
++}
--- /dev/null
--- /dev/null
++error: this call to `map()` won't have an effect on the call to `count()`
++ --> $DIR/suspicious_map.rs:4:13
++ |
++LL | let _ = (0..3).map(|x| x + 2).count();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::suspicious-map` implied by `-D warnings`
++ = help: make sure you did not confuse `map` with `filter` or `for_each`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::suspicious_unary_op_formatting)]
++
++#[rustfmt::skip]
++fn main() {
++ // weird binary operator formatting:
++ let a = 42;
++
++ if a >- 30 {}
++ if a >=- 30 {}
++
++ let b = true;
++ let c = false;
++
++ if b &&! c {}
++
++ if a >- 30 {}
++
++ // those are ok:
++ if a >-30 {}
++ if a < -30 {}
++ if b && !c {}
++ if a > - 30 {}
++}
--- /dev/null
--- /dev/null
++error: by not having a space between `>` and `-` it looks like `>-` is a single operator
++ --> $DIR/suspicious_unary_op_formatting.rs:8:9
++ |
++LL | if a >- 30 {}
++ | ^^^^
++ |
++ = note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings`
++ = help: put a space between `>` and `-` and remove the space after `-`
++
++error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator
++ --> $DIR/suspicious_unary_op_formatting.rs:9:9
++ |
++LL | if a >=- 30 {}
++ | ^^^^^
++ |
++ = help: put a space between `>=` and `-` and remove the space after `-`
++
++error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator
++ --> $DIR/suspicious_unary_op_formatting.rs:14:9
++ |
++LL | if b &&! c {}
++ | ^^^^^
++ |
++ = help: put a space between `&&` and `!` and remove the space after `!`
++
++error: by not having a space between `>` and `-` it looks like `>-` is a single operator
++ --> $DIR/suspicious_unary_op_formatting.rs:16:9
++ |
++LL | if a >- 30 {}
++ | ^^^^^^
++ |
++ = help: put a space between `>` and `-` and remove the space after `-`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all)]
++#![allow(
++ clippy::blacklisted_name,
++ clippy::no_effect,
++ clippy::redundant_clone,
++ redundant_semicolons,
++ unused_assignments
++)]
++
++struct Foo(u32);
++
++#[derive(Clone)]
++struct Bar {
++ a: u32,
++ b: u32,
++}
++
++fn field() {
++ let mut bar = Bar { a: 1, b: 2 };
++
++ let temp = bar.a;
++ bar.a = bar.b;
++ bar.b = temp;
++
++ let mut baz = vec![bar.clone(), bar.clone()];
++ let temp = baz[0].a;
++ baz[0].a = baz[1].a;
++ baz[1].a = temp;
++}
++
++fn array() {
++ let mut foo = [1, 2];
++ foo.swap(0, 1);
++
++ foo.swap(0, 1);
++}
++
++fn slice() {
++ let foo = &mut [1, 2];
++ foo.swap(0, 1);
++
++ foo.swap(0, 1);
++}
++
++fn unswappable_slice() {
++ let foo = &mut [vec![1, 2], vec![3, 4]];
++ let temp = foo[0][1];
++ foo[0][1] = foo[1][0];
++ foo[1][0] = temp;
++
++ // swap(foo[0][1], foo[1][0]) would fail
++}
++
++fn vec() {
++ let mut foo = vec![1, 2];
++ foo.swap(0, 1);
++
++ foo.swap(0, 1);
++}
++
++#[rustfmt::skip]
++fn main() {
++ field();
++ array();
++ slice();
++ unswappable_slice();
++ vec();
++
++ let mut a = 42;
++ let mut b = 1337;
++
++ std::mem::swap(&mut a, &mut b);
++
++ ; std::mem::swap(&mut a, &mut b);
++
++ let mut c = Foo(42);
++
++ std::mem::swap(&mut c.0, &mut a);
++
++ ; std::mem::swap(&mut c.0, &mut a);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::all)]
++#![allow(
++ clippy::blacklisted_name,
++ clippy::no_effect,
++ clippy::redundant_clone,
++ redundant_semicolons,
++ unused_assignments
++)]
++
++struct Foo(u32);
++
++#[derive(Clone)]
++struct Bar {
++ a: u32,
++ b: u32,
++}
++
++fn field() {
++ let mut bar = Bar { a: 1, b: 2 };
++
++ let temp = bar.a;
++ bar.a = bar.b;
++ bar.b = temp;
++
++ let mut baz = vec![bar.clone(), bar.clone()];
++ let temp = baz[0].a;
++ baz[0].a = baz[1].a;
++ baz[1].a = temp;
++}
++
++fn array() {
++ let mut foo = [1, 2];
++ let temp = foo[0];
++ foo[0] = foo[1];
++ foo[1] = temp;
++
++ foo.swap(0, 1);
++}
++
++fn slice() {
++ let foo = &mut [1, 2];
++ let temp = foo[0];
++ foo[0] = foo[1];
++ foo[1] = temp;
++
++ foo.swap(0, 1);
++}
++
++fn unswappable_slice() {
++ let foo = &mut [vec![1, 2], vec![3, 4]];
++ let temp = foo[0][1];
++ foo[0][1] = foo[1][0];
++ foo[1][0] = temp;
++
++ // swap(foo[0][1], foo[1][0]) would fail
++}
++
++fn vec() {
++ let mut foo = vec![1, 2];
++ let temp = foo[0];
++ foo[0] = foo[1];
++ foo[1] = temp;
++
++ foo.swap(0, 1);
++}
++
++#[rustfmt::skip]
++fn main() {
++ field();
++ array();
++ slice();
++ unswappable_slice();
++ vec();
++
++ let mut a = 42;
++ let mut b = 1337;
++
++ a = b;
++ b = a;
++
++ ; let t = a;
++ a = b;
++ b = t;
++
++ let mut c = Foo(42);
++
++ c.0 = a;
++ a = c.0;
++
++ ; let t = c.0;
++ c.0 = a;
++ a = t;
++}
--- /dev/null
--- /dev/null
++error: this looks like you are swapping elements of `foo` manually
++ --> $DIR/swap.rs:35:5
++ |
++LL | / let temp = foo[0];
++LL | | foo[0] = foo[1];
++LL | | foo[1] = temp;
++ | |_________________^ help: try: `foo.swap(0, 1)`
++ |
++ = note: `-D clippy::manual-swap` implied by `-D warnings`
++
++error: this looks like you are swapping elements of `foo` manually
++ --> $DIR/swap.rs:44:5
++ |
++LL | / let temp = foo[0];
++LL | | foo[0] = foo[1];
++LL | | foo[1] = temp;
++ | |_________________^ help: try: `foo.swap(0, 1)`
++
++error: this looks like you are swapping elements of `foo` manually
++ --> $DIR/swap.rs:62:5
++ |
++LL | / let temp = foo[0];
++LL | | foo[0] = foo[1];
++LL | | foo[1] = temp;
++ | |_________________^ help: try: `foo.swap(0, 1)`
++
++error: this looks like you are swapping `a` and `b` manually
++ --> $DIR/swap.rs:83:7
++ |
++LL | ; let t = a;
++ | _______^
++LL | | a = b;
++LL | | b = t;
++ | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
++ |
++ = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are swapping `c.0` and `a` manually
++ --> $DIR/swap.rs:92:7
++ |
++LL | ; let t = c.0;
++ | _______^
++LL | | c.0 = a;
++LL | | a = t;
++ | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
++ |
++ = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are trying to swap `a` and `b`
++ --> $DIR/swap.rs:80:5
++ |
++LL | / a = b;
++LL | | b = a;
++ | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
++ |
++ = note: `-D clippy::almost-swapped` implied by `-D warnings`
++ = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are trying to swap `c.0` and `a`
++ --> $DIR/swap.rs:89:5
++ |
++LL | / c.0 = a;
++LL | | a = c.0;
++ | |___________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
++ |
++ = note: or maybe you should use `std::mem::replace`?
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::tabs_in_doc_comments)]
++#[allow(dead_code)]
++
++///
++/// Struct to hold two strings:
++/// - first one
++/// - second one
++pub struct DoubleString {
++ ///
++ /// - First String:
++ /// - needs to be inside here
++ first_string: String,
++ ///
++ /// - Second String:
++ /// - needs to be inside here
++ second_string: String,
++}
++
++/// This is main
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::tabs_in_doc_comments)]
++#[allow(dead_code)]
++
++///
++/// Struct to hold two strings:
++/// - first one
++/// - second one
++pub struct DoubleString {
++ ///
++ /// - First String:
++ /// - needs to be inside here
++ first_string: String,
++ ///
++ /// - Second String:
++ /// - needs to be inside here
++ second_string: String,
++}
++
++/// This is main
++fn main() {}
--- /dev/null
--- /dev/null
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:12:9
++ |
++LL | /// - First String:
++ | ^^^^ help: consider using four spaces per tab
++ |
++ = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:13:9
++ |
++LL | /// - needs to be inside here
++ | ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:16:9
++ |
++LL | /// - Second String:
++ | ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:17:9
++ |
++LL | /// - needs to be inside here
++ | ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:8:5
++ |
++LL | /// - first one
++ | ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:8:13
++ |
++LL | /// - first one
++ | ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:9:5
++ |
++LL | /// - second one
++ | ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++ --> $DIR/tabs_in_doc_comments.rs:9:14
++ |
++LL | /// - second one
++ | ^^^^ help: consider using four spaces per tab
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::temporary_assignment)]
++
++use std::ops::{Deref, DerefMut};
++
++struct TupleStruct(i32);
++
++struct Struct {
++ field: i32,
++}
++
++struct MultiStruct {
++ structure: Struct,
++}
++
++struct Wrapper<'a> {
++ inner: &'a mut Struct,
++}
++
++impl<'a> Deref for Wrapper<'a> {
++ type Target = Struct;
++ fn deref(&self) -> &Struct {
++ self.inner
++ }
++}
++
++impl<'a> DerefMut for Wrapper<'a> {
++ fn deref_mut(&mut self) -> &mut Struct {
++ self.inner
++ }
++}
++
++struct ArrayStruct {
++ array: [i32; 1],
++}
++
++const A: TupleStruct = TupleStruct(1);
++const B: Struct = Struct { field: 1 };
++const C: MultiStruct = MultiStruct {
++ structure: Struct { field: 1 },
++};
++const D: ArrayStruct = ArrayStruct { array: [1] };
++
++fn main() {
++ let mut s = Struct { field: 0 };
++ let mut t = (0, 0);
++
++ Struct { field: 0 }.field = 1;
++ MultiStruct {
++ structure: Struct { field: 0 },
++ }
++ .structure
++ .field = 1;
++ ArrayStruct { array: [0] }.array[0] = 1;
++ (0, 0).0 = 1;
++
++ A.0 = 2;
++ B.field = 2;
++ C.structure.field = 2;
++ D.array[0] = 2;
++
++ // no error
++ s.field = 1;
++ t.0 = 1;
++ Wrapper { inner: &mut s }.field = 1;
++ let mut a_mut = TupleStruct(1);
++ a_mut.0 = 2;
++ let mut b_mut = Struct { field: 1 };
++ b_mut.field = 2;
++ let mut c_mut = MultiStruct {
++ structure: Struct { field: 1 },
++ };
++ c_mut.structure.field = 2;
++ let mut d_mut = ArrayStruct { array: [1] };
++ d_mut.array[0] = 2;
++}
--- /dev/null
--- /dev/null
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:47:5
++ |
++LL | Struct { field: 0 }.field = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::temporary-assignment` implied by `-D warnings`
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:48:5
++ |
++LL | / MultiStruct {
++LL | | structure: Struct { field: 0 },
++LL | | }
++LL | | .structure
++LL | | .field = 1;
++ | |______________^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:53:5
++ |
++LL | ArrayStruct { array: [0] }.array[0] = 1;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:54:5
++ |
++LL | (0, 0).0 = 1;
++ | ^^^^^^^^^^^^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:56:5
++ |
++LL | A.0 = 2;
++ | ^^^^^^^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:57:5
++ |
++LL | B.field = 2;
++ | ^^^^^^^^^^^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:58:5
++ |
++LL | C.structure.field = 2;
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: assignment to temporary
++ --> $DIR/temporary_assignment.rs:59:5
++ |
++LL | D.array[0] = 2;
++ | ^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++//run-rustfix
++
++#![warn(clippy::to_digit_is_some)]
++
++fn main() {
++ let c = 'x';
++ let d = &c;
++
++ let _ = d.is_digit(10);
++ let _ = char::is_digit(c, 10);
++}
--- /dev/null
--- /dev/null
++//run-rustfix
++
++#![warn(clippy::to_digit_is_some)]
++
++fn main() {
++ let c = 'x';
++ let d = &c;
++
++ let _ = d.to_digit(10).is_some();
++ let _ = char::to_digit(c, 10).is_some();
++}
--- /dev/null
--- /dev/null
++error: use of `.to_digit(..).is_some()`
++ --> $DIR/to_digit_is_some.rs:9:13
++ |
++LL | let _ = d.to_digit(10).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
++ |
++ = note: `-D clippy::to-digit-is-some` implied by `-D warnings`
++
++error: use of `.to_digit(..).is_some()`
++ --> $DIR/to_digit_is_some.rs:10:13
++ |
++LL | let _ = char::to_digit(c, 10).is_some();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::toplevel_ref_arg)]
++
++fn main() {
++ // Closures should not warn
++ let y = |ref x| println!("{:?}", x);
++ y(1u8);
++
++ let _x = &1;
++
++ let _y: &(&_, u8) = &(&1, 2);
++
++ let _z = &(1 + 2);
++
++ let _z = &mut (1 + 2);
++
++ let (ref x, _) = (1, 2); // ok, not top level
++ println!("The answer is {}.", x);
++
++ let _x = &vec![1, 2, 3];
++
++ // Make sure that allowing the lint works
++ #[allow(clippy::toplevel_ref_arg)]
++ let ref mut _x = 1_234_543;
++
++ // ok
++ for ref _x in 0..10 {}
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::toplevel_ref_arg)]
++
++fn main() {
++ // Closures should not warn
++ let y = |ref x| println!("{:?}", x);
++ y(1u8);
++
++ let ref _x = 1;
++
++ let ref _y: (&_, u8) = (&1, 2);
++
++ let ref _z = 1 + 2;
++
++ let ref mut _z = 1 + 2;
++
++ let (ref x, _) = (1, 2); // ok, not top level
++ println!("The answer is {}.", x);
++
++ let ref _x = vec![1, 2, 3];
++
++ // Make sure that allowing the lint works
++ #[allow(clippy::toplevel_ref_arg)]
++ let ref mut _x = 1_234_543;
++
++ // ok
++ for ref _x in 0..10 {}
++}
--- /dev/null
--- /dev/null
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++ --> $DIR/toplevel_ref_arg.rs:10:9
++ |
++LL | let ref _x = 1;
++ | ----^^^^^^----- help: try: `let _x = &1;`
++ |
++ = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++ --> $DIR/toplevel_ref_arg.rs:12:9
++ |
++LL | let ref _y: (&_, u8) = (&1, 2);
++ | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++ --> $DIR/toplevel_ref_arg.rs:14:9
++ |
++LL | let ref _z = 1 + 2;
++ | ----^^^^^^--------- help: try: `let _z = &(1 + 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++ --> $DIR/toplevel_ref_arg.rs:16:9
++ |
++LL | let ref mut _z = 1 + 2;
++ | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++ --> $DIR/toplevel_ref_arg.rs:21:9
++ |
++LL | let ref _x = vec![1, 2, 3];
++ | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::toplevel_ref_arg)]
++#![allow(unused)]
++
++fn the_answer(ref mut x: u8) {
++ *x = 42;
++}
++
++fn main() {
++ let mut x = 0;
++ the_answer(x);
++}
--- /dev/null
--- /dev/null
++error: `ref` directly on a function argument is ignored. Consider using a reference type instead.
++ --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15
++ |
++LL | fn the_answer(ref mut x: u8) {
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![allow(unused_parens)]
++
++fn main() {
++ let x: i32 = 42;
++ let _ = (x & 0b1111 == 0); // suggest trailing_zeros
++ let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
++ let _ = x & 0b1_1010 == 0; // do not lint
++ let _ = x & 1 == 0; // do not lint
++}
--- /dev/null
--- /dev/null
++error: bit mask could be simplified with a call to `trailing_zeros`
++ --> $DIR/trailing_zeros.rs:5:13
++ |
++LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros
++ | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4`
++ |
++ = note: `-D clippy::verbose-bit-mask` implied by `-D warnings`
++
++error: bit mask could be simplified with a call to `trailing_zeros`
++ --> $DIR/trailing_zeros.rs:6:13
++ |
++LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
++ | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++
++extern crate core;
++
++use std::mem::transmute as my_transmute;
++use std::vec::Vec as MyVec;
++
++fn my_int() -> Usize {
++ Usize(42)
++}
++
++fn my_vec() -> MyVec<i32> {
++ vec![]
++}
++
++#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)]
++#[warn(clippy::useless_transmute)]
++unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
++ let _: &'a T = core::intrinsics::transmute(t);
++
++ let _: &'a U = core::intrinsics::transmute(t);
++
++ let _: *const T = core::intrinsics::transmute(t);
++
++ let _: *mut T = core::intrinsics::transmute(t);
++
++ let _: *const U = core::intrinsics::transmute(t);
++}
++
++#[warn(clippy::useless_transmute)]
++fn useless() {
++ unsafe {
++ let _: Vec<i32> = core::intrinsics::transmute(my_vec());
++
++ let _: Vec<i32> = core::mem::transmute(my_vec());
++
++ let _: Vec<i32> = std::intrinsics::transmute(my_vec());
++
++ let _: Vec<i32> = std::mem::transmute(my_vec());
++
++ let _: Vec<i32> = my_transmute(my_vec());
++
++ let _: *const usize = std::mem::transmute(5_isize);
++
++ let _ = 5_isize as *const usize;
++
++ let _: *const usize = std::mem::transmute(1 + 1usize);
++
++ let _ = (1 + 1_usize) as *const usize;
++ }
++}
++
++struct Usize(usize);
++
++#[warn(clippy::crosspointer_transmute)]
++fn crosspointer() {
++ let mut int: Usize = Usize(0);
++ let int_const_ptr: *const Usize = &int as *const Usize;
++ let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
++
++ unsafe {
++ let _: Usize = core::intrinsics::transmute(int_const_ptr);
++
++ let _: Usize = core::intrinsics::transmute(int_mut_ptr);
++
++ let _: *const Usize = core::intrinsics::transmute(my_int());
++
++ let _: *mut Usize = core::intrinsics::transmute(my_int());
++ }
++}
++
++#[warn(clippy::transmute_int_to_char)]
++fn int_to_char() {
++ let _: char = unsafe { std::mem::transmute(0_u32) };
++ let _: char = unsafe { std::mem::transmute(0_i32) };
++}
++
++#[warn(clippy::transmute_int_to_bool)]
++fn int_to_bool() {
++ let _: bool = unsafe { std::mem::transmute(0_u8) };
++}
++
++#[warn(clippy::transmute_int_to_float)]
++fn int_to_float() {
++ let _: f32 = unsafe { std::mem::transmute(0_u32) };
++ let _: f32 = unsafe { std::mem::transmute(0_i32) };
++}
++
++fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
++ let _: &str = unsafe { std::mem::transmute(b) };
++ let _: &mut str = unsafe { std::mem::transmute(mb) };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: transmute from a type (`&T`) to itself
++ --> $DIR/transmute.rs:19:20
++ |
++LL | let _: &'a T = core::intrinsics::transmute(t);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::useless-transmute` implied by `-D warnings`
++
++error: transmute from a reference to a pointer
++ --> $DIR/transmute.rs:23:23
++ |
++LL | let _: *const T = core::intrinsics::transmute(t);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
++
++error: transmute from a reference to a pointer
++ --> $DIR/transmute.rs:25:21
++ |
++LL | let _: *mut T = core::intrinsics::transmute(t);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
++
++error: transmute from a reference to a pointer
++ --> $DIR/transmute.rs:27:23
++ |
++LL | let _: *const U = core::intrinsics::transmute(t);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++ --> $DIR/transmute.rs:33:27
++ |
++LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++ --> $DIR/transmute.rs:35:27
++ |
++LL | let _: Vec<i32> = core::mem::transmute(my_vec());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++ --> $DIR/transmute.rs:37:27
++ |
++LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++ --> $DIR/transmute.rs:39:27
++ |
++LL | let _: Vec<i32> = std::mem::transmute(my_vec());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++ --> $DIR/transmute.rs:41:27
++ |
++LL | let _: Vec<i32> = my_transmute(my_vec());
++ | ^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from an integer to a pointer
++ --> $DIR/transmute.rs:43:31
++ |
++LL | let _: *const usize = std::mem::transmute(5_isize);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
++
++error: transmute from an integer to a pointer
++ --> $DIR/transmute.rs:47:31
++ |
++LL | let _: *const usize = std::mem::transmute(1 + 1usize);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
++
++error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
++ --> $DIR/transmute.rs:62:24
++ |
++LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
++
++error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
++ --> $DIR/transmute.rs:64:24
++ |
++LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
++ --> $DIR/transmute.rs:66:31
++ |
++LL | let _: *const Usize = core::intrinsics::transmute(my_int());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
++ --> $DIR/transmute.rs:68:29
++ |
++LL | let _: *mut Usize = core::intrinsics::transmute(my_int());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `u32` to a `char`
++ --> $DIR/transmute.rs:74:28
++ |
++LL | let _: char = unsafe { std::mem::transmute(0_u32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
++ |
++ = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
++
++error: transmute from a `i32` to a `char`
++ --> $DIR/transmute.rs:75:28
++ |
++LL | let _: char = unsafe { std::mem::transmute(0_i32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
++
++error: transmute from a `u8` to a `bool`
++ --> $DIR/transmute.rs:80:28
++ |
++LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
++ |
++ = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
++
++error: transmute from a `u32` to a `f32`
++ --> $DIR/transmute.rs:85:27
++ |
++LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
++ |
++ = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
++
++error: transmute from a `i32` to a `f32`
++ --> $DIR/transmute.rs:86:27
++ |
++LL | let _: f32 = unsafe { std::mem::transmute(0_i32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
++
++error: transmute from a `&[u8]` to a `&str`
++ --> $DIR/transmute.rs:90:28
++ |
++LL | let _: &str = unsafe { std::mem::transmute(b) };
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
++ |
++ = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
++
++error: transmute from a `&mut [u8]` to a `&mut str`
++ --> $DIR/transmute.rs:91:32
++ |
++LL | let _: &mut str = unsafe { std::mem::transmute(mb) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
++
++error: aborting due to 22 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-64bit
++
++#[warn(clippy::wrong_transmute)]
++fn main() {
++ unsafe {
++ let _: *const usize = std::mem::transmute(6.0f32);
++
++ let _: *mut usize = std::mem::transmute(6.0f32);
++
++ let _: *const usize = std::mem::transmute('x');
++
++ let _: *mut usize = std::mem::transmute('x');
++ }
++}
--- /dev/null
--- /dev/null
++error: transmute from a `f32` to a pointer
++ --> $DIR/transmute_32bit.rs:6:31
++ |
++LL | let _: *const usize = std::mem::transmute(6.0f32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::wrong-transmute` implied by `-D warnings`
++
++error: transmute from a `f32` to a pointer
++ --> $DIR/transmute_32bit.rs:8:29
++ |
++LL | let _: *mut usize = std::mem::transmute(6.0f32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `char` to a pointer
++ --> $DIR/transmute_32bit.rs:10:31
++ |
++LL | let _: *const usize = std::mem::transmute('x');
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `char` to a pointer
++ --> $DIR/transmute_32bit.rs:12:29
++ |
++LL | let _: *mut usize = std::mem::transmute('x');
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// ignore-32bit
++
++#[warn(clippy::wrong_transmute)]
++fn main() {
++ unsafe {
++ let _: *const usize = std::mem::transmute(6.0f64);
++
++ let _: *mut usize = std::mem::transmute(6.0f64);
++ }
++}
--- /dev/null
--- /dev/null
++error: transmute from a `f64` to a pointer
++ --> $DIR/transmute_64bit.rs:6:31
++ |
++LL | let _: *const usize = std::mem::transmute(6.0f64);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::wrong-transmute` implied by `-D warnings`
++
++error: transmute from a `f64` to a pointer
++ --> $DIR/transmute_64bit.rs:8:29
++ |
++LL | let _: *mut usize = std::mem::transmute(6.0f64);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unsound_collection_transmute)]
++
++use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
++use std::mem::transmute;
++
++fn main() {
++ unsafe {
++ // wrong size
++ let _ = transmute::<_, Vec<u32>>(vec![0u8]);
++ // wrong layout
++ let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
++
++ // wrong size
++ let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
++ // wrong layout
++ let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
++
++ // wrong size
++ let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
++ // wrong layout
++ let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
++
++ // wrong size
++ let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
++ // wrong layout
++ let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
++
++ // wrong size
++ let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
++ // wrong layout
++ let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
++
++ // wrong size
++ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
++ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
++ // wrong layout
++ let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
++ let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
++
++ // wrong size
++ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
++ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
++ // wrong layout
++ let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
++ let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
++ }
++}
--- /dev/null
--- /dev/null
++error: transmute from `std::vec::Vec<u8>` to `std::vec::Vec<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:9:17
++ |
++LL | let _ = transmute::<_, Vec<u32>>(vec![0u8]);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings`
++
++error: transmute from `std::vec::Vec<u32>` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:11:17
++ |
++LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::VecDeque<u8>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:14:17
++ |
++LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:16:17
++ |
++LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BinaryHeap<u8>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:19:17
++ |
++LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:21:17
++ |
++LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeSet<u8>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:24:17
++ |
++LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:26:17
++ |
++LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashSet<u8>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:29:17
++ |
++LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:31:17
++ |
++LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u8, u8>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:34:17
++ |
++LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u32, u32>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:35:17
++ |
++LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u8, [u8; 4]>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:37:17
++ |
++LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap<u32, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:38:17
++ |
++LL | let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u8, u8>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:41:17
++ |
++LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u32, u32>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:42:17
++ |
++LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u8, [u8; 4]>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:44:17
++ |
++LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap<u32, u32>` with mismatched layout is unsound
++ --> $DIR/transmute_collection.rs:45:17
++ |
++LL | let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 18 previous errors
++
--- /dev/null
--- /dev/null
++#[warn(clippy::transmute_float_to_int)]
++
++fn float_to_int() {
++ let _: u32 = unsafe { std::mem::transmute(1f32) };
++ let _: i32 = unsafe { std::mem::transmute(1f32) };
++ let _: u64 = unsafe { std::mem::transmute(1f64) };
++ let _: i64 = unsafe { std::mem::transmute(1f64) };
++ let _: u64 = unsafe { std::mem::transmute(1.0) };
++ let _: u64 = unsafe { std::mem::transmute(-1.0) };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: transmute from a `f32` to a `u32`
++ --> $DIR/transmute_float_to_int.rs:4:27
++ |
++LL | let _: u32 = unsafe { std::mem::transmute(1f32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()`
++ |
++ = note: `-D clippy::transmute-float-to-int` implied by `-D warnings`
++
++error: transmute from a `f32` to a `i32`
++ --> $DIR/transmute_float_to_int.rs:5:27
++ |
++LL | let _: i32 = unsafe { std::mem::transmute(1f32) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32`
++
++error: transmute from a `f64` to a `u64`
++ --> $DIR/transmute_float_to_int.rs:6:27
++ |
++LL | let _: u64 = unsafe { std::mem::transmute(1f64) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()`
++
++error: transmute from a `f64` to a `i64`
++ --> $DIR/transmute_float_to_int.rs:7:27
++ |
++LL | let _: i64 = unsafe { std::mem::transmute(1f64) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64`
++
++error: transmute from a `f64` to a `u64`
++ --> $DIR/transmute_float_to_int.rs:8:27
++ |
++LL | let _: u64 = unsafe { std::mem::transmute(1.0) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()`
++
++error: transmute from a `f64` to a `u64`
++ --> $DIR/transmute_float_to_int.rs:9:27
++ |
++LL | let _: u64 = unsafe { std::mem::transmute(-1.0) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::transmute_ptr_to_ptr)]
++
++// Make sure we can modify lifetimes, which is one of the recommended uses
++// of transmute
++
++// Make sure we can do static lifetime transmutes
++unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T {
++ std::mem::transmute::<&'a T, &'static T>(t)
++}
++
++// Make sure we can do non-static lifetime transmutes
++unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
++ std::mem::transmute::<&'a T, &'b T>(t)
++}
++
++struct LifetimeParam<'a> {
++ s: &'a str,
++}
++
++struct GenericParam<T> {
++ t: T,
++}
++
++fn transmute_ptr_to_ptr() {
++ let ptr = &1u32 as *const u32;
++ let mut_ptr = &mut 1u32 as *mut u32;
++ unsafe {
++ // pointer-to-pointer transmutes; bad
++ let _: *const f32 = std::mem::transmute(ptr);
++ let _: *mut f32 = std::mem::transmute(mut_ptr);
++ // ref-ref transmutes; bad
++ let _: &f32 = std::mem::transmute(&1u32);
++ let _: &f64 = std::mem::transmute(&1f32);
++ // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
++ // the same type
++ let _: &mut f32 = std::mem::transmute(&mut 1u32);
++ let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
++ }
++
++ // these are recommendations for solving the above; if these lint we need to update
++ // those suggestions
++ let _ = ptr as *const f32;
++ let _ = mut_ptr as *mut f32;
++ let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
++ let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
++
++ // transmute internal lifetimes, should not lint
++ let s = "hello world".to_owned();
++ let lp = LifetimeParam { s: &s };
++ let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
++ let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: transmute from a pointer to a pointer
++ --> $DIR/transmute_ptr_to_ptr.rs:29:29
++ |
++LL | let _: *const f32 = std::mem::transmute(ptr);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
++ |
++ = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
++
++error: transmute from a pointer to a pointer
++ --> $DIR/transmute_ptr_to_ptr.rs:30:27
++ |
++LL | let _: *mut f32 = std::mem::transmute(mut_ptr);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
++
++error: transmute from a reference to a reference
++ --> $DIR/transmute_ptr_to_ptr.rs:32:23
++ |
++LL | let _: &f32 = std::mem::transmute(&1u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
++
++error: transmute from a reference to a reference
++ --> $DIR/transmute_ptr_to_ptr.rs:33:23
++ |
++LL | let _: &f64 = std::mem::transmute(&1f32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)`
++
++error: transmute from a reference to a reference
++ --> $DIR/transmute_ptr_to_ptr.rs:36:27
++ |
++LL | let _: &mut f32 = std::mem::transmute(&mut 1u32);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
++
++error: transmute from a reference to a reference
++ --> $DIR/transmute_ptr_to_ptr.rs:37:37
++ |
++LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::transmute_ptr_to_ref)]
++
++unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
++ let _: &T = std::mem::transmute(p);
++ let _: &T = &*p;
++
++ let _: &mut T = std::mem::transmute(m);
++ let _: &mut T = &mut *m;
++
++ let _: &T = std::mem::transmute(m);
++ let _: &T = &*m;
++
++ let _: &mut T = std::mem::transmute(p as *mut T);
++ let _ = &mut *(p as *mut T);
++
++ let _: &T = std::mem::transmute(o);
++ let _: &T = &*(o as *const T);
++
++ let _: &mut T = std::mem::transmute(om);
++ let _: &mut T = &mut *(om as *mut T);
++
++ let _: &T = std::mem::transmute(om);
++ let _: &T = &*(om as *const T);
++}
++
++fn issue1231() {
++ struct Foo<'a, T> {
++ bar: &'a T,
++ }
++
++ let raw = 42 as *const i32;
++ let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
++
++ let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
++
++ type Bar<'a> = &'a u8;
++ let raw = 42 as *const i32;
++ unsafe { std::mem::transmute::<_, Bar>(raw) };
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
++ --> $DIR/transmute_ptr_to_ref.rs:4:17
++ |
++LL | let _: &T = std::mem::transmute(p);
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
++ |
++ = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
++ --> $DIR/transmute_ptr_to_ref.rs:7:21
++ |
++LL | let _: &mut T = std::mem::transmute(m);
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
++ --> $DIR/transmute_ptr_to_ref.rs:10:17
++ |
++LL | let _: &T = std::mem::transmute(m);
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
++ --> $DIR/transmute_ptr_to_ref.rs:13:21
++ |
++LL | let _: &mut T = std::mem::transmute(p as *mut T);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
++
++error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
++ --> $DIR/transmute_ptr_to_ref.rs:16:17
++ |
++LL | let _: &T = std::mem::transmute(o);
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
++
++error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
++ --> $DIR/transmute_ptr_to_ref.rs:19:21
++ |
++LL | let _: &mut T = std::mem::transmute(om);
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
++
++error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
++ --> $DIR/transmute_ptr_to_ref.rs:22:17
++ |
++LL | let _: &T = std::mem::transmute(om);
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<u8>`)
++ --> $DIR/transmute_ptr_to_ref.rs:32:32
++ |
++LL | let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`)
++ --> $DIR/transmute_ptr_to_ref.rs:34:33
++ |
++LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
++ --> $DIR/transmute_ptr_to_ref.rs:38:14
++ |
++LL | unsafe { std::mem::transmute::<_, Bar>(raw) };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![warn(clippy::transmuting_null)]
++#![allow(clippy::zero_ptr)]
++#![allow(clippy::transmute_ptr_to_ref)]
++#![allow(clippy::eq_op)]
++
++// Easy to lint because these only span one line.
++fn one_liners() {
++ unsafe {
++ let _: &u64 = std::mem::transmute(0 as *const u64);
++ let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
++ }
++}
++
++pub const ZPTR: *const usize = 0 as *const _;
++pub const NOT_ZPTR: *const usize = 1 as *const _;
++
++fn transmute_const() {
++ unsafe {
++ // Should raise a lint.
++ let _: &u64 = std::mem::transmute(ZPTR);
++ // Should NOT raise a lint.
++ let _: &u64 = std::mem::transmute(NOT_ZPTR);
++ }
++}
++
++fn main() {
++ one_liners();
++ transmute_const();
++}
--- /dev/null
--- /dev/null
++error: transmuting a known null pointer into a reference.
++ --> $DIR/transmuting_null.rs:10:23
++ |
++LL | let _: &u64 = std::mem::transmute(0 as *const u64);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::transmuting-null` implied by `-D warnings`
++
++error: transmuting a known null pointer into a reference.
++ --> $DIR/transmuting_null.rs:11:23
++ |
++LL | let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmuting a known null pointer into a reference.
++ --> $DIR/transmuting_null.rs:21:23
++ |
++LL | let _: &u64 = std::mem::transmute(ZPTR);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
++// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
++
++#![deny(clippy::trivially_copy_pass_by_ref)]
++#![allow(
++ clippy::many_single_char_names,
++ clippy::blacklisted_name,
++ clippy::redundant_field_names
++)]
++
++#[derive(Copy, Clone)]
++struct Foo(u32);
++
++#[derive(Copy, Clone)]
++struct Bar([u8; 24]);
++
++#[derive(Copy, Clone)]
++pub struct Color {
++ pub r: u8,
++ pub g: u8,
++ pub b: u8,
++ pub a: u8,
++}
++
++struct FooRef<'a> {
++ foo: &'a Foo,
++}
++
++type Baz = u32;
++
++fn good(a: &mut u32, b: u32, c: &Bar) {}
++
++fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
++ &foo.0
++}
++
++#[allow(clippy::needless_lifetimes)]
++fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
++ &foo.0
++}
++
++fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
++ FooRef { foo }
++}
++
++#[allow(clippy::needless_lifetimes)]
++fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
++ FooRef { foo }
++}
++
++fn bad(x: &u32, y: &Foo, z: &Baz) {}
++
++impl Foo {
++ fn good(self, a: &mut u32, b: u32, c: &Bar) {}
++
++ fn good2(&mut self) {}
++
++ fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++
++ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++}
++
++impl AsRef<u32> for Foo {
++ fn as_ref(&self) -> &u32 {
++ &self.0
++ }
++}
++
++impl Bar {
++ fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
++
++ fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++}
++
++trait MyTrait {
++ fn trait_method(&self, _foo: &Foo);
++}
++
++pub trait MyTrait2 {
++ fn trait_method2(&self, _color: &Color);
++}
++
++impl MyTrait for Foo {
++ fn trait_method(&self, _foo: &Foo) {
++ unimplemented!()
++ }
++}
++
++#[allow(unused_variables)]
++mod issue3992 {
++ pub trait A {
++ #[allow(clippy::trivially_copy_pass_by_ref)]
++ fn a(b: &u16) {}
++ }
++
++ #[allow(clippy::trivially_copy_pass_by_ref)]
++ pub fn c(d: &u16) {}
++}
++
++fn main() {
++ let (mut foo, bar) = (Foo(0), Bar([0; 24]));
++ let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
++ good(&mut a, b, &c);
++ good_return_implicit_lt_ref(&y);
++ good_return_explicit_lt_ref(&y);
++ bad(&x, &y, &z);
++ foo.good(&mut a, b, &c);
++ foo.good2();
++ foo.bad(&x, &y, &z);
++ Foo::bad2(&x, &y, &z);
++ bar.good(&mut a, b, &c);
++ Bar::bad2(&x, &y, &z);
++ foo.as_ref();
++}
--- /dev/null
--- /dev/null
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:51:11
++ |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `u32`
++ |
++note: the lint level is defined here
++ --> $DIR/trivially_copy_pass_by_ref.rs:4:9
++ |
++LL | #![deny(clippy::trivially_copy_pass_by_ref)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:51:20
++ |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:51:29
++ |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:58:12
++ |
++LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^^ help: consider passing by value instead: `self`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:58:22
++ |
++LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:58:31
++ |
++LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:58:40
++ |
++LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:60:16
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:60:25
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:60:34
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:72:16
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:72:25
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:72:34
++ |
++LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++ | ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:76:34
++ |
++LL | fn trait_method(&self, _foo: &Foo);
++ | ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++ --> $DIR/trivially_copy_pass_by_ref.rs:80:37
++ |
++LL | fn trait_method2(&self, _color: &Color);
++ | ^^^^^^ help: consider passing by value instead: `Color`
++
++error: aborting due to 15 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:macro_rules.rs
++
++#![deny(clippy::try_err)]
++
++#[macro_use]
++extern crate macro_rules;
++
++// Tests that a simple case works
++// Should flag `Err(err)?`
++pub fn basic_test() -> Result<i32, i32> {
++ let err: i32 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ return Err(err);
++ }
++ Ok(0)
++}
++
++// Tests that `.into()` is added when appropriate
++pub fn into_test() -> Result<i32, i32> {
++ let err: u8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ return Err(err.into());
++ }
++ Ok(0)
++}
++
++// Tests that tries in general don't trigger the error
++pub fn negative_test() -> Result<i32, i32> {
++ Ok(nested_error()? + 1)
++}
++
++// Tests that `.into()` isn't added when the error type
++// matches the surrounding closure's return type, even
++// when it doesn't match the surrounding function's.
++pub fn closure_matches_test() -> Result<i32, i32> {
++ let res: Result<i32, i8> = Some(1)
++ .into_iter()
++ .map(|i| {
++ let err: i8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ return Err(err);
++ }
++ Ok(i)
++ })
++ .next()
++ .unwrap();
++
++ Ok(res?)
++}
++
++// Tests that `.into()` isn't added when the error type
++// doesn't match the surrounding closure's return type.
++pub fn closure_into_test() -> Result<i32, i32> {
++ let res: Result<i32, i16> = Some(1)
++ .into_iter()
++ .map(|i| {
++ let err: i8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ return Err(err.into());
++ }
++ Ok(i)
++ })
++ .next()
++ .unwrap();
++
++ Ok(res?)
++}
++
++fn nested_error() -> Result<i32, i32> {
++ Ok(1)
++}
++
++fn main() {
++ basic_test().unwrap();
++ into_test().unwrap();
++ negative_test().unwrap();
++ closure_matches_test().unwrap();
++ closure_into_test().unwrap();
++
++ // We don't want to lint in external macros
++ try_err!();
++}
++
++macro_rules! bar {
++ () => {
++ String::from("aasdfasdfasdfa")
++ };
++}
++
++macro_rules! foo {
++ () => {
++ bar!()
++ };
++}
++
++pub fn macro_inside(fail: bool) -> Result<i32, String> {
++ if fail {
++ return Err(foo!());
++ }
++ Ok(0)
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:macro_rules.rs
++
++#![deny(clippy::try_err)]
++
++#[macro_use]
++extern crate macro_rules;
++
++// Tests that a simple case works
++// Should flag `Err(err)?`
++pub fn basic_test() -> Result<i32, i32> {
++ let err: i32 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ Err(err)?;
++ }
++ Ok(0)
++}
++
++// Tests that `.into()` is added when appropriate
++pub fn into_test() -> Result<i32, i32> {
++ let err: u8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ Err(err)?;
++ }
++ Ok(0)
++}
++
++// Tests that tries in general don't trigger the error
++pub fn negative_test() -> Result<i32, i32> {
++ Ok(nested_error()? + 1)
++}
++
++// Tests that `.into()` isn't added when the error type
++// matches the surrounding closure's return type, even
++// when it doesn't match the surrounding function's.
++pub fn closure_matches_test() -> Result<i32, i32> {
++ let res: Result<i32, i8> = Some(1)
++ .into_iter()
++ .map(|i| {
++ let err: i8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ Err(err)?;
++ }
++ Ok(i)
++ })
++ .next()
++ .unwrap();
++
++ Ok(res?)
++}
++
++// Tests that `.into()` isn't added when the error type
++// doesn't match the surrounding closure's return type.
++pub fn closure_into_test() -> Result<i32, i32> {
++ let res: Result<i32, i16> = Some(1)
++ .into_iter()
++ .map(|i| {
++ let err: i8 = 1;
++ // To avoid warnings during rustfix
++ if true {
++ Err(err)?;
++ }
++ Ok(i)
++ })
++ .next()
++ .unwrap();
++
++ Ok(res?)
++}
++
++fn nested_error() -> Result<i32, i32> {
++ Ok(1)
++}
++
++fn main() {
++ basic_test().unwrap();
++ into_test().unwrap();
++ negative_test().unwrap();
++ closure_matches_test().unwrap();
++ closure_into_test().unwrap();
++
++ // We don't want to lint in external macros
++ try_err!();
++}
++
++macro_rules! bar {
++ () => {
++ String::from("aasdfasdfasdfa")
++ };
++}
++
++macro_rules! foo {
++ () => {
++ bar!()
++ };
++}
++
++pub fn macro_inside(fail: bool) -> Result<i32, String> {
++ if fail {
++ Err(foo!())?;
++ }
++ Ok(0)
++}
--- /dev/null
--- /dev/null
++error: returning an `Err(_)` with the `?` operator
++ --> $DIR/try_err.rs:15:9
++ |
++LL | Err(err)?;
++ | ^^^^^^^^^ help: try this: `return Err(err)`
++ |
++note: the lint level is defined here
++ --> $DIR/try_err.rs:4:9
++ |
++LL | #![deny(clippy::try_err)]
++ | ^^^^^^^^^^^^^^^
++
++error: returning an `Err(_)` with the `?` operator
++ --> $DIR/try_err.rs:25:9
++ |
++LL | Err(err)?;
++ | ^^^^^^^^^ help: try this: `return Err(err.into())`
++
++error: returning an `Err(_)` with the `?` operator
++ --> $DIR/try_err.rs:45:17
++ |
++LL | Err(err)?;
++ | ^^^^^^^^^ help: try this: `return Err(err)`
++
++error: returning an `Err(_)` with the `?` operator
++ --> $DIR/try_err.rs:64:17
++ |
++LL | Err(err)?;
++ | ^^^^^^^^^ help: try this: `return Err(err.into())`
++
++error: returning an `Err(_)` with the `?` operator
++ --> $DIR/try_err.rs:103:9
++ |
++LL | Err(foo!())?;
++ | ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// Regression test
++
++pub fn retry<F: Fn()>(f: F) {
++ for _i in 0.. {
++ f();
++ }
++}
++
++fn main() {
++ for y in 0..4 {
++ let func = || ();
++ func();
++ }
++}
--- /dev/null
--- /dev/null
++#[deny(clippy::type_repetition_in_bounds)]
++
++pub fn foo<T>(_t: T)
++where
++ T: Copy,
++ T: Clone,
++{
++ unimplemented!();
++}
++
++pub fn bar<T, U>(_t: T, _u: U)
++where
++ T: Copy,
++ U: Clone,
++{
++ unimplemented!();
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this type has already been used as a bound predicate
++ --> $DIR/type_repetition_in_bounds.rs:6:5
++ |
++LL | T: Clone,
++ | ^^^^^^^^
++ |
++note: the lint level is defined here
++ --> $DIR/type_repetition_in_bounds.rs:1:8
++ |
++LL | #[deny(clippy::type_repetition_in_bounds)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ = help: consider combining the bounds: `T: Copy + Clone`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::cast_lossless)]
++
++// should not warn on lossy casting in constant types
++// because not supported yet
++const C: i32 = 42;
++const C_I64: i64 = C as i64;
++
++fn main() {
++ // should suggest i64::from(c)
++ let c: i32 = 42;
++ let c_i64: i64 = i64::from(c);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::cast_lossless)]
++
++// should not warn on lossy casting in constant types
++// because not supported yet
++const C: i32 = 42;
++const C_I64: i64 = C as i64;
++
++fn main() {
++ // should suggest i64::from(c)
++ let c: i32 = 42;
++ let c_i64: i64 = c as i64;
++}
--- /dev/null
--- /dev/null
++error: casting `i32` to `i64` may become silently lossy if you later change the type
++ --> $DIR/types.rs:14:22
++ |
++LL | let c_i64: i64 = c as i64;
++ | ^^^^^^^^ help: try: `i64::from(c)`
++ |
++ = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#[warn(clippy::zero_width_space)]
++fn zero() {
++ print!("Here >< is a ZWS, and another");
++ print!("This\u{200B}is\u{200B}fine");
++}
++
++#[warn(clippy::unicode_not_nfc)]
++fn canon() {
++ print!("̀àh?");
++ print!("a\u{0300}h?"); // also ok
++}
++
++#[warn(clippy::non_ascii_literal)]
++fn uni() {
++ print!("Üben!");
++ print!("\u{DC}ben!"); // this is ok
++}
++
++fn main() {
++ zero();
++ uni();
++ canon();
++}
--- /dev/null
--- /dev/null
++error: zero-width space detected
++ --> $DIR/unicode.rs:3:12
++ |
++LL | print!("Here >< is a ZWS, and another");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
++ |
++ = note: `-D clippy::zero-width-space` implied by `-D warnings`
++
++error: non-NFC Unicode sequence detected
++ --> $DIR/unicode.rs:9:12
++ |
++LL | print!("̀àh?");
++ | ^^^^^ help: consider replacing the string with: `"̀àh?"`
++ |
++ = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
++
++error: literal non-ASCII character detected
++ --> $DIR/unicode.rs:15:12
++ |
++LL | print!("Üben!");
++ | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
++ |
++ = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![feature(stmt_expr_attributes)]
++
++use std::mem::MaybeUninit;
++
++fn main() {
++ let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++
++ // edge case: For now we lint on empty arrays
++ let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
++
++ // edge case: For now we accept unit tuples
++ let _: () = unsafe { MaybeUninit::uninit().assume_init() };
++
++ // This is OK, because `MaybeUninit` allows uninitialized data.
++ let _: MaybeUninit<usize> = unsafe { MaybeUninit::uninit().assume_init() };
++
++ // This is OK, because all constitutent types are uninit-compatible.
++ let _: (MaybeUninit<usize>, MaybeUninit<bool>) = unsafe { MaybeUninit::uninit().assume_init() };
++
++ // This is OK, because all constitutent types are uninit-compatible.
++ let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
++}
--- /dev/null
--- /dev/null
++error: this call for this type may be undefined behavior
++ --> $DIR/uninit.rs:6:29
++ |
++LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `#[deny(clippy::uninit_assumed_init)]` on by default
++
++error: this call for this type may be undefined behavior
++ --> $DIR/uninit.rs:9:31
++ |
++LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::unit_arg)]
++#![allow(unused_braces, clippy::no_effect, unused_must_use)]
++
++use std::fmt::Debug;
++
++fn foo<T: Debug>(t: T) {
++ println!("{:?}", t);
++}
++
++fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
++ println!("{:?}, {:?}, {:?}", t1, t2, t3);
++}
++
++struct Bar;
++
++impl Bar {
++ fn bar<T: Debug>(&self, t: T) {
++ println!("{:?}", t);
++ }
++}
++
++fn bad() {
++ foo(());
++ foo(());
++ foo(());
++ foo(());
++ foo3((), 2, 2);
++ let b = Bar;
++ b.bar(());
++}
++
++fn ok() {
++ foo(());
++ foo(1);
++ foo({ 1 });
++ foo3("a", 3, vec![3]);
++ let b = Bar;
++ b.bar({ 1 });
++ b.bar(());
++ question_mark();
++}
++
++fn question_mark() -> Result<(), ()> {
++ Ok(Ok(())?)?;
++ Ok(Ok(()))??;
++ Ok(())
++}
++
++#[allow(dead_code)]
++mod issue_2945 {
++ fn unit_fn() -> Result<(), i32> {
++ Ok(())
++ }
++
++ fn fallible() -> Result<(), i32> {
++ Ok(unit_fn()?)
++ }
++}
++
++fn main() {
++ bad();
++ ok();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![warn(clippy::unit_arg)]
++#![allow(unused_braces, clippy::no_effect, unused_must_use)]
++
++use std::fmt::Debug;
++
++fn foo<T: Debug>(t: T) {
++ println!("{:?}", t);
++}
++
++fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
++ println!("{:?}, {:?}, {:?}", t1, t2, t3);
++}
++
++struct Bar;
++
++impl Bar {
++ fn bar<T: Debug>(&self, t: T) {
++ println!("{:?}", t);
++ }
++}
++
++fn bad() {
++ foo({});
++ foo({
++ 1;
++ });
++ foo(foo(1));
++ foo({
++ foo(1);
++ foo(2);
++ });
++ foo3({}, 2, 2);
++ let b = Bar;
++ b.bar({
++ 1;
++ });
++}
++
++fn ok() {
++ foo(());
++ foo(1);
++ foo({ 1 });
++ foo3("a", 3, vec![3]);
++ let b = Bar;
++ b.bar({ 1 });
++ b.bar(());
++ question_mark();
++}
++
++fn question_mark() -> Result<(), ()> {
++ Ok(Ok(())?)?;
++ Ok(Ok(()))??;
++ Ok(())
++}
++
++#[allow(dead_code)]
++mod issue_2945 {
++ fn unit_fn() -> Result<(), i32> {
++ Ok(())
++ }
++
++ fn fallible() -> Result<(), i32> {
++ Ok(unit_fn()?)
++ }
++}
++
++fn main() {
++ bad();
++ ok();
++}
--- /dev/null
--- /dev/null
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:24:9
++ |
++LL | foo({});
++ | ^^
++ |
++ = note: `-D clippy::unit-arg` implied by `-D warnings`
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | foo(());
++ | ^^
++
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:25:9
++ |
++LL | foo({
++ | _________^
++LL | | 1;
++LL | | });
++ | |_____^
++ |
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | foo(());
++ | ^^
++
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:28:9
++ |
++LL | foo(foo(1));
++ | ^^^^^^
++ |
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | foo(());
++ | ^^
++
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:29:9
++ |
++LL | foo({
++ | _________^
++LL | | foo(1);
++LL | | foo(2);
++LL | | });
++ | |_____^
++ |
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | foo(());
++ | ^^
++
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:33:10
++ |
++LL | foo3({}, 2, 2);
++ | ^^
++ |
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | foo3((), 2, 2);
++ | ^^
++
++error: passing a unit value to a function
++ --> $DIR/unit_arg.rs:35:11
++ |
++LL | b.bar({
++ | ___________^
++LL | | 1;
++LL | | });
++ | |_____^
++ |
++help: if you intended to pass a unit value, use a unit literal instead
++ |
++LL | b.bar(());
++ | ^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unit_cmp)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++#[derive(PartialEq)]
++pub struct ContainsUnit(()); // should be fine
++
++fn main() {
++ // this is fine
++ if true == false {}
++
++ // this warns
++ if {
++ true;
++ } == {
++ false;
++ } {}
++
++ if {
++ true;
++ } > {
++ false;
++ } {}
++
++ assert_eq!(
++ {
++ true;
++ },
++ {
++ false;
++ }
++ );
++ debug_assert_eq!(
++ {
++ true;
++ },
++ {
++ false;
++ }
++ );
++
++ assert_ne!(
++ {
++ true;
++ },
++ {
++ false;
++ }
++ );
++ debug_assert_ne!(
++ {
++ true;
++ },
++ {
++ false;
++ }
++ );
++}
--- /dev/null
--- /dev/null
++error: ==-comparison of unit values detected. This will always be true
++ --> $DIR/unit_cmp.rs:12:8
++ |
++LL | if {
++ | ________^
++LL | | true;
++LL | | } == {
++LL | | false;
++LL | | } {}
++ | |_____^
++ |
++ = note: `-D clippy::unit-cmp` implied by `-D warnings`
++
++error: >-comparison of unit values detected. This will always be false
++ --> $DIR/unit_cmp.rs:18:8
++ |
++LL | if {
++ | ________^
++LL | | true;
++LL | | } > {
++LL | | false;
++LL | | } {}
++ | |_____^
++
++error: `assert_eq` of unit values detected. This will always succeed
++ --> $DIR/unit_cmp.rs:24:5
++ |
++LL | / assert_eq!(
++LL | | {
++LL | | true;
++LL | | },
++... |
++LL | | }
++LL | | );
++ | |______^
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert_eq` of unit values detected. This will always succeed
++ --> $DIR/unit_cmp.rs:32:5
++ |
++LL | / debug_assert_eq!(
++LL | | {
++LL | | true;
++LL | | },
++... |
++LL | | }
++LL | | );
++ | |______^
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert_ne` of unit values detected. This will always fail
++ --> $DIR/unit_cmp.rs:41:5
++ |
++LL | / assert_ne!(
++LL | | {
++LL | | true;
++LL | | },
++... |
++LL | | }
++LL | | );
++ | |______^
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert_ne` of unit values detected. This will always fail
++ --> $DIR/unit_cmp.rs:49:5
++ |
++LL | / debug_assert_ne!(
++LL | | {
++LL | | true;
++LL | | },
++... |
++LL | | }
++LL | | );
++ | |______^
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#[clippy::unknown]
++#[clippy::cognitive_complexity = "1"]
++fn main() {}
--- /dev/null
--- /dev/null
++error: Usage of unknown attribute
++ --> $DIR/unknown_attribute.rs:1:11
++ |
++LL | #[clippy::unknown]
++ | ^^^^^^^
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::pedantic)]
++// Should suggest lowercase
++#![allow(clippy::all)]
++#![warn(clippy::cmp_nan)]
++
++// Should suggest similar clippy lint name
++#[warn(clippy::if_not_else)]
++#[warn(clippy::unnecessary_cast)]
++#[warn(clippy::useless_transmute)]
++// Shouldn't suggest rustc lint name(`dead_code`)
++#[warn(clippy::drop_copy)]
++// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
++#[warn(clippy::unused_self)]
++// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
++#[warn(clippy::redundant_static_lifetimes)]
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::pedantic)]
++// Should suggest lowercase
++#![allow(clippy::All)]
++#![warn(clippy::CMP_NAN)]
++
++// Should suggest similar clippy lint name
++#[warn(clippy::if_not_els)]
++#[warn(clippy::UNNecsaRy_cAst)]
++#[warn(clippy::useles_transute)]
++// Shouldn't suggest rustc lint name(`dead_code`)
++#[warn(clippy::dead_cod)]
++// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
++#[warn(clippy::unused_colle)]
++// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
++#[warn(clippy::const_static_lifetim)]
++fn main() {}
--- /dev/null
--- /dev/null
++error: unknown clippy lint: clippy::if_not_els
++ --> $DIR/unknown_clippy_lints.rs:9:8
++ |
++LL | #[warn(clippy::if_not_els)]
++ | ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else`
++ |
++ = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings`
++
++error: unknown clippy lint: clippy::UNNecsaRy_cAst
++ --> $DIR/unknown_clippy_lints.rs:10:8
++ |
++LL | #[warn(clippy::UNNecsaRy_cAst)]
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_cast`
++
++error: unknown clippy lint: clippy::useles_transute
++ --> $DIR/unknown_clippy_lints.rs:11:8
++ |
++LL | #[warn(clippy::useles_transute)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::useless_transmute`
++
++error: unknown clippy lint: clippy::dead_cod
++ --> $DIR/unknown_clippy_lints.rs:13:8
++ |
++LL | #[warn(clippy::dead_cod)]
++ | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::drop_copy`
++
++error: unknown clippy lint: clippy::unused_colle
++ --> $DIR/unknown_clippy_lints.rs:15:8
++ |
++LL | #[warn(clippy::unused_colle)]
++ | ^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unused_self`
++
++error: unknown clippy lint: clippy::const_static_lifetim
++ --> $DIR/unknown_clippy_lints.rs:17:8
++ |
++LL | #[warn(clippy::const_static_lifetim)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes`
++
++error: unknown clippy lint: clippy::All
++ --> $DIR/unknown_clippy_lints.rs:5:10
++ |
++LL | #![allow(clippy::All)]
++ | ^^^^^^^^^^^ help: lowercase the lint name: `clippy::all`
++
++error: unknown clippy lint: clippy::CMP_NAN
++ --> $DIR/unknown_clippy_lints.rs:6:9
++ |
++LL | #![warn(clippy::CMP_NAN)]
++ | ^^^^^^^^^^^^^^^ help: lowercase the lint name: `clippy::cmp_nan`
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect)]
++
++fn main() {
++ // Test cast_unnecessary
++ 1i32 as i32;
++ 1f32 as f32;
++ false as bool;
++ &1i32 as &i32;
++
++ // macro version
++ macro_rules! foo {
++ ($a:ident, $b:ident) => {
++ #[allow(unused)]
++ pub fn $a() -> $b {
++ 1 as $b
++ }
++ };
++ }
++ foo!(a, i32);
++ foo!(b, f32);
++ foo!(c, f64);
++}
--- /dev/null
--- /dev/null
++error: casting to the same type is unnecessary (`i32` -> `i32`)
++ --> $DIR/unnecessary_cast.rs:6:5
++ |
++LL | 1i32 as i32;
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: casting to the same type is unnecessary (`f32` -> `f32`)
++ --> $DIR/unnecessary_cast.rs:7:5
++ |
++LL | 1f32 as f32;
++ | ^^^^^^^^^^^
++
++error: casting to the same type is unnecessary (`bool` -> `bool`)
++ --> $DIR/unnecessary_cast.rs:8:5
++ |
++LL | false as bool;
++ | ^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++ // casting integer literal to float is unnecessary
++ 100_f32;
++ 100_f64;
++ 100_f64;
++ // Should not trigger
++ #[rustfmt::skip]
++ let v = vec!(1);
++ &v as &[i32];
++ 1.0 as f64;
++ 1 as u64;
++ 0x10 as f32;
++ 0o10 as f32;
++ 0b10 as f32;
++ 0x11 as f64;
++ 0o11 as f64;
++ 0b11 as f64;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++ // casting integer literal to float is unnecessary
++ 100 as f32;
++ 100 as f64;
++ 100_i32 as f64;
++ // Should not trigger
++ #[rustfmt::skip]
++ let v = vec!(1);
++ &v as &[i32];
++ 1.0 as f64;
++ 1 as u64;
++ 0x10 as f32;
++ 0o10 as f32;
++ 0b10 as f32;
++ 0x11 as f64;
++ 0o11 as f64;
++ 0b11 as f64;
++}
--- /dev/null
--- /dev/null
++error: casting integer literal to `f32` is unnecessary
++ --> $DIR/unnecessary_cast_fixable.rs:8:5
++ |
++LL | 100 as f32;
++ | ^^^^^^^^^^ help: try: `100_f32`
++ |
++ = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: casting integer literal to `f64` is unnecessary
++ --> $DIR/unnecessary_cast_fixable.rs:9:5
++ |
++LL | 100 as f64;
++ | ^^^^^^^^^^ help: try: `100_f64`
++
++error: casting integer literal to `f64` is unnecessary
++ --> $DIR/unnecessary_cast_fixable.rs:10:5
++ |
++LL | 100_i32 as f64;
++ | ^^^^^^^^^^^^^^ help: try: `100_f64`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// does not test any rustfixable lints
++
++#![warn(clippy::clone_on_ref_ptr)]
++#![allow(unused, clippy::redundant_clone)]
++
++use std::cell::RefCell;
++use std::rc::{self, Rc};
++use std::sync::{self, Arc};
++
++trait SomeTrait {}
++struct SomeImpl;
++impl SomeTrait for SomeImpl {}
++
++fn main() {}
++
++fn is_ascii(ch: char) -> bool {
++ ch.is_ascii()
++}
++
++fn clone_on_copy() {
++ 42.clone();
++
++ vec![1].clone(); // ok, not a Copy type
++ Some(vec![1]).clone(); // ok, not a Copy type
++ (&42).clone();
++
++ let rc = RefCell::new(0);
++ rc.borrow().clone();
++
++ // Issue #4348
++ let mut x = 43;
++ let _ = &x.clone(); // ok, getting a ref
++ 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
++ is_ascii('z'.clone());
++
++ // Issue #5436
++ let mut vec = Vec::new();
++ vec.push(42.clone());
++}
++
++fn clone_on_ref_ptr() {
++ let rc = Rc::new(true);
++ let arc = Arc::new(true);
++
++ let rcweak = Rc::downgrade(&rc);
++ let arc_weak = Arc::downgrade(&arc);
++
++ rc.clone();
++ Rc::clone(&rc);
++
++ arc.clone();
++ Arc::clone(&arc);
++
++ rcweak.clone();
++ rc::Weak::clone(&rcweak);
++
++ arc_weak.clone();
++ sync::Weak::clone(&arc_weak);
++
++ let x = Arc::new(SomeImpl);
++ let _: Arc<dyn SomeTrait> = x.clone();
++}
++
++fn clone_on_copy_generic<T: Copy>(t: T) {
++ t.clone();
++
++ Some(t).clone();
++}
++
++fn clone_on_double_ref() {
++ let x = vec![1];
++ let y = &&x;
++ let z: &Vec<_> = y.clone();
++
++ println!("{:p} {:p}", *y, z);
++}
++
++mod many_derefs {
++ struct A;
++ struct B;
++ struct C;
++ struct D;
++ #[derive(Copy, Clone)]
++ struct E;
++
++ macro_rules! impl_deref {
++ ($src:ident, $dst:ident) => {
++ impl std::ops::Deref for $src {
++ type Target = $dst;
++ fn deref(&self) -> &Self::Target {
++ &$dst
++ }
++ }
++ };
++ }
++
++ impl_deref!(A, B);
++ impl_deref!(B, C);
++ impl_deref!(C, D);
++ impl std::ops::Deref for D {
++ type Target = &'static E;
++ fn deref(&self) -> &Self::Target {
++ &&E
++ }
++ }
++
++ fn go1() {
++ let a = A;
++ let _: E = a.clone();
++ let _: E = *****a;
++ }
++
++ fn check(mut encoded: &[u8]) {
++ let _ = &mut encoded.clone();
++ let _ = &encoded.clone();
++ }
++}
--- /dev/null
--- /dev/null
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:21:5
++ |
++LL | 42.clone();
++ | ^^^^^^^^^^ help: try removing the `clone` call: `42`
++ |
++ = note: `-D clippy::clone-on-copy` implied by `-D warnings`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:25:5
++ |
++LL | (&42).clone();
++ | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:28:5
++ |
++LL | rc.borrow().clone();
++ | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:34:14
++ |
++LL | is_ascii('z'.clone());
++ | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:38:14
++ |
++LL | vec.push(42.clone());
++ | ^^^^^^^^^^ help: try removing the `clone` call: `42`
++
++error: using `.clone()` on a ref-counted pointer
++ --> $DIR/unnecessary_clone.rs:48:5
++ |
++LL | rc.clone();
++ | ^^^^^^^^^^ help: try this: `Rc::<bool>::clone(&rc)`
++ |
++ = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
++
++error: using `.clone()` on a ref-counted pointer
++ --> $DIR/unnecessary_clone.rs:51:5
++ |
++LL | arc.clone();
++ | ^^^^^^^^^^^ help: try this: `Arc::<bool>::clone(&arc)`
++
++error: using `.clone()` on a ref-counted pointer
++ --> $DIR/unnecessary_clone.rs:54:5
++ |
++LL | rcweak.clone();
++ | ^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&rcweak)`
++
++error: using `.clone()` on a ref-counted pointer
++ --> $DIR/unnecessary_clone.rs:57:5
++ |
++LL | arc_weak.clone();
++ | ^^^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&arc_weak)`
++
++error: using `.clone()` on a ref-counted pointer
++ --> $DIR/unnecessary_clone.rs:61:33
++ |
++LL | let _: Arc<dyn SomeTrait> = x.clone();
++ | ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:65:5
++ |
++LL | t.clone();
++ | ^^^^^^^^^ help: try removing the `clone` call: `t`
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:67:5
++ |
++LL | Some(t).clone();
++ | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++ --> $DIR/unnecessary_clone.rs:73:22
++ |
++LL | let z: &Vec<_> = y.clone();
++ | ^^^^^^^^^
++ |
++ = note: `#[deny(clippy::clone_double_ref)]` on by default
++help: try dereferencing it
++ |
++LL | let z: &Vec<_> = &(*y).clone();
++ | ^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++ |
++LL | let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `clone` on a `Copy` type
++ --> $DIR/unnecessary_clone.rs:109:20
++ |
++LL | let _: E = a.clone();
++ | ^^^^^^^^^ help: try dereferencing it: `*****a`
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++ --> $DIR/unnecessary_clone.rs:114:22
++ |
++LL | let _ = &mut encoded.clone();
++ | ^^^^^^^^^^^^^^^
++ |
++help: try dereferencing it
++ |
++LL | let _ = &mut &(*encoded).clone();
++ | ^^^^^^^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++ |
++LL | let _ = &mut <&[u8]>::clone(encoded);
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++ --> $DIR/unnecessary_clone.rs:115:18
++ |
++LL | let _ = &encoded.clone();
++ | ^^^^^^^^^^^^^^^
++ |
++help: try dereferencing it
++ |
++LL | let _ = &&(*encoded).clone();
++ | ^^^^^^^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++ |
++LL | let _ = &<&[u8]>::clone(encoded);
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 16 previous errors
++
--- /dev/null
--- /dev/null
++fn main() {
++ let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
++ let _ = (0..4).filter_map(|x| {
++ if x > 1 {
++ return Some(x);
++ };
++ None
++ });
++ let _ = (0..4).filter_map(|x| match x {
++ 0 | 1 => None,
++ _ => Some(x),
++ });
++
++ let _ = (0..4).filter_map(|x| Some(x + 1));
++
++ let _ = (0..4).filter_map(i32::checked_abs);
++}
--- /dev/null
--- /dev/null
++error: this `.filter_map` can be written more simply using `.filter`
++ --> $DIR/unnecessary_filter_map.rs:2:13
++ |
++LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
++
++error: this `.filter_map` can be written more simply using `.filter`
++ --> $DIR/unnecessary_filter_map.rs:3:13
++ |
++LL | let _ = (0..4).filter_map(|x| {
++ | _____________^
++LL | | if x > 1 {
++LL | | return Some(x);
++LL | | };
++LL | | None
++LL | | });
++ | |______^
++
++error: this `.filter_map` can be written more simply using `.filter`
++ --> $DIR/unnecessary_filter_map.rs:9:13
++ |
++LL | let _ = (0..4).filter_map(|x| match x {
++ | _____________^
++LL | | 0 | 1 => None,
++LL | | _ => Some(x),
++LL | | });
++ | |______^
++
++error: this `.filter_map` can be written more simply using `.map`
++ --> $DIR/unnecessary_filter_map.rs:14:13
++ |
++LL | let _ = (0..4).filter_map(|x| Some(x + 1));
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports)]
++#![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();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_imports)]
++#![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);
++}
--- /dev/null
--- /dev/null
++error: called `flat_map(|x| x)` on an `Iterator`
++ --> $DIR/unnecessary_flat_map.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`
++ --> $DIR/unnecessary_flat_map.rs:13:22
++ |
++LL | let _ = iterator.flat_map(convert::identity);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code)]
++
++/// Calls which should trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold() {
++ // Can be replaced by .any
++ let _ = (0..3).any(|x| x > 2);
++ // Can be replaced by .all
++ let _ = (0..3).all(|x| x > 2);
++ // Can be replaced by .sum
++ let _: i32 = (0..3).sum();
++ // Can be replaced by .product
++ let _: i32 = (0..3).product();
++}
++
++/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
++fn unnecessary_fold_span_for_multi_element_chain() {
++ let _: bool = (0..3).map(|x| 2 * x).any(|x| x > 2);
++}
++
++/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold_should_ignore() {
++ let _ = (0..3).fold(true, |acc, x| acc || x > 2);
++ let _ = (0..3).fold(false, |acc, x| acc && x > 2);
++ let _ = (0..3).fold(1, |acc, x| acc + x);
++ let _ = (0..3).fold(0, |acc, x| acc * x);
++ let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
++
++ // We only match against an accumulator on the left
++ // hand side. We could lint for .sum and .product when
++ // it's on the right, but don't for now (and this wouldn't
++ // be valid if we extended the lint to cover arbitrary numeric
++ // types).
++ let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
++ let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
++ let _ = (0..3).fold(0, |acc, x| x + acc);
++ let _ = (0..3).fold(1, |acc, x| x * acc);
++
++ let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
++ let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
++}
++
++/// Should lint only the line containing the fold
++fn unnecessary_fold_over_multiple_lines() {
++ let _ = (0..3)
++ .map(|x| x + 1)
++ .filter(|x| x % 2 == 0)
++ .any(|x| x > 2);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code)]
++
++/// Calls which should trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold() {
++ // Can be replaced by .any
++ let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++ // Can be replaced by .all
++ let _ = (0..3).fold(true, |acc, x| acc && x > 2);
++ // Can be replaced by .sum
++ let _: i32 = (0..3).fold(0, |acc, x| acc + x);
++ // Can be replaced by .product
++ let _: i32 = (0..3).fold(1, |acc, x| acc * x);
++}
++
++/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
++fn unnecessary_fold_span_for_multi_element_chain() {
++ let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
++}
++
++/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold_should_ignore() {
++ let _ = (0..3).fold(true, |acc, x| acc || x > 2);
++ let _ = (0..3).fold(false, |acc, x| acc && x > 2);
++ let _ = (0..3).fold(1, |acc, x| acc + x);
++ let _ = (0..3).fold(0, |acc, x| acc * x);
++ let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
++
++ // We only match against an accumulator on the left
++ // hand side. We could lint for .sum and .product when
++ // it's on the right, but don't for now (and this wouldn't
++ // be valid if we extended the lint to cover arbitrary numeric
++ // types).
++ let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
++ let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
++ let _ = (0..3).fold(0, |acc, x| x + acc);
++ let _ = (0..3).fold(1, |acc, x| x * acc);
++
++ let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
++ let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
++}
++
++/// Should lint only the line containing the fold
++fn unnecessary_fold_over_multiple_lines() {
++ let _ = (0..3)
++ .map(|x| x + 1)
++ .filter(|x| x % 2 == 0)
++ .fold(false, |acc, x| acc || x > 2);
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:8:20
++ |
++LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++ |
++ = note: `-D clippy::unnecessary-fold` implied by `-D warnings`
++
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:10:20
++ |
++LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)`
++
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:12:25
++ |
++LL | let _: i32 = (0..3).fold(0, |acc, x| acc + x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
++
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:14:25
++ |
++LL | let _: i32 = (0..3).fold(1, |acc, x| acc * x);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
++
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:19:41
++ |
++LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++
++error: this `.fold` can be written more succinctly using another method
++ --> $DIR/unnecessary_fold.rs:49:10
++ |
++LL | .fold(false, |acc, x| acc || x > 2);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![feature(box_syntax)]
++#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
++#![warn(clippy::unnecessary_operation)]
++
++struct Tuple(i32);
++struct Struct {
++ field: i32,
++}
++enum Enum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++struct DropStruct {
++ field: i32,
++}
++impl Drop for DropStruct {
++ fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++ fn drop(&mut self) {}
++}
++enum DropEnum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++impl Drop for DropEnum {
++ fn drop(&mut self) {}
++}
++struct FooString {
++ s: String,
++}
++
++fn get_number() -> i32 {
++ 0
++}
++
++fn get_usize() -> usize {
++ 0
++}
++fn get_struct() -> Struct {
++ Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++ DropStruct { field: 0 }
++}
++
++fn main() {
++ get_number();
++ get_number();
++ get_struct();
++ get_number();
++ get_number();
++ 5;get_number();
++ get_number();
++ get_number();
++ 5;6;get_number();
++ get_number();
++ get_number();
++ get_number();
++ 5;get_number();
++ 42;get_number();
++ [42, 55];get_usize();
++ 42;get_number();
++ get_number();
++ [42; 55];get_usize();
++ get_number();
++ String::from("blah");
++
++ // Do not warn
++ DropTuple(get_number());
++ DropStruct { field: get_number() };
++ DropStruct { field: get_number() };
++ DropStruct { ..get_drop_struct() };
++ DropEnum::Tuple(get_number());
++ DropEnum::Struct { field: get_number() };
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![feature(box_syntax)]
++#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
++#![warn(clippy::unnecessary_operation)]
++
++struct Tuple(i32);
++struct Struct {
++ field: i32,
++}
++enum Enum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++struct DropStruct {
++ field: i32,
++}
++impl Drop for DropStruct {
++ fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++ fn drop(&mut self) {}
++}
++enum DropEnum {
++ Tuple(i32),
++ Struct { field: i32 },
++}
++impl Drop for DropEnum {
++ fn drop(&mut self) {}
++}
++struct FooString {
++ s: String,
++}
++
++fn get_number() -> i32 {
++ 0
++}
++
++fn get_usize() -> usize {
++ 0
++}
++fn get_struct() -> Struct {
++ Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++ DropStruct { field: 0 }
++}
++
++fn main() {
++ Tuple(get_number());
++ Struct { field: get_number() };
++ Struct { ..get_struct() };
++ Enum::Tuple(get_number());
++ Enum::Struct { field: get_number() };
++ 5 + get_number();
++ *&get_number();
++ &get_number();
++ (5, 6, get_number());
++ box get_number();
++ get_number()..;
++ ..get_number();
++ 5..get_number();
++ [42, get_number()];
++ [42, 55][get_usize()];
++ (42, get_number()).1;
++ [get_number(); 55];
++ [42; 55][get_usize()];
++ {
++ get_number()
++ };
++ FooString {
++ s: String::from("blah"),
++ };
++
++ // Do not warn
++ DropTuple(get_number());
++ DropStruct { field: get_number() };
++ DropStruct { field: get_number() };
++ DropStruct { ..get_drop_struct() };
++ DropEnum::Tuple(get_number());
++ DropEnum::Struct { field: get_number() };
++}
--- /dev/null
--- /dev/null
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:51:5
++ |
++LL | Tuple(get_number());
++ | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++ |
++ = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:52:5
++ |
++LL | Struct { field: get_number() };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:53:5
++ |
++LL | Struct { ..get_struct() };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:54:5
++ |
++LL | Enum::Tuple(get_number());
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:55:5
++ |
++LL | Enum::Struct { field: get_number() };
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:56:5
++ |
++LL | 5 + get_number();
++ | ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:57:5
++ |
++LL | *&get_number();
++ | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:58:5
++ |
++LL | &get_number();
++ | ^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:59:5
++ |
++LL | (5, 6, get_number());
++ | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:60:5
++ |
++LL | box get_number();
++ | ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:61:5
++ |
++LL | get_number()..;
++ | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:62:5
++ |
++LL | ..get_number();
++ | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:63:5
++ |
++LL | 5..get_number();
++ | ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:64:5
++ |
++LL | [42, get_number()];
++ | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:65:5
++ |
++LL | [42, 55][get_usize()];
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:66:5
++ |
++LL | (42, get_number()).1;
++ | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:67:5
++ |
++LL | [get_number(); 55];
++ | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:68:5
++ |
++LL | [42; 55][get_usize()];
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:69:5
++ |
++LL | / {
++LL | | get_number()
++LL | | };
++ | |______^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++ --> $DIR/unnecessary_operation.rs:72:5
++ |
++LL | / FooString {
++LL | | s: String::from("blah"),
++LL | | };
++ | |______^ help: replace it with: `String::from("blah");`
++
++error: aborting due to 20 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![feature(stmt_expr_attributes)]
++#![allow(unused_variables)]
++
++struct Outer {
++ inner: u32,
++}
++
++#[deny(clippy::ref_in_deref)]
++fn main() {
++ let outer = Outer { inner: 0 };
++ let inner = outer.inner;
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![feature(stmt_expr_attributes)]
++#![allow(unused_variables)]
++
++struct Outer {
++ inner: u32,
++}
++
++#[deny(clippy::ref_in_deref)]
++fn main() {
++ let outer = Outer { inner: 0 };
++ let inner = (&outer).inner;
++}
--- /dev/null
--- /dev/null
++error: Creating a reference that is immediately dereferenced.
++ --> $DIR/unnecessary_ref.rs:13:17
++ |
++LL | let inner = (&outer).inner;
++ | ^^^^^^^^ help: try this: `outer`
++ |
++note: the lint level is defined here
++ --> $DIR/unnecessary_ref.rs:10:8
++ |
++LL | #[deny(clippy::ref_in_deref)]
++ | ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unneeded_field_pattern)]
++#[allow(dead_code, unused)]
++
++struct Foo {
++ a: i32,
++ b: i32,
++ c: i32,
++}
++
++fn main() {
++ let f = Foo { a: 0, b: 0, c: 0 };
++
++ match f {
++ Foo { a: _, b: 0, .. } => {},
++
++ Foo { a: _, b: _, c: _ } => {},
++ }
++ match f {
++ Foo { b: 0, .. } => {}, // should be OK
++ Foo { .. } => {}, // and the Force might be with this one
++ }
++}
--- /dev/null
--- /dev/null
++error: You matched a field with a wildcard pattern. Consider using `..` instead
++ --> $DIR/unneeded_field_pattern.rs:14:15
++ |
++LL | Foo { a: _, b: 0, .. } => {},
++ | ^^^^
++ |
++ = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
++ = help: Try with `Foo { b: 0, .. }`
++
++error: All the struct fields are matched to a wildcard pattern, consider using `..`.
++ --> $DIR/unneeded_field_pattern.rs:16:9
++ |
++LL | Foo { a: _, b: _, c: _ } => {},
++ | ^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: Try with `Foo { .. }` instead
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++#![deny(clippy::unneeded_wildcard_pattern)]
++
++fn main() {
++ let t = (0, 1, 2, 3);
++
++ if let (0, ..) = t {};
++ if let (0, ..) = t {};
++ if let (.., 0) = t {};
++ if let (.., 0) = t {};
++ if let (0, ..) = t {};
++ if let (0, ..) = t {};
++ if let (_, 0, ..) = t {};
++ if let (.., 0, _) = t {};
++ if let (0, _, _, _) = t {};
++ if let (0, ..) = t {};
++ if let (.., 0) = t {};
++
++ #[rustfmt::skip]
++ {
++ if let (0, ..,) = t {};
++ }
++
++ struct S(usize, usize, usize, usize);
++
++ let s = S(0, 1, 2, 3);
++
++ if let S(0, ..) = s {};
++ if let S(0, ..) = s {};
++ if let S(.., 0) = s {};
++ if let S(.., 0) = s {};
++ if let S(0, ..) = s {};
++ if let S(0, ..) = s {};
++ if let S(_, 0, ..) = s {};
++ if let S(.., 0, _) = s {};
++ if let S(0, _, _, _) = s {};
++ if let S(0, ..) = s {};
++ if let S(.., 0) = s {};
++
++ #[rustfmt::skip]
++ {
++ if let S(0, ..,) = s {};
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++#![deny(clippy::unneeded_wildcard_pattern)]
++
++fn main() {
++ let t = (0, 1, 2, 3);
++
++ if let (0, .., _) = t {};
++ if let (0, _, ..) = t {};
++ if let (_, .., 0) = t {};
++ if let (.., _, 0) = t {};
++ if let (0, _, _, ..) = t {};
++ if let (0, .., _, _) = t {};
++ if let (_, 0, ..) = t {};
++ if let (.., 0, _) = t {};
++ if let (0, _, _, _) = t {};
++ if let (0, ..) = t {};
++ if let (.., 0) = t {};
++
++ #[rustfmt::skip]
++ {
++ if let (0, .., _, _,) = t {};
++ }
++
++ struct S(usize, usize, usize, usize);
++
++ let s = S(0, 1, 2, 3);
++
++ if let S(0, .., _) = s {};
++ if let S(0, _, ..) = s {};
++ if let S(_, .., 0) = s {};
++ if let S(.., _, 0) = s {};
++ if let S(0, _, _, ..) = s {};
++ if let S(0, .., _, _) = s {};
++ if let S(_, 0, ..) = s {};
++ if let S(.., 0, _) = s {};
++ if let S(0, _, _, _) = s {};
++ if let S(0, ..) = s {};
++ if let S(.., 0) = s {};
++
++ #[rustfmt::skip]
++ {
++ if let S(0, .., _, _,) = s {};
++ }
++}
--- /dev/null
--- /dev/null
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:8:18
++ |
++LL | if let (0, .., _) = t {};
++ | ^^^ help: remove it
++ |
++note: the lint level is defined here
++ --> $DIR/unneeded_wildcard_pattern.rs:3:9
++ |
++LL | #![deny(clippy::unneeded_wildcard_pattern)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:9:16
++ |
++LL | if let (0, _, ..) = t {};
++ | ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:10:13
++ |
++LL | if let (_, .., 0) = t {};
++ | ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:11:15
++ |
++LL | if let (.., _, 0) = t {};
++ | ^^^ help: remove it
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:12:16
++ |
++LL | if let (0, _, _, ..) = t {};
++ | ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:13:18
++ |
++LL | if let (0, .., _, _) = t {};
++ | ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:22:22
++ |
++LL | if let (0, .., _, _,) = t {};
++ | ^^^^^^ help: remove them
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:29:19
++ |
++LL | if let S(0, .., _) = s {};
++ | ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:30:17
++ |
++LL | if let S(0, _, ..) = s {};
++ | ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:31:14
++ |
++LL | if let S(_, .., 0) = s {};
++ | ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++ --> $DIR/unneeded_wildcard_pattern.rs:32:16
++ |
++LL | if let S(.., _, 0) = s {};
++ | ^^^ help: remove it
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:33:17
++ |
++LL | if let S(0, _, _, ..) = s {};
++ | ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:34:19
++ |
++LL | if let S(0, .., _, _) = s {};
++ | ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++ --> $DIR/unneeded_wildcard_pattern.rs:43:23
++ |
++LL | if let S(0, .., _, _,) = s {};
++ | ^^^^^^ help: remove them
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unreadable_literal)]
++
++struct Foo(u64);
++
++macro_rules! foo {
++ () => {
++ Foo(123123123123)
++ };
++}
++
++fn main() {
++ let _good = (
++ 0b1011_i64,
++ 0o1_234_u32,
++ 0x1_234_567,
++ 65536,
++ 1_2345_6789,
++ 1234_f32,
++ 1_234.12_f32,
++ 1_234.123_f32,
++ 1.123_4_f32,
++ );
++ let _bad = (0b11_0110_i64, 0xcafe_babe_usize, 123_456_f32, 1.234_567_f32);
++ let _good_sci = 1.1234e1;
++ let _bad_sci = 1.123_456e1;
++
++ let _fail9 = 0x00ab_cdef;
++ let _fail10: u32 = 0xBAFE_BAFE;
++ let _fail11 = 0x0abc_deff;
++ let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc;
++
++ let _ = foo!();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unreadable_literal)]
++
++struct Foo(u64);
++
++macro_rules! foo {
++ () => {
++ Foo(123123123123)
++ };
++}
++
++fn main() {
++ let _good = (
++ 0b1011_i64,
++ 0o1_234_u32,
++ 0x1_234_567,
++ 65536,
++ 1_2345_6789,
++ 1234_f32,
++ 1_234.12_f32,
++ 1_234.123_f32,
++ 1.123_4_f32,
++ );
++ let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++ let _good_sci = 1.1234e1;
++ let _bad_sci = 1.123456e1;
++
++ let _fail9 = 0xabcdef;
++ let _fail10: u32 = 0xBAFEBAFE;
++ let _fail11 = 0xabcdeff;
++ let _fail12: i128 = 0xabcabcabcabcabcabc;
++
++ let _ = foo!();
++}
--- /dev/null
--- /dev/null
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:25:17
++ |
++LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++ | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
++ |
++ = note: `-D clippy::unreadable-literal` implied by `-D warnings`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:25:31
++ |
++LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++ | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:25:49
++ |
++LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++ | ^^^^^^^^^^ help: consider: `123_456_f32`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:25:61
++ |
++LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++ | ^^^^^^^^^^^^ help: consider: `1.234_567_f32`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:27:20
++ |
++LL | let _bad_sci = 1.123456e1;
++ | ^^^^^^^^^^ help: consider: `1.123_456e1`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:29:18
++ |
++LL | let _fail9 = 0xabcdef;
++ | ^^^^^^^^ help: consider: `0x00ab_cdef`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:30:24
++ |
++LL | let _fail10: u32 = 0xBAFEBAFE;
++ | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:31:19
++ |
++LL | let _fail11 = 0xabcdeff;
++ | ^^^^^^^^^ help: consider: `0x0abc_deff`
++
++error: long literal lacking separators
++ --> $DIR/unreadable_literal.rs:32:25
++ |
++LL | let _fail12: i128 = 0xabcabcabcabcabcabc;
++ | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unsafe_derive_deserialize)]
++#![allow(unused, clippy::missing_safety_doc)]
++
++extern crate serde;
++
++use serde::Deserialize;
++
++#[derive(Deserialize)]
++pub struct A {}
++impl A {
++ pub unsafe fn new(_a: i32, _b: i32) -> Self {
++ Self {}
++ }
++}
++
++#[derive(Deserialize)]
++pub struct B {}
++impl B {
++ pub unsafe fn unsafe_method(&self) {}
++}
++
++#[derive(Deserialize)]
++pub struct C {}
++impl C {
++ pub fn unsafe_block(&self) {
++ unsafe {}
++ }
++}
++
++#[derive(Deserialize)]
++pub struct D {}
++impl D {
++ pub fn inner_unsafe_fn(&self) {
++ unsafe fn inner() {}
++ }
++}
++
++// Does not derive `Deserialize`, should be ignored
++pub struct E {}
++impl E {
++ pub unsafe fn new(_a: i32, _b: i32) -> Self {
++ Self {}
++ }
++
++ pub unsafe fn unsafe_method(&self) {}
++
++ pub fn unsafe_block(&self) {
++ unsafe {}
++ }
++
++ pub fn inner_unsafe_fn(&self) {
++ unsafe fn inner() {}
++ }
++}
++
++// Does not have methods using `unsafe`, should be ignored
++#[derive(Deserialize)]
++pub struct F {}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++ --> $DIR/unsafe_derive_deserialize.rs:8:10
++ |
++LL | #[derive(Deserialize)]
++ | ^^^^^^^^^^^
++ |
++ = note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings`
++ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++ --> $DIR/unsafe_derive_deserialize.rs:16:10
++ |
++LL | #[derive(Deserialize)]
++ | ^^^^^^^^^^^
++ |
++ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++ --> $DIR/unsafe_derive_deserialize.rs:22:10
++ |
++LL | #[derive(Deserialize)]
++ | ^^^^^^^^^^^
++ |
++ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++ --> $DIR/unsafe_derive_deserialize.rs:30:10
++ |
++LL | #[derive(Deserialize)]
++ | ^^^^^^^^^^^
++ |
++ = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused_imports)]
++#![allow(dead_code)]
++#![warn(clippy::unsafe_removed_from_name)]
++
++use std::cell::UnsafeCell as TotallySafeCell;
++
++use std::cell::UnsafeCell as TotallySafeCellAgain;
++
++// Shouldn't error
++use std::cell::RefCell as ProbablyNotUnsafe;
++use std::cell::RefCell as RefCellThatCantBeUnsafe;
++use std::cell::UnsafeCell as SuperDangerousUnsafeCell;
++use std::cell::UnsafeCell as Dangerunsafe;
++use std::cell::UnsafeCell as Bombsawayunsafe;
++
++mod mod_with_some_unsafe_things {
++ pub struct Safe {}
++ pub struct Unsafe {}
++}
++
++use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
++
++// Shouldn't error
++use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime;
++use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing;
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCell`
++ --> $DIR/unsafe_removed_from_name.rs:5:1
++ |
++LL | use std::cell::UnsafeCell as TotallySafeCell;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unsafe-removed-from-name` implied by `-D warnings`
++
++error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain`
++ --> $DIR/unsafe_removed_from_name.rs:7:1
++ |
++LL | use std::cell::UnsafeCell as TotallySafeCellAgain;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety`
++ --> $DIR/unsafe_removed_from_name.rs:21:1
++ |
++LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++// Test for proc-macro attribute
++#[derive(ClippyMiniMacroTest)]
++struct Foo;
++
++macro_rules! lit_from_macro {
++ () => {
++ 42_usize
++ };
++}
++
++fn main() {
++ let _ok1 = 1234_i32;
++ let _ok2 = 1234_isize;
++ let _ok3 = 0x123_isize;
++ let _fail1 = 1234_i32;
++ let _fail2 = 1234_u32;
++ let _fail3 = 1234_isize;
++ let _fail4 = 1234_usize;
++ let _fail5 = 0x123_isize;
++
++ let _okf1 = 1.5_f32;
++ let _okf2 = 1_f32;
++ let _failf1 = 1.5_f32;
++ let _failf2 = 1_f32;
++
++ // Test for macro
++ let _ = lit_from_macro!();
++
++ // Counter example
++ let _ = line!();
++ // Because `assert!` contains `line!()` macro.
++ assert_eq!(4897_u32, 32223);
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++// Test for proc-macro attribute
++#[derive(ClippyMiniMacroTest)]
++struct Foo;
++
++macro_rules! lit_from_macro {
++ () => {
++ 42usize
++ };
++}
++
++fn main() {
++ let _ok1 = 1234_i32;
++ let _ok2 = 1234_isize;
++ let _ok3 = 0x123_isize;
++ let _fail1 = 1234i32;
++ let _fail2 = 1234u32;
++ let _fail3 = 1234isize;
++ let _fail4 = 1234usize;
++ let _fail5 = 0x123isize;
++
++ let _okf1 = 1.5_f32;
++ let _okf2 = 1_f32;
++ let _failf1 = 1.5f32;
++ let _failf2 = 1f32;
++
++ // Test for macro
++ let _ = lit_from_macro!();
++
++ // Counter example
++ let _ = line!();
++ // Because `assert!` contains `line!()` macro.
++ assert_eq!(4897u32, 32223);
++}
--- /dev/null
--- /dev/null
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:23:18
++ |
++LL | let _fail1 = 1234i32;
++ | ^^^^^^^ help: add an underscore: `1234_i32`
++ |
++ = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:24:18
++ |
++LL | let _fail2 = 1234u32;
++ | ^^^^^^^ help: add an underscore: `1234_u32`
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:25:18
++ |
++LL | let _fail3 = 1234isize;
++ | ^^^^^^^^^ help: add an underscore: `1234_isize`
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:26:18
++ |
++LL | let _fail4 = 1234usize;
++ | ^^^^^^^^^ help: add an underscore: `1234_usize`
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:27:18
++ |
++LL | let _fail5 = 0x123isize;
++ | ^^^^^^^^^^ help: add an underscore: `0x123_isize`
++
++error: float type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:31:19
++ |
++LL | let _failf1 = 1.5f32;
++ | ^^^^^^ help: add an underscore: `1.5_f32`
++
++error: float type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:32:19
++ |
++LL | let _failf2 = 1f32;
++ | ^^^^ help: add an underscore: `1_f32`
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:15:9
++ |
++LL | 42usize
++ | ^^^^^^^ help: add an underscore: `42_usize`
++...
++LL | let _ = lit_from_macro!();
++ | ----------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: integer type suffix should be separated by an underscore
++ --> $DIR/unseparated_prefix_literals.rs:40:16
++ |
++LL | assert_eq!(4897u32, 32223);
++ | ^^^^^^^ help: add an underscore: `4897_u32`
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(dead_code)]
++#![warn(clippy::unused_io_amount)]
++
++use std::io;
++
++fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
++ s.write(b"test")?;
++ let mut buf = [0u8; 4];
++ s.read(&mut buf)?;
++ Ok(())
++}
++
++fn unwrap<T: io::Read + io::Write>(s: &mut T) {
++ s.write(b"test").unwrap();
++ let mut buf = [0u8; 4];
++ s.read(&mut buf).unwrap();
++}
++
++fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
++ s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
++ s.write_vectored(&[io::IoSlice::new(&[])])?;
++ Ok(())
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: written amount is not handled. Use `Write::write_all` instead
++ --> $DIR/unused_io_amount.rs:7:5
++ |
++LL | s.write(b"test")?;
++ | ^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::unused-io-amount` implied by `-D warnings`
++
++error: read amount is not handled. Use `Read::read_exact` instead
++ --> $DIR/unused_io_amount.rs:9:5
++ |
++LL | s.read(&mut buf)?;
++ | ^^^^^^^^^^^^^^^^^
++
++error: written amount is not handled. Use `Write::write_all` instead
++ --> $DIR/unused_io_amount.rs:14:5
++ |
++LL | s.write(b"test").unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: read amount is not handled. Use `Read::read_exact` instead
++ --> $DIR/unused_io_amount.rs:16:5
++ |
++LL | s.read(&mut buf).unwrap();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: read amount is not handled
++ --> $DIR/unused_io_amount.rs:20:5
++ |
++LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: written amount is not handled
++ --> $DIR/unused_io_amount.rs:21:5
++ |
++LL | s.write_vectored(&[io::IoSlice::new(&[])])?;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::unused_self)]
++#![allow(clippy::boxed_local, clippy::fn_params_excessive_bools)]
++
++mod unused_self {
++ use std::pin::Pin;
++ use std::sync::{Arc, Mutex};
++
++ struct A {}
++
++ impl A {
++ fn unused_self_move(self) {}
++ fn unused_self_ref(&self) {}
++ fn unused_self_mut_ref(&mut self) {}
++ fn unused_self_pin_ref(self: Pin<&Self>) {}
++ fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
++ fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
++ fn unused_self_box(self: Box<Self>) {}
++ fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
++ x + y
++ }
++ fn unused_self_class_method(&self) {
++ Self::static_method();
++ }
++
++ fn static_method() {}
++ }
++}
++
++mod unused_self_allow {
++ struct A {}
++
++ impl A {
++ // shouldn't trigger
++ #[allow(clippy::unused_self)]
++ fn unused_self_move(self) {}
++ }
++
++ struct B {}
++
++ // shouldn't trigger
++ #[allow(clippy::unused_self)]
++ impl B {
++ fn unused_self_move(self) {}
++ }
++
++ struct C {}
++
++ #[allow(clippy::unused_self)]
++ impl C {
++ #[warn(clippy::unused_self)]
++ fn some_fn((): ()) {}
++
++ // shouldn't trigger
++ fn unused_self_move(self) {}
++ }
++}
++
++mod used_self {
++ use std::pin::Pin;
++
++ struct A {
++ x: u8,
++ }
++
++ impl A {
++ fn used_self_move(self) -> u8 {
++ self.x
++ }
++ fn used_self_ref(&self) -> u8 {
++ self.x
++ }
++ fn used_self_mut_ref(&mut self) {
++ self.x += 1
++ }
++ fn used_self_pin_ref(self: Pin<&Self>) -> u8 {
++ self.x
++ }
++ fn used_self_box(self: Box<Self>) -> u8 {
++ self.x
++ }
++ fn used_self_with_other_unused_args(&self, x: u8, y: u8) -> u8 {
++ self.x
++ }
++ fn used_in_nested_closure(&self) -> u8 {
++ let mut a = || -> u8 { self.x };
++ a()
++ }
++
++ #[allow(clippy::collapsible_if)]
++ fn used_self_method_nested_conditions(&self, a: bool, b: bool, c: bool, d: bool) {
++ if a {
++ if b {
++ if c {
++ if d {
++ self.used_self_ref();
++ }
++ }
++ }
++ }
++ }
++
++ fn foo(&self) -> u32 {
++ let mut sum = 0u32;
++ for i in 0..self.x {
++ sum += i as u32;
++ }
++ sum
++ }
++
++ fn bar(&mut self, x: u8) -> u32 {
++ let mut y = 0u32;
++ for i in 0..x {
++ y += self.foo()
++ }
++ y
++ }
++ }
++}
++
++mod not_applicable {
++ use std::fmt;
++
++ struct A {}
++
++ impl fmt::Debug for A {
++ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++ write!(f, "A")
++ }
++ }
++
++ impl A {
++ fn method(x: u8, y: u8) {}
++ }
++
++ trait B {
++ fn method(&self) {}
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: unused `self` argument
++ --> $DIR/unused_self.rs:11:29
++ |
++LL | fn unused_self_move(self) {}
++ | ^^^^
++ |
++ = note: `-D clippy::unused-self` implied by `-D warnings`
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:12:28
++ |
++LL | fn unused_self_ref(&self) {}
++ | ^^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:13:32
++ |
++LL | fn unused_self_mut_ref(&mut self) {}
++ | ^^^^^^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:14:32
++ |
++LL | fn unused_self_pin_ref(self: Pin<&Self>) {}
++ | ^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:15:36
++ |
++LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
++ | ^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:16:35
++ |
++LL | fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
++ | ^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:17:28
++ |
++LL | fn unused_self_box(self: Box<Self>) {}
++ | ^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:18:40
++ |
++LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
++ | ^^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: unused `self` argument
++ --> $DIR/unused_self.rs:21:37
++ |
++LL | fn unused_self_class_method(&self) {
++ | ^^^^^
++ |
++ = help: consider refactoring to a associated function
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++// The output for humans should just highlight the whole span without showing
++// the suggested replacement, but we also want to test that suggested
++// replacement only removes one set of parentheses, rather than naïvely
++// stripping away any starting or ending parenthesis characters—hence this
++// test of the JSON error format.
++
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++#![deny(clippy::unused_unit)]
++#![allow(dead_code)]
++
++struct Unitter;
++impl Unitter {
++ // try to disorient the lint with multiple unit returns and newlines
++ #[allow(clippy::no_effect)]
++ pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G)
++ where G: Fn() -> () {
++ let _y: &dyn Fn() -> () = &f;
++ (); // this should not lint, as it's not in return type position
++ }
++}
++
++impl Into<()> for Unitter {
++ #[rustfmt::skip]
++ fn into(self) {
++
++ }
++}
++
++fn return_unit() { }
++
++#[allow(clippy::needless_return)]
++#[allow(clippy::never_loop)]
++#[allow(clippy::unit_cmp)]
++fn main() {
++ let u = Unitter;
++ assert_eq!(u.get_unit(|| {}, return_unit), u.into());
++ return_unit();
++ loop {
++ break;
++ }
++ return;
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/4076
++fn foo() {
++ macro_rules! foo {
++ (recv($r:expr) -> $res:pat => $body:expr) => {
++ $body
++ }
++ }
++
++ foo! {
++ recv(rx) -> _x => ()
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++// The output for humans should just highlight the whole span without showing
++// the suggested replacement, but we also want to test that suggested
++// replacement only removes one set of parentheses, rather than naïvely
++// stripping away any starting or ending parenthesis characters—hence this
++// test of the JSON error format.
++
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++#![deny(clippy::unused_unit)]
++#![allow(dead_code)]
++
++struct Unitter;
++impl Unitter {
++ // try to disorient the lint with multiple unit returns and newlines
++ #[allow(clippy::no_effect)]
++ pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) ->
++ ()
++ where G: Fn() -> () {
++ let _y: &dyn Fn() -> () = &f;
++ (); // this should not lint, as it's not in return type position
++ }
++}
++
++impl Into<()> for Unitter {
++ #[rustfmt::skip]
++ fn into(self) -> () {
++ ()
++ }
++}
++
++fn return_unit() -> () { () }
++
++#[allow(clippy::needless_return)]
++#[allow(clippy::never_loop)]
++#[allow(clippy::unit_cmp)]
++fn main() {
++ let u = Unitter;
++ assert_eq!(u.get_unit(|| {}, return_unit), u.into());
++ return_unit();
++ loop {
++ break();
++ }
++ return();
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/4076
++fn foo() {
++ macro_rules! foo {
++ (recv($r:expr) -> $res:pat => $body:expr) => {
++ $body
++ }
++ }
++
++ foo! {
++ recv(rx) -> _x => ()
++ }
++}
--- /dev/null
--- /dev/null
++error: unneeded unit return type
++ --> $DIR/unused_unit.rs:19:59
++ |
++LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) ->
++ | ___________________________________________________________^
++LL | | ()
++ | |__________^ help: remove the `-> ()`
++ |
++note: the lint level is defined here
++ --> $DIR/unused_unit.rs:12:9
++ |
++LL | #![deny(clippy::unused_unit)]
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: unneeded unit return type
++ --> $DIR/unused_unit.rs:29:19
++ |
++LL | fn into(self) -> () {
++ | ^^^^^ help: remove the `-> ()`
++
++error: unneeded unit expression
++ --> $DIR/unused_unit.rs:30:9
++ |
++LL | ()
++ | ^^ help: remove the final `()`
++
++error: unneeded unit return type
++ --> $DIR/unused_unit.rs:34:18
++ |
++LL | fn return_unit() -> () { () }
++ | ^^^^^ help: remove the `-> ()`
++
++error: unneeded unit expression
++ --> $DIR/unused_unit.rs:34:26
++ |
++LL | fn return_unit() -> () { () }
++ | ^^ help: remove the final `()`
++
++error: unneeded `()`
++ --> $DIR/unused_unit.rs:44:14
++ |
++LL | break();
++ | ^^ help: remove the `()`
++
++error: unneeded `()`
++ --> $DIR/unused_unit.rs:46:11
++ |
++LL | return();
++ | ^^ help: remove the `()`
++
++error: aborting due to 7 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)]
++
++fn unwrap_option() {
++ let opt = Some(0);
++ let _ = opt.unwrap();
++}
++
++fn unwrap_result() {
++ let res: Result<u8, ()> = Ok(0);
++ let _ = res.unwrap();
++}
++
++fn main() {
++ unwrap_option();
++ unwrap_result();
++}
--- /dev/null
--- /dev/null
++error: used `unwrap()` on `an Option` value
++ --> $DIR/unwrap.rs:5:13
++ |
++LL | let _ = opt.unwrap();
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::option-unwrap-used` implied by `-D warnings`
++ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
++
++error: used `unwrap()` on `a Result` value
++ --> $DIR/unwrap.rs:10:13
++ |
++LL | let _ = res.unwrap();
++ | ^^^^^^^^^^^^
++ |
++ = note: `-D clippy::result-unwrap-used` implied by `-D warnings`
++ = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::all)]
++
++fn main() {
++ let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++}
++
++fn new_lines() {
++ let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++}
--- /dev/null
--- /dev/null
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/unwrap_or.rs:4:47
++ |
++LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
++ |
++ = note: `-D clippy::or-fun-call` implied by `-D warnings`
++
++error: use of `unwrap_or` followed by a function call
++ --> $DIR/unwrap_or.rs:8:47
++ |
++LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++# A script to update the references for all tests. The idea is that
++# you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. You then
++# run this script, which will copy those files over. If you find
++# yourself manually editing a foo.stderr file, you're doing it wrong.
++#
++# See all `update-references.sh`, if you just want to update a single test.
++
++if [[ "$1" == "--help" || "$1" == "-h" ]]; then
++ echo "usage: $0"
++fi
++
++CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target}
++PROFILE=${PROFILE:-debug}
++BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base
++
++MY_DIR=$(dirname "$0")
++cd "$MY_DIR" || exit
++find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++# A script to update the references for particular tests. The idea is
++# that you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. This
++# script will then copy that output and replace the "expected output"
++# files. You can then commit the changes.
++#
++# If you find yourself manually editing a `foo.stderr` file, you're
++# doing it wrong.
++
++if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
++ echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
++ echo ""
++ echo "For example:"
++ echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
++fi
++
++MYDIR=$(dirname "$0")
++
++BUILD_DIR="$1"
++shift
++
++while [[ "$1" != "" ]]; do
++ STDERR_NAME="${1/%.rs/.stderr}"
++ STDOUT_NAME="${1/%.rs/.stdout}"
++ FIXED_NAME="${1/%.rs/.fixed}"
++ shift
++ if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
++ ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
++ echo updating "$MYDIR"/"$STDOUT_NAME"
++ cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
++ fi
++ if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
++ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
++ echo updating "$MYDIR"/"$STDERR_NAME"
++ cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
++ fi
++ if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \
++ ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then
++ echo updating "$MYDIR"/"$FIXED_NAME"
++ cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"
++ fi
++done
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait)]
++
++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 into_bytes(&self) -> Vec<u8>;
++ }
++
++ // This should not be linted
++ impl IntoBytes for u8 {
++ fn into_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() -> Self {
++ Self {}
++ }
++ };
++ }
++
++ struct Foo {}
++
++ impl Foo {
++ use_self_expand!(); // Should lint in local macros
++ }
++}
++
++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);
++ }
++
++ impl Trait<Vec<A>> for Vec<B> {
++ fn a(_: Vec<A>) {}
++ }
++}
++
++#[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]
++ }
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// edition:2018
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait)]
++
++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 into_bytes(&self) -> Vec<u8>;
++ }
++
++ // This should not be linted
++ impl IntoBytes for u8 {
++ fn into_bytes(&self) -> Vec<u8> {
++ vec![*self]
++ }
++ }
++}
++
++mod existential {
++ struct Foo;
++
++ impl Foo {
++ fn bad(foos: &[Self]) -> 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 lint in local macros
++ }
++}
++
++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);
++ }
++
++ impl Trait<Vec<A>> for Vec<B> {
++ fn a(_: Vec<A>) {}
++ }
++}
++
++#[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]
++ }
++ }
++}
--- /dev/null
--- /dev/null
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:14: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:15:13
++ |
++LL | Foo {}
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:17:22
++ |
++LL | fn test() -> Foo {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:18:13
++ |
++LL | Foo::new()
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:23:25
++ |
++LL | fn default() -> Foo {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:24:13
++ |
++LL | Foo::new()
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:89:56
++ |
++LL | fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:104:13
++ |
++LL | TS(0)
++ | ^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:112:25
++ |
++LL | fn new() -> Foo {
++ | ^^^ help: use the applicable keyword: `Self`
++...
++LL | use_self_expand!(); // Should lint in local macros
++ | ------------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:113:17
++ |
++LL | Foo {}
++ | ^^^ help: use the applicable keyword: `Self`
++...
++LL | use_self_expand!(); // Should lint in local macros
++ | ------------------- in this macro invocation
++ |
++ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:148:21
++ |
++LL | fn baz() -> Foo {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:149:13
++ |
++LL | Foo {}
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:136:29
++ |
++LL | fn bar() -> Bar {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:137:21
++ |
++LL | Bar { foo: Foo {} }
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:166:21
++ |
++LL | let _ = Enum::B(42);
++ | ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:167:21
++ |
++LL | let _ = Enum::C { field: true };
++ | ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:168:21
++ |
++LL | let _ = Enum::A;
++ | ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:199:13
++ |
++LL | nested::A::fun_1();
++ | ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:200:13
++ |
++LL | nested::A::A;
++ | ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:202:13
++ |
++LL | nested::A {};
++ | ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:221:13
++ |
++LL | TestStruct::from_something()
++ | ^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:235:25
++ |
++LL | async fn g() -> S {
++ | ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:236:13
++ |
++LL | S {}
++ | ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:240:16
++ |
++LL | &p[S::A..S::B]
++ | ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self.rs:240:22
++ |
++LL | &p[S::A..S::B]
++ | ^ help: use the applicable keyword: `Self`
++
++error: aborting due to 25 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait, clippy::boxed_local)]
++
++use std::ops::Mul;
++
++trait SelfTrait {
++ fn refs(p1: &Self) -> &Self;
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
++ fn mut_refs(p1: &mut Self) -> &mut Self;
++ fn nested(p1: Box<Self>, p2: (&u8, &Self));
++ fn vals(r: Self) -> Self;
++}
++
++#[derive(Default)]
++struct Bad;
++
++impl SelfTrait for Bad {
++ fn refs(p1: &Self) -> &Self {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Self) -> &mut Self {
++ p1
++ }
++
++ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++ fn vals(_: Self) -> Self {
++ Self::default()
++ }
++}
++
++impl Mul for Bad {
++ type Output = Self;
++
++ fn mul(self, rhs: Self) -> Self {
++ rhs
++ }
++}
++
++impl Clone for Bad {
++ fn clone(&self) -> Self {
++ Self
++ }
++}
++
++#[derive(Default)]
++struct Good;
++
++impl SelfTrait for Good {
++ fn refs(p1: &Self) -> &Self {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Self) -> &mut Self {
++ p1
++ }
++
++ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++ fn vals(_: Self) -> Self {
++ Self::default()
++ }
++}
++
++impl Mul for Good {
++ type Output = Self;
++
++ fn mul(self, rhs: Self) -> Self {
++ rhs
++ }
++}
++
++trait NameTrait {
++ fn refs(p1: &u8) -> &u8;
++ fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
++ fn mut_refs(p1: &mut u8) -> &mut u8;
++ fn nested(p1: Box<u8>, p2: (&u8, &u8));
++ fn vals(p1: u8) -> u8;
++}
++
++// Using `Self` instead of the type name is OK
++impl NameTrait for u8 {
++ fn refs(p1: &Self) -> &Self {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Self) -> &mut Self {
++ p1
++ }
++
++ fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
++
++ fn vals(_: Self) -> Self {
++ Self::default()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait, clippy::boxed_local)]
++
++use std::ops::Mul;
++
++trait SelfTrait {
++ fn refs(p1: &Self) -> &Self;
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
++ fn mut_refs(p1: &mut Self) -> &mut Self;
++ fn nested(p1: Box<Self>, p2: (&u8, &Self));
++ fn vals(r: Self) -> Self;
++}
++
++#[derive(Default)]
++struct Bad;
++
++impl SelfTrait for Bad {
++ fn refs(p1: &Bad) -> &Bad {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Bad) -> &mut Bad {
++ p1
++ }
++
++ fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++
++ fn vals(_: Bad) -> Bad {
++ Bad::default()
++ }
++}
++
++impl Mul for Bad {
++ type Output = Bad;
++
++ fn mul(self, rhs: Bad) -> Bad {
++ rhs
++ }
++}
++
++impl Clone for Bad {
++ fn clone(&self) -> Self {
++ Bad
++ }
++}
++
++#[derive(Default)]
++struct Good;
++
++impl SelfTrait for Good {
++ fn refs(p1: &Self) -> &Self {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Self) -> &mut Self {
++ p1
++ }
++
++ fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++ fn vals(_: Self) -> Self {
++ Self::default()
++ }
++}
++
++impl Mul for Good {
++ type Output = Self;
++
++ fn mul(self, rhs: Self) -> Self {
++ rhs
++ }
++}
++
++trait NameTrait {
++ fn refs(p1: &u8) -> &u8;
++ fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
++ fn mut_refs(p1: &mut u8) -> &mut u8;
++ fn nested(p1: Box<u8>, p2: (&u8, &u8));
++ fn vals(p1: u8) -> u8;
++}
++
++// Using `Self` instead of the type name is OK
++impl NameTrait for u8 {
++ fn refs(p1: &Self) -> &Self {
++ p1
++ }
++
++ fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++ p1
++ }
++
++ fn mut_refs(p1: &mut Self) -> &mut Self {
++ p1
++ }
++
++ fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
++
++ fn vals(_: Self) -> Self {
++ Self::default()
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:21:18
++ |
++LL | fn refs(p1: &Bad) -> &Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++ |
++ = note: `-D clippy::use-self` implied by `-D warnings`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:21:27
++ |
++LL | fn refs(p1: &Bad) -> &Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:25:33
++ |
++LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:25:49
++ |
++LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:29:26
++ |
++LL | fn mut_refs(p1: &mut Bad) -> &mut Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:29:39
++ |
++LL | fn mut_refs(p1: &mut Bad) -> &mut Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:33:24
++ |
++LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:33:42
++ |
++LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:35:16
++ |
++LL | fn vals(_: Bad) -> Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:35:24
++ |
++LL | fn vals(_: Bad) -> Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:36:9
++ |
++LL | Bad::default()
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:41:19
++ |
++LL | type Output = Bad;
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:43:23
++ |
++LL | fn mul(self, rhs: Bad) -> Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:43:31
++ |
++LL | fn mul(self, rhs: Bad) -> Bad {
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++ --> $DIR/use_self_trait.rs:50:9
++ |
++LL | Bad
++ | ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 15 previous errors
++
--- /dev/null
--- /dev/null
++// edition:2018
++// aux-build:proc_macro_derive.rs
++
++#![feature(rustc_private)]
++#![warn(clippy::all)]
++#![allow(clippy::blacklisted_name)]
++#![warn(clippy::used_underscore_binding)]
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// This should not trigger the lint. There's underscore binding inside the external derive that
++// would trigger the `used_underscore_binding` lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++macro_rules! test_macro {
++ () => {{
++ let _foo = 42;
++ _foo + 1
++ }};
++}
++
++/// Tests that we lint if we use a binding with a single leading underscore
++fn prefix_underscore(_foo: u32) -> u32 {
++ _foo + 1
++}
++
++/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion
++fn in_macro_or_desugar(_foo: u32) {
++ println!("{}", _foo);
++ assert_eq!(_foo, _foo);
++
++ test_macro!() + 1;
++}
++
++// Struct for testing use of fields prefixed with an underscore
++struct StructFieldTest {
++ _underscore_field: u32,
++}
++
++/// Tests that we lint the use of a struct field which is prefixed with an underscore
++fn in_struct_field() {
++ let mut s = StructFieldTest { _underscore_field: 0 };
++ s._underscore_field += 1;
++}
++
++/// Tests that we do not lint if the underscore is not a prefix
++fn non_prefix_underscore(some_foo: u32) -> u32 {
++ some_foo + 1
++}
++
++/// Tests that we do not lint if we do not use the binding (simple case)
++fn unused_underscore_simple(_foo: u32) -> u32 {
++ 1
++}
++
++/// Tests that we do not lint if we do not use the binding (complex case). This checks for
++/// compatibility with the built-in `unused_variables` lint.
++fn unused_underscore_complex(mut _foo: u32) -> u32 {
++ _foo += 1;
++ _foo = 2;
++ 1
++}
++
++/// Test that we do not lint for multiple underscores
++fn multiple_underscores(__foo: u32) -> u32 {
++ __foo + 1
++}
++
++// Non-variable bindings with preceding underscore
++fn _fn_test() {}
++struct _StructTest;
++enum _EnumTest {
++ _Empty,
++ _Value(_StructTest),
++}
++
++/// Tests that we do not lint for non-variable bindings
++fn non_variables() {
++ _fn_test();
++ let _s = _StructTest;
++ let _e = match _EnumTest::_Value(_StructTest) {
++ _EnumTest::_Empty => 0,
++ _EnumTest::_Value(_st) => 1,
++ };
++ let f = _fn_test;
++ f();
++}
++
++// Tests that we do not lint if the binding comes from await desugaring,
++// but we do lint the awaited expression. See issue 5360.
++async fn await_desugaring() {
++ async fn foo() {}
++ fn uses_i(_i: i32) {}
++
++ foo().await;
++ ({
++ let _i = 5;
++ uses_i(_i);
++ foo()
++ })
++ .await
++}
++
++fn main() {
++ let foo = 0u32;
++ // tests of unused_underscore lint
++ let _ = prefix_underscore(foo);
++ in_macro_or_desugar(foo);
++ in_struct_field();
++ // possible false positives
++ let _ = non_prefix_underscore(foo);
++ let _ = unused_underscore_simple(foo);
++ let _ = unused_underscore_complex(foo);
++ let _ = multiple_underscores(foo);
++ non_variables();
++ await_desugaring();
++}
--- /dev/null
--- /dev/null
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:26:5
++ |
++LL | _foo + 1
++ | ^^^^
++ |
++ = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:31:20
++ |
++LL | println!("{}", _foo);
++ | ^^^^
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:32:16
++ |
++LL | assert_eq!(_foo, _foo);
++ | ^^^^
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:32:22
++ |
++LL | assert_eq!(_foo, _foo);
++ | ^^^^
++
++error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:45:5
++ |
++LL | s._underscore_field += 1;
++ | ^^^^^^^^^^^^^^^^^^^
++
++error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++ --> $DIR/used_underscore_binding.rs:100:16
++ |
++LL | uses_i(_i);
++ | ^^
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++#![deny(clippy::useless_asref)]
++
++trait Trait {
++ fn as_ptr(&self);
++}
++
++impl<'a> Trait for &'a [u8] {
++ fn as_ptr(&self) {
++ self.as_ref().as_ptr();
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::useless_asref)]
++
++use std::fmt::Debug;
++
++struct FakeAsRef;
++
++#[allow(clippy::should_implement_trait)]
++impl FakeAsRef {
++ fn as_ref(&self) -> &Self {
++ self
++ }
++}
++
++struct MoreRef;
++
++impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
++ fn as_ref(&self) -> &&'a &'b &'c MoreRef {
++ &&&&MoreRef
++ }
++}
++
++fn foo_rstr(x: &str) {
++ println!("{:?}", x);
++}
++fn foo_rslice(x: &[i32]) {
++ println!("{:?}", x);
++}
++fn foo_mrslice(x: &mut [i32]) {
++ println!("{:?}", x);
++}
++fn foo_rrrrmr(_: &&&&MoreRef) {
++ println!("so many refs");
++}
++
++fn not_ok() {
++ let rstr: &str = "hello";
++ let mut mrslice: &mut [i32] = &mut [1, 2, 3];
++
++ {
++ let rslice: &[i32] = &*mrslice;
++ foo_rstr(rstr);
++ foo_rstr(rstr);
++ foo_rslice(rslice);
++ foo_rslice(rslice);
++ }
++ {
++ foo_mrslice(mrslice);
++ foo_mrslice(mrslice);
++ foo_rslice(mrslice);
++ foo_rslice(mrslice);
++ }
++
++ {
++ let rrrrrstr = &&&&rstr;
++ let rrrrrslice = &&&&&*mrslice;
++ foo_rslice(rrrrrslice);
++ foo_rslice(rrrrrslice);
++ foo_rstr(rrrrrstr);
++ foo_rstr(rrrrrstr);
++ }
++ {
++ let mrrrrrslice = &mut &mut &mut &mut mrslice;
++ foo_mrslice(mrrrrrslice);
++ foo_mrslice(mrrrrrslice);
++ foo_rslice(mrrrrrslice);
++ foo_rslice(mrrrrrslice);
++ }
++ #[allow(unused_parens, clippy::double_parens)]
++ foo_rrrrmr((&&&&MoreRef));
++
++ generic_not_ok(mrslice);
++ generic_ok(mrslice);
++}
++
++fn ok() {
++ let string = "hello".to_owned();
++ let mut arr = [1, 2, 3];
++ let mut vec = vec![1, 2, 3];
++
++ {
++ foo_rstr(string.as_ref());
++ foo_rslice(arr.as_ref());
++ foo_rslice(vec.as_ref());
++ }
++ {
++ foo_mrslice(arr.as_mut());
++ foo_mrslice(vec.as_mut());
++ }
++
++ {
++ let rrrrstring = &&&&string;
++ let rrrrarr = &&&&arr;
++ let rrrrvec = &&&&vec;
++ foo_rstr(rrrrstring.as_ref());
++ foo_rslice(rrrrarr.as_ref());
++ foo_rslice(rrrrvec.as_ref());
++ }
++ {
++ let mrrrrarr = &mut &mut &mut &mut arr;
++ let mrrrrvec = &mut &mut &mut &mut vec;
++ foo_mrslice(mrrrrarr.as_mut());
++ foo_mrslice(mrrrrvec.as_mut());
++ }
++ FakeAsRef.as_ref();
++ foo_rrrrmr(MoreRef.as_ref());
++
++ generic_not_ok(arr.as_mut());
++ generic_ok(&mut arr);
++}
++
++fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
++ println!("{:?}", t);
++}
++fn foo_rt<T: Debug + ?Sized>(t: &T) {
++ println!("{:?}", t);
++}
++
++fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
++ foo_mrt(mrt);
++ foo_mrt(mrt);
++ foo_rt(mrt);
++ foo_rt(mrt);
++}
++
++fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
++ foo_mrt(mru.as_mut());
++ foo_rt(mru.as_ref());
++}
++
++fn main() {
++ not_ok();
++ ok();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::useless_asref)]
++
++use std::fmt::Debug;
++
++struct FakeAsRef;
++
++#[allow(clippy::should_implement_trait)]
++impl FakeAsRef {
++ fn as_ref(&self) -> &Self {
++ self
++ }
++}
++
++struct MoreRef;
++
++impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
++ fn as_ref(&self) -> &&'a &'b &'c MoreRef {
++ &&&&MoreRef
++ }
++}
++
++fn foo_rstr(x: &str) {
++ println!("{:?}", x);
++}
++fn foo_rslice(x: &[i32]) {
++ println!("{:?}", x);
++}
++fn foo_mrslice(x: &mut [i32]) {
++ println!("{:?}", x);
++}
++fn foo_rrrrmr(_: &&&&MoreRef) {
++ println!("so many refs");
++}
++
++fn not_ok() {
++ let rstr: &str = "hello";
++ let mut mrslice: &mut [i32] = &mut [1, 2, 3];
++
++ {
++ let rslice: &[i32] = &*mrslice;
++ foo_rstr(rstr.as_ref());
++ foo_rstr(rstr);
++ foo_rslice(rslice.as_ref());
++ foo_rslice(rslice);
++ }
++ {
++ foo_mrslice(mrslice.as_mut());
++ foo_mrslice(mrslice);
++ foo_rslice(mrslice.as_ref());
++ foo_rslice(mrslice);
++ }
++
++ {
++ let rrrrrstr = &&&&rstr;
++ let rrrrrslice = &&&&&*mrslice;
++ foo_rslice(rrrrrslice.as_ref());
++ foo_rslice(rrrrrslice);
++ foo_rstr(rrrrrstr.as_ref());
++ foo_rstr(rrrrrstr);
++ }
++ {
++ let mrrrrrslice = &mut &mut &mut &mut mrslice;
++ foo_mrslice(mrrrrrslice.as_mut());
++ foo_mrslice(mrrrrrslice);
++ foo_rslice(mrrrrrslice.as_ref());
++ foo_rslice(mrrrrrslice);
++ }
++ #[allow(unused_parens, clippy::double_parens)]
++ foo_rrrrmr((&&&&MoreRef).as_ref());
++
++ generic_not_ok(mrslice);
++ generic_ok(mrslice);
++}
++
++fn ok() {
++ let string = "hello".to_owned();
++ let mut arr = [1, 2, 3];
++ let mut vec = vec![1, 2, 3];
++
++ {
++ foo_rstr(string.as_ref());
++ foo_rslice(arr.as_ref());
++ foo_rslice(vec.as_ref());
++ }
++ {
++ foo_mrslice(arr.as_mut());
++ foo_mrslice(vec.as_mut());
++ }
++
++ {
++ let rrrrstring = &&&&string;
++ let rrrrarr = &&&&arr;
++ let rrrrvec = &&&&vec;
++ foo_rstr(rrrrstring.as_ref());
++ foo_rslice(rrrrarr.as_ref());
++ foo_rslice(rrrrvec.as_ref());
++ }
++ {
++ let mrrrrarr = &mut &mut &mut &mut arr;
++ let mrrrrvec = &mut &mut &mut &mut vec;
++ foo_mrslice(mrrrrarr.as_mut());
++ foo_mrslice(mrrrrvec.as_mut());
++ }
++ FakeAsRef.as_ref();
++ foo_rrrrmr(MoreRef.as_ref());
++
++ generic_not_ok(arr.as_mut());
++ generic_ok(&mut arr);
++}
++
++fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
++ println!("{:?}", t);
++}
++fn foo_rt<T: Debug + ?Sized>(t: &T) {
++ println!("{:?}", t);
++}
++
++fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
++ foo_mrt(mrt.as_mut());
++ foo_mrt(mrt);
++ foo_rt(mrt.as_ref());
++ foo_rt(mrt);
++}
++
++fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
++ foo_mrt(mru.as_mut());
++ foo_rt(mru.as_ref());
++}
++
++fn main() {
++ not_ok();
++ ok();
++}
--- /dev/null
--- /dev/null
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:43:18
++ |
++LL | foo_rstr(rstr.as_ref());
++ | ^^^^^^^^^^^^^ help: try this: `rstr`
++ |
++note: the lint level is defined here
++ --> $DIR/useless_asref.rs:3:9
++ |
++LL | #![deny(clippy::useless_asref)]
++ | ^^^^^^^^^^^^^^^^^^^^^
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:45:20
++ |
++LL | foo_rslice(rslice.as_ref());
++ | ^^^^^^^^^^^^^^^ help: try this: `rslice`
++
++error: this call to `as_mut` does nothing
++ --> $DIR/useless_asref.rs:49:21
++ |
++LL | foo_mrslice(mrslice.as_mut());
++ | ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:51:20
++ |
++LL | foo_rslice(mrslice.as_ref());
++ | ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:58:20
++ |
++LL | foo_rslice(rrrrrslice.as_ref());
++ | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:60:18
++ |
++LL | foo_rstr(rrrrrstr.as_ref());
++ | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
++
++error: this call to `as_mut` does nothing
++ --> $DIR/useless_asref.rs:65:21
++ |
++LL | foo_mrslice(mrrrrrslice.as_mut());
++ | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:67:20
++ |
++LL | foo_rslice(mrrrrrslice.as_ref());
++ | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:71:16
++ |
++LL | foo_rrrrmr((&&&&MoreRef).as_ref());
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
++
++error: this call to `as_mut` does nothing
++ --> $DIR/useless_asref.rs:121:13
++ |
++LL | foo_mrt(mrt.as_mut());
++ | ^^^^^^^^^^^^ help: try this: `mrt`
++
++error: this call to `as_ref` does nothing
++ --> $DIR/useless_asref.rs:123:12
++ |
++LL | foo_rt(mrt.as_ref());
++ | ^^^^^^^^^^^^ help: try this: `mrt`
++
++error: aborting due to 11 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:proc_macro_derive.rs
++
++#![warn(clippy::useless_attribute)]
++#![warn(unreachable_pub)]
++#![feature(rustc_private)]
++
++#![allow(dead_code)]
++#![cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++#[rustfmt::skip]
++#[allow(unused_imports)]
++#[allow(unused_extern_crates)]
++#[macro_use]
++extern crate rustc_middle;
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// don't lint on unused_import for `use` items
++#[allow(unused_imports)]
++use std::collections;
++
++// don't lint on unused for `use` items
++#[allow(unused)]
++use std::option;
++
++// don't lint on deprecated for `use` items
++mod foo {
++ #[deprecated]
++ pub struct Bar;
++}
++#[allow(deprecated)]
++pub use foo::Bar;
++
++// This should not trigger the lint. There's lint level definitions inside the external derive
++// that would trigger the useless_attribute lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++// don't lint on unreachable_pub for `use` items
++mod a {
++ mod b {
++ #[allow(dead_code)]
++ #[allow(unreachable_pub)]
++ pub struct C {}
++ }
++
++ #[allow(unreachable_pub)]
++ pub use self::b::C;
++}
++
++fn test_indented_attr() {
++ #![allow(clippy::almost_swapped)]
++ use std::collections::HashSet;
++
++ let _ = HashSet::<u32>::default();
++}
++
++fn main() {
++ test_indented_attr();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:proc_macro_derive.rs
++
++#![warn(clippy::useless_attribute)]
++#![warn(unreachable_pub)]
++#![feature(rustc_private)]
++
++#[allow(dead_code)]
++#[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++#[rustfmt::skip]
++#[allow(unused_imports)]
++#[allow(unused_extern_crates)]
++#[macro_use]
++extern crate rustc_middle;
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// don't lint on unused_import for `use` items
++#[allow(unused_imports)]
++use std::collections;
++
++// don't lint on unused for `use` items
++#[allow(unused)]
++use std::option;
++
++// don't lint on deprecated for `use` items
++mod foo {
++ #[deprecated]
++ pub struct Bar;
++}
++#[allow(deprecated)]
++pub use foo::Bar;
++
++// This should not trigger the lint. There's lint level definitions inside the external derive
++// that would trigger the useless_attribute lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++// don't lint on unreachable_pub for `use` items
++mod a {
++ mod b {
++ #[allow(dead_code)]
++ #[allow(unreachable_pub)]
++ pub struct C {}
++ }
++
++ #[allow(unreachable_pub)]
++ pub use self::b::C;
++}
++
++fn test_indented_attr() {
++ #[allow(clippy::almost_swapped)]
++ use std::collections::HashSet;
++
++ let _ = HashSet::<u32>::default();
++}
++
++fn main() {
++ test_indented_attr();
++}
--- /dev/null
--- /dev/null
++error: useless lint attribute
++ --> $DIR/useless_attribute.rs:8:1
++ |
++LL | #[allow(dead_code)]
++ | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]`
++ |
++ = note: `-D clippy::useless-attribute` implied by `-D warnings`
++
++error: useless lint attribute
++ --> $DIR/useless_attribute.rs:9:1
++ |
++LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
++
++error: useless lint attribute
++ --> $DIR/useless_attribute.rs:53:5
++ |
++LL | #[allow(clippy::almost_swapped)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::useless_vec)]
++
++#[derive(Debug)]
++struct NonCopy;
++
++fn on_slice(_: &[u8]) {}
++#[allow(clippy::ptr_arg)]
++fn on_vec(_: &Vec<u8>) {}
++
++struct Line {
++ length: usize,
++}
++
++impl Line {
++ fn length(&self) -> usize {
++ self.length
++ }
++}
++
++fn main() {
++ on_slice(&[]);
++ on_slice(&[]);
++
++ on_slice(&[1, 2]);
++ on_slice(&[1, 2]);
++
++ on_slice(&[1, 2]);
++ on_slice(&[1, 2]);
++ #[rustfmt::skip]
++ on_slice(&[1, 2]);
++ on_slice(&[1, 2]);
++
++ on_slice(&[1; 2]);
++ on_slice(&[1; 2]);
++
++ on_vec(&vec![]);
++ on_vec(&vec![1, 2]);
++ on_vec(&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()]);
++
++ for a in &[1, 2, 3] {
++ println!("{:?}", a);
++ }
++
++ for a in vec![NonCopy, NonCopy] {
++ println!("{:?}", a);
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::useless_vec)]
++
++#[derive(Debug)]
++struct NonCopy;
++
++fn on_slice(_: &[u8]) {}
++#[allow(clippy::ptr_arg)]
++fn on_vec(_: &Vec<u8>) {}
++
++struct Line {
++ length: usize,
++}
++
++impl Line {
++ fn length(&self) -> usize {
++ self.length
++ }
++}
++
++fn main() {
++ on_slice(&vec![]);
++ on_slice(&[]);
++
++ on_slice(&vec![1, 2]);
++ on_slice(&[1, 2]);
++
++ on_slice(&vec![1, 2]);
++ on_slice(&[1, 2]);
++ #[rustfmt::skip]
++ on_slice(&vec!(1, 2));
++ on_slice(&[1, 2]);
++
++ on_slice(&vec![1; 2]);
++ on_slice(&[1; 2]);
++
++ on_vec(&vec![]);
++ on_vec(&vec![1, 2]);
++ on_vec(&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()]);
++
++ for a in vec![1, 2, 3] {
++ println!("{:?}", a);
++ }
++
++ for a in vec![NonCopy, NonCopy] {
++ println!("{:?}", a);
++ }
++}
--- /dev/null
--- /dev/null
++error: useless use of `vec!`
++ --> $DIR/vec.rs:23:14
++ |
++LL | on_slice(&vec![]);
++ | ^^^^^^^ help: you can use a slice directly: `&[]`
++ |
++ = note: `-D clippy::useless-vec` implied by `-D warnings`
++
++error: useless use of `vec!`
++ --> $DIR/vec.rs:26:14
++ |
++LL | on_slice(&vec![1, 2]);
++ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++ --> $DIR/vec.rs:29:14
++ |
++LL | on_slice(&vec![1, 2]);
++ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++ --> $DIR/vec.rs:32:14
++ |
++LL | on_slice(&vec!(1, 2));
++ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++ --> $DIR/vec.rs:35:14
++ |
++LL | on_slice(&vec![1; 2]);
++ | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]`
++
++error: useless use of `vec!`
++ --> $DIR/vec.rs:48:14
++ |
++LL | for a in vec![1, 2, 3] {
++ | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
++
++error: aborting due to 6 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code)]
++
++struct SizedStruct(i32);
++struct UnsizedStruct([i32]);
++struct BigStruct([i32; 10000]);
++
++/// The following should trigger the lint
++mod should_trigger {
++ use super::SizedStruct;
++
++ struct StructWithVecBox {
++ sized_type: Vec<SizedStruct>,
++ }
++
++ struct A(Vec<SizedStruct>);
++ struct B(Vec<Vec<u32>>);
++}
++
++/// The following should not trigger the lint
++mod should_not_trigger {
++ use super::{BigStruct, UnsizedStruct};
++
++ struct C(Vec<Box<UnsizedStruct>>);
++ struct D(Vec<Box<BigStruct>>);
++
++ struct StructWithVecBoxButItsUnsized {
++ unsized_type: Vec<Box<UnsizedStruct>>,
++ }
++
++ struct TraitVec<T: ?Sized> {
++ // Regression test for #3720. This was causing an ICE.
++ inner: Vec<Box<T>>,
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(dead_code)]
++
++struct SizedStruct(i32);
++struct UnsizedStruct([i32]);
++struct BigStruct([i32; 10000]);
++
++/// The following should trigger the lint
++mod should_trigger {
++ use super::SizedStruct;
++
++ struct StructWithVecBox {
++ sized_type: Vec<Box<SizedStruct>>,
++ }
++
++ struct A(Vec<Box<SizedStruct>>);
++ struct B(Vec<Vec<Box<(u32)>>>);
++}
++
++/// The following should not trigger the lint
++mod should_not_trigger {
++ use super::{BigStruct, UnsizedStruct};
++
++ struct C(Vec<Box<UnsizedStruct>>);
++ struct D(Vec<Box<BigStruct>>);
++
++ struct StructWithVecBoxButItsUnsized {
++ unsized_type: Vec<Box<UnsizedStruct>>,
++ }
++
++ struct TraitVec<T: ?Sized> {
++ // Regression test for #3720. This was causing an ICE.
++ inner: Vec<Box<T>>,
++ }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/vec_box_sized.rs:14:21
++ |
++LL | sized_type: Vec<Box<SizedStruct>>,
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
++ |
++ = note: `-D clippy::vec-box` implied by `-D warnings`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/vec_box_sized.rs:17:14
++ |
++LL | struct A(Vec<Box<SizedStruct>>);
++ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++ --> $DIR/vec_box_sized.rs:18:18
++ |
++LL | struct B(Vec<Vec<Box<(u32)>>>);
++ | ^^^^^^^^^^^^^^^ help: try: `Vec<u32>`
++
++error: aborting due to 3 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::verbose_file_reads)]
++use std::env::temp_dir;
++use std::fs::File;
++use std::io::Read;
++
++struct Struct;
++// To make sure we only warn on File::{read_to_end, read_to_string} calls
++impl Struct {
++ pub fn read_to_end(&self) {}
++
++ pub fn read_to_string(&self) {}
++}
++
++fn main() -> std::io::Result<()> {
++ let path = "foo.txt";
++ // Lint shouldn't catch this
++ let s = Struct;
++ s.read_to_end();
++ s.read_to_string();
++ // Should catch this
++ let mut f = File::open(&path)?;
++ let mut buffer = Vec::new();
++ f.read_to_end(&mut buffer)?;
++ // ...and this
++ let mut string_buffer = String::new();
++ f.read_to_string(&mut string_buffer)?;
++ Ok(())
++}
--- /dev/null
--- /dev/null
++error: use of `File::read_to_end`
++ --> $DIR/verbose_file_reads.rs:23:5
++ |
++LL | f.read_to_end(&mut buffer)?;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::verbose-file-reads` implied by `-D warnings`
++ = help: consider using `fs::read` instead
++
++error: use of `File::read_to_string`
++ --> $DIR/verbose_file_reads.rs:26:5
++ |
++LL | f.read_to_string(&mut string_buffer)?;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider using `fs::read_to_string` instead
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++use std::fmt::Debug;
++use std::ptr;
++use std::rc::Rc;
++use std::sync::Arc;
++
++#[warn(clippy::vtable_address_comparisons)]
++fn main() {
++ let a: *const dyn Debug = &1 as &dyn Debug;
++ let b: *const dyn Debug = &1 as &dyn Debug;
++
++ // These should fail:
++ let _ = a == b;
++ let _ = a != b;
++ let _ = a < b;
++ let _ = a <= b;
++ let _ = a > b;
++ let _ = a >= b;
++ ptr::eq(a, b);
++
++ let a = &1 as &dyn Debug;
++ let b = &1 as &dyn Debug;
++ ptr::eq(a, b);
++
++ let a: Rc<dyn Debug> = Rc::new(1);
++ Rc::ptr_eq(&a, &a);
++
++ let a: Arc<dyn Debug> = Arc::new(1);
++ Arc::ptr_eq(&a, &a);
++
++ // These should be fine:
++ let a = &1;
++ ptr::eq(a, a);
++
++ let a = Rc::new(1);
++ Rc::ptr_eq(&a, &a);
++
++ let a = Arc::new(1);
++ Arc::ptr_eq(&a, &a);
++
++ let a: &[u8] = b"";
++ ptr::eq(a, a);
++}
--- /dev/null
--- /dev/null
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:12:13
++ |
++LL | let _ = a == b;
++ | ^^^^^^
++ |
++ = note: `-D clippy::vtable-address-comparisons` implied by `-D warnings`
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:13:13
++ |
++LL | let _ = a != b;
++ | ^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:14:13
++ |
++LL | let _ = a < b;
++ | ^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:15:13
++ |
++LL | let _ = a <= b;
++ | ^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:16:13
++ |
++LL | let _ = a > b;
++ | ^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:17:13
++ |
++LL | let _ = a >= b;
++ | ^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:18:5
++ |
++LL | ptr::eq(a, b);
++ | ^^^^^^^^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:22:5
++ |
++LL | ptr::eq(a, b);
++ | ^^^^^^^^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:25:5
++ |
++LL | Rc::ptr_eq(&a, &a);
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++ --> $DIR/vtable_address_comparisons.rs:28:5
++ |
++LL | Arc::ptr_eq(&a, &a);
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++ = help: consider extracting and comparing data pointers only
++
++error: aborting due to 10 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::while_let_loop)]
++
++fn main() {
++ let y = Some(true);
++ loop {
++ if let Some(_x) = y {
++ let _v = 1;
++ } else {
++ break;
++ }
++ }
++
++ #[allow(clippy::never_loop)]
++ loop {
++ // no error, break is not in else clause
++ if let Some(_x) = y {
++ let _v = 1;
++ }
++ break;
++ }
++
++ loop {
++ match y {
++ Some(_x) => true,
++ None => break,
++ };
++ }
++
++ loop {
++ let x = match y {
++ Some(x) => x,
++ None => break,
++ };
++ let _x = x;
++ let _str = "foo";
++ }
++
++ loop {
++ let x = match y {
++ Some(x) => x,
++ None => break,
++ };
++ {
++ let _a = "bar";
++ };
++ {
++ let _b = "foobar";
++ }
++ }
++
++ loop {
++ // no error, else branch does something other than break
++ match y {
++ Some(_x) => true,
++ _ => {
++ let _z = 1;
++ break;
++ },
++ };
++ }
++
++ while let Some(x) = y {
++ // no error, obviously
++ println!("{}", x);
++ }
++
++ // #675, this used to have a wrong suggestion
++ loop {
++ let (e, l) = match "".split_whitespace().next() {
++ Some(word) => (word.is_empty(), word.len()),
++ None => break,
++ };
++
++ let _ = (e, l);
++ }
++}
++
++fn issue771() {
++ let mut a = 100;
++ let b = Some(true);
++ loop {
++ if a > 10 {
++ break;
++ }
++
++ match b {
++ Some(_) => a = 0,
++ None => break,
++ }
++ }
++}
++
++fn issue1017() {
++ let r: Result<u32, u32> = Ok(42);
++ let mut len = 1337;
++
++ loop {
++ match r {
++ Err(_) => len = 0,
++ Ok(length) => {
++ len = length;
++ break;
++ },
++ }
++ }
++}
++
++#[allow(clippy::never_loop)]
++fn issue1948() {
++ // should not trigger clippy::while_let_loop lint because break passes an expression
++ let a = Some(10);
++ let b = loop {
++ if let Some(c) = a {
++ break Some(c);
++ } else {
++ break None;
++ }
++ };
++}
--- /dev/null
--- /dev/null
++error: this loop could be written as a `while let` loop
++ --> $DIR/while_let_loop.rs:5:5
++ |
++LL | / loop {
++LL | | if let Some(_x) = y {
++LL | | let _v = 1;
++LL | | } else {
++LL | | break;
++LL | | }
++LL | | }
++ | |_____^ help: try: `while let Some(_x) = y { .. }`
++ |
++ = note: `-D clippy::while-let-loop` implied by `-D warnings`
++
++error: this loop could be written as a `while let` loop
++ --> $DIR/while_let_loop.rs:22:5
++ |
++LL | / loop {
++LL | | match y {
++LL | | Some(_x) => true,
++LL | | None => break,
++LL | | };
++LL | | }
++ | |_____^ help: try: `while let Some(_x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++ --> $DIR/while_let_loop.rs:29:5
++ |
++LL | / loop {
++LL | | let x = match y {
++LL | | Some(x) => x,
++LL | | None => break,
++... |
++LL | | let _str = "foo";
++LL | | }
++ | |_____^ help: try: `while let Some(x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++ --> $DIR/while_let_loop.rs:38:5
++ |
++LL | / loop {
++LL | | let x = match y {
++LL | | Some(x) => x,
++LL | | None => break,
++... |
++LL | | }
++LL | | }
++ | |_____^ help: try: `while let Some(x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++ --> $DIR/while_let_loop.rs:68:5
++ |
++LL | / loop {
++LL | | let (e, l) = match "".split_whitespace().next() {
++LL | | Some(word) => (word.is_empty(), word.len()),
++LL | | None => break,
++... |
++LL | | let _ = (e, l);
++LL | | }
++ | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::while_let_on_iterator)]
++#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++
++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() {
++ break;
++ }
++ println!("Remaining iter {:?}", iter);
++
++ // 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 nested_loops() {
++ let a = [42, 1337];
++ let mut y = a.iter();
++ loop {
++ // x is reused, so don't lint here
++ while let Some(_) = y.next() {}
++ }
++
++ let mut y = a.iter();
++ for _ in 0..2 {
++ while let Some(_) = y.next() {
++ // y is reused, don't lint
++ }
++ }
++
++ 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 and suggest:
++ //
++ // for _ in values.iter() {}
++ //
++ 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 main() {
++ base();
++ refutable();
++ nested_loops();
++ issue1121();
++ issue2965();
++ issue3670();
++ issue1654();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![warn(clippy::while_let_on_iterator)]
++#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++
++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() {
++ break;
++ }
++ println!("Remaining iter {:?}", iter);
++
++ // 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 nested_loops() {
++ let a = [42, 1337];
++ let mut y = a.iter();
++ loop {
++ // x is reused, so don't lint here
++ while let Some(_) = y.next() {}
++ }
++
++ let mut y = a.iter();
++ for _ in 0..2 {
++ while let Some(_) = y.next() {
++ // y is reused, don't lint
++ }
++ }
++
++ 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 and suggest:
++ //
++ // for _ in values.iter() {}
++ //
++ 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 main() {
++ base();
++ refutable();
++ nested_loops();
++ issue1121();
++ issue2965();
++ issue3670();
++ issue1654();
++}
--- /dev/null
--- /dev/null
++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:97:9
++ |
++LL | while let Some(_) = y.next() {
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::wildcard_in_or_patterns)]
++
++fn main() {
++ match "foo" {
++ "a" => {
++ dbg!("matched a");
++ },
++ "bar" | _ => {
++ dbg!("matched (bar or) wild");
++ },
++ };
++ match "foo" {
++ "a" => {
++ dbg!("matched a");
++ },
++ "bar" | "bar2" | _ => {
++ dbg!("matched (bar or bar2 or) wild");
++ },
++ };
++ match "foo" {
++ "a" => {
++ dbg!("matched a");
++ },
++ _ | "bar" | _ => {
++ dbg!("matched (bar or) wild");
++ },
++ };
++ match "foo" {
++ "a" => {
++ dbg!("matched a");
++ },
++ _ | "bar" => {
++ dbg!("matched (bar or) wild");
++ },
++ };
++}
--- /dev/null
--- /dev/null
++error: wildcard pattern covers any other pattern as it will match anyway.
++ --> $DIR/wild_in_or_pats.rs:8:9
++ |
++LL | "bar" | _ => {
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings`
++ = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++ --> $DIR/wild_in_or_pats.rs:16:9
++ |
++LL | "bar" | "bar2" | _ => {
++ | ^^^^^^^^^^^^^^^^^^
++ |
++ = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++ --> $DIR/wild_in_or_pats.rs:24:9
++ |
++LL | _ | "bar" | _ => {
++ | ^^^^^^^^^^^^^
++ |
++ = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++ --> $DIR/wild_in_or_pats.rs:32:9
++ |
++LL | _ | "bar" => {
++ | ^^^^^^^^^
++ |
++ = help: Consider handling `_` separately.
++
++error: aborting due to 4 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::wildcard_enum_match_arm)]
++#![allow(
++ unreachable_code,
++ unused_variables,
++ dead_code,
++ clippy::single_match,
++ clippy::wildcard_in_or_patterns
++)]
++
++use std::io::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 => {},
++ std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {},
++ }
++ match error_kind {
++ ErrorKind::NotFound => {},
++ 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 => {},
++ _ => {},
++ }
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![deny(clippy::wildcard_enum_match_arm)]
++#![allow(
++ unreachable_code,
++ unused_variables,
++ dead_code,
++ clippy::single_match,
++ clippy::wildcard_in_or_patterns
++)]
++
++use std::io::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 => {},
++ 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 => {},
++ _ => {},
++ }
++}
--- /dev/null
--- /dev/null
++error: wildcard match will miss any future added variants
++ --> $DIR/wildcard_enum_match_arm.rs:37: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:3:9
++ |
++LL | #![deny(clippy::wildcard_enum_match_arm)]
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: wildcard match will miss any future added variants
++ --> $DIR/wildcard_enum_match_arm.rs:41: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 miss any future added variants
++ --> $DIR/wildcard_enum_match_arm.rs:45: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 miss any future added variants
++ --> $DIR/wildcard_enum_match_arm.rs:61:9
++ |
++LL | _ => "No red",
++ | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
++
++error: match on non-exhaustive enum doesn't explicitly match all known variants
++ --> $DIR/wildcard_enum_match_arm.rs:78:9
++ |
++LL | _ => {},
++ | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:wildcard_imports_helper.rs
++
++#![warn(clippy::wildcard_imports)]
++//#![allow(clippy::redundant_pub_crate)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++extern crate wildcard_imports_helper;
++
++use crate::fn_mod::foo;
++use crate::mod_mod::inner_mod;
++use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
++#[macro_use]
++use crate::struct_mod::{A, inner_struct_mod};
++
++#[allow(unused_imports)]
++use wildcard_imports_helper::inner::inner_for_self_import;
++use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
++use wildcard_imports_helper::{ExternA, extern_foo};
++
++use std::io::prelude::*;
++
++struct ReadFoo;
++
++impl Read for ReadFoo {
++ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
++ Ok(0)
++ }
++}
++
++mod fn_mod {
++ pub fn foo() {}
++}
++
++mod mod_mod {
++ pub mod inner_mod {
++ pub fn foo() {}
++ }
++}
++
++mod multi_fn_mod {
++ pub fn multi_foo() {}
++ pub fn multi_bar() {}
++ pub fn multi_baz() {}
++ pub mod multi_inner_mod {
++ pub fn foo() {}
++ }
++}
++
++mod struct_mod {
++ pub struct A;
++ pub struct B;
++ pub mod inner_struct_mod {
++ pub struct C;
++ }
++
++ #[macro_export]
++ macro_rules! double_struct_import_test {
++ () => {
++ let _ = A;
++ };
++ }
++}
++
++fn main() {
++ foo();
++ multi_foo();
++ multi_bar();
++ multi_inner_mod::foo();
++ inner_mod::foo();
++ extern_foo();
++ inner_extern_bar();
++
++ let _ = A;
++ let _ = inner_struct_mod::C;
++ let _ = ExternA;
++
++ double_struct_import_test!();
++ double_struct_import_test!();
++}
++
++mod in_fn_test {
++ pub use self::inner_exported::*;
++ #[allow(unused_imports)]
++ pub(crate) use self::inner_exported2::*;
++
++ fn test_intern() {
++ use crate::fn_mod::foo;
++
++ foo();
++ }
++
++ fn test_extern() {
++ use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
++ use wildcard_imports_helper::{ExternA, extern_foo};
++
++ inner_for_self_import::inner_extern_foo();
++ inner_extern_foo();
++
++ extern_foo();
++
++ let _ = ExternA;
++ }
++
++ fn test_inner_nested() {
++ use self::{inner::inner_foo, inner2::inner_bar};
++
++ inner_foo();
++ inner_bar();
++ }
++
++ fn test_extern_reexported() {
++ use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
++
++ extern_exported();
++ let _ = ExternExportedStruct;
++ let _ = ExternExportedEnum::A;
++ }
++
++ mod inner_exported {
++ pub fn exported() {}
++ pub struct ExportedStruct;
++ pub enum ExportedEnum {
++ A,
++ }
++ }
++
++ mod inner_exported2 {
++ pub(crate) fn exported2() {}
++ }
++
++ mod inner {
++ pub fn inner_foo() {}
++ }
++
++ mod inner2 {
++ pub fn inner_bar() {}
++ }
++}
++
++fn test_reexported() {
++ use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
++
++ exported();
++ let _ = ExportedStruct;
++ let _ = ExportedEnum::A;
++}
++
++#[rustfmt::skip]
++fn test_weird_formatting() {
++ use crate:: in_fn_test::exported;
++ use crate:: fn_mod::foo;
++
++ exported();
++ foo();
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++// aux-build:wildcard_imports_helper.rs
++
++#![warn(clippy::wildcard_imports)]
++//#![allow(clippy::redundant_pub_crate)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++extern crate wildcard_imports_helper;
++
++use crate::fn_mod::*;
++use crate::mod_mod::*;
++use crate::multi_fn_mod::*;
++#[macro_use]
++use crate::struct_mod::*;
++
++#[allow(unused_imports)]
++use wildcard_imports_helper::inner::inner_for_self_import;
++use wildcard_imports_helper::inner::inner_for_self_import::*;
++use wildcard_imports_helper::*;
++
++use std::io::prelude::*;
++
++struct ReadFoo;
++
++impl Read for ReadFoo {
++ fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
++ Ok(0)
++ }
++}
++
++mod fn_mod {
++ pub fn foo() {}
++}
++
++mod mod_mod {
++ pub mod inner_mod {
++ pub fn foo() {}
++ }
++}
++
++mod multi_fn_mod {
++ pub fn multi_foo() {}
++ pub fn multi_bar() {}
++ pub fn multi_baz() {}
++ pub mod multi_inner_mod {
++ pub fn foo() {}
++ }
++}
++
++mod struct_mod {
++ pub struct A;
++ pub struct B;
++ pub mod inner_struct_mod {
++ pub struct C;
++ }
++
++ #[macro_export]
++ macro_rules! double_struct_import_test {
++ () => {
++ let _ = A;
++ };
++ }
++}
++
++fn main() {
++ foo();
++ multi_foo();
++ multi_bar();
++ multi_inner_mod::foo();
++ inner_mod::foo();
++ extern_foo();
++ inner_extern_bar();
++
++ let _ = A;
++ let _ = inner_struct_mod::C;
++ let _ = ExternA;
++
++ double_struct_import_test!();
++ double_struct_import_test!();
++}
++
++mod in_fn_test {
++ pub use self::inner_exported::*;
++ #[allow(unused_imports)]
++ pub(crate) use self::inner_exported2::*;
++
++ fn test_intern() {
++ use crate::fn_mod::*;
++
++ foo();
++ }
++
++ fn test_extern() {
++ use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
++ use wildcard_imports_helper::*;
++
++ inner_for_self_import::inner_extern_foo();
++ inner_extern_foo();
++
++ extern_foo();
++
++ let _ = ExternA;
++ }
++
++ fn test_inner_nested() {
++ use self::{inner::*, inner2::*};
++
++ inner_foo();
++ inner_bar();
++ }
++
++ fn test_extern_reexported() {
++ use wildcard_imports_helper::*;
++
++ extern_exported();
++ let _ = ExternExportedStruct;
++ let _ = ExternExportedEnum::A;
++ }
++
++ mod inner_exported {
++ pub fn exported() {}
++ pub struct ExportedStruct;
++ pub enum ExportedEnum {
++ A,
++ }
++ }
++
++ mod inner_exported2 {
++ pub(crate) fn exported2() {}
++ }
++
++ mod inner {
++ pub fn inner_foo() {}
++ }
++
++ mod inner2 {
++ pub fn inner_bar() {}
++ }
++}
++
++fn test_reexported() {
++ use crate::in_fn_test::*;
++
++ exported();
++ let _ = ExportedStruct;
++ let _ = ExportedEnum::A;
++}
++
++#[rustfmt::skip]
++fn test_weird_formatting() {
++ use crate:: in_fn_test:: * ;
++ use crate:: fn_mod::
++ *;
++
++ exported();
++ foo();
++}
--- /dev/null
--- /dev/null
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:11:5
++ |
++LL | use crate::fn_mod::*;
++ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
++ |
++ = note: `-D clippy::wildcard-imports` implied by `-D warnings`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:12:5
++ |
++LL | use crate::mod_mod::*;
++ | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:13:5
++ |
++LL | use crate::multi_fn_mod::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:15:5
++ |
++LL | use crate::struct_mod::*;
++ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:19:5
++ |
++LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:20:5
++ |
++LL | use wildcard_imports_helper::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:89:13
++ |
++LL | use crate::fn_mod::*;
++ | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:95:75
++ |
++LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
++ | ^ help: try: `inner_extern_foo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:96:13
++ |
++LL | use wildcard_imports_helper::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:107:20
++ |
++LL | use self::{inner::*, inner2::*};
++ | ^^^^^^^^ help: try: `inner::inner_foo`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:107:30
++ |
++LL | use self::{inner::*, inner2::*};
++ | ^^^^^^^^^ help: try: `inner2::inner_bar`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:114:13
++ |
++LL | use wildcard_imports_helper::*;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:143:9
++ |
++LL | use crate::in_fn_test::*;
++ | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:152:9
++ |
++LL | use crate:: in_fn_test:: * ;
++ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
++
++error: usage of wildcard import
++ --> $DIR/wildcard_imports.rs:153:9
++ |
++LL | use crate:: fn_mod::
++ | _________^
++LL | | *;
++ | |_________^ help: try: `crate:: fn_mod::foo`
++
++error: aborting due to 15 previous errors
++
--- /dev/null
--- /dev/null
++#![allow(unused_must_use)]
++#![warn(clippy::write_literal)]
++
++use std::io::Write;
++
++fn main() {
++ let mut v = Vec::new();
++
++ // these should be fine
++ write!(&mut v, "Hello");
++ writeln!(&mut v, "Hello");
++ let world = "world";
++ writeln!(&mut v, "Hello {}", world);
++ writeln!(&mut v, "Hello {world}", world = world);
++ writeln!(&mut v, "3 in hex is {:X}", 3);
++ writeln!(&mut v, "2 + 1 = {:.4}", 3);
++ writeln!(&mut v, "2 + 1 = {:5.4}", 3);
++ writeln!(&mut v, "Debug test {:?}", "hello, world");
++ writeln!(&mut v, "{0:8} {1:>8}", "hello", "world");
++ writeln!(&mut v, "{1:8} {0:>8}", "hello", "world");
++ writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
++ writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
++ writeln!(&mut v, "{number:>width$}", number = 1, width = 6);
++ writeln!(&mut v, "{number:>0width$}", number = 1, width = 6);
++
++ // these should throw warnings
++ writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
++ write!(&mut v, "Hello {}", "world");
++ writeln!(&mut v, "Hello {} {}", world, "world");
++ writeln!(&mut v, "Hello {}", "world");
++ writeln!(&mut v, "10 / 4 is {}", 2.5);
++ writeln!(&mut v, "2 + 1 = {}", 3);
++
++ // positional args don't change the fact
++ // that we're using a literal -- this should
++ // throw a warning
++ writeln!(&mut v, "{0} {1}", "hello", "world");
++ writeln!(&mut v, "{1} {0}", "hello", "world");
++
++ // named args shouldn't change anything either
++ writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++ writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++}
--- /dev/null
--- /dev/null
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:27:79
++ |
++LL | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
++ | ^
++ |
++ = note: `-D clippy::write-literal` implied by `-D warnings`
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:28:32
++ |
++LL | write!(&mut v, "Hello {}", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:29:44
++ |
++LL | writeln!(&mut v, "Hello {} {}", world, "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:30:34
++ |
++LL | writeln!(&mut v, "Hello {}", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:31:38
++ |
++LL | writeln!(&mut v, "10 / 4 is {}", 2.5);
++ | ^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:32:36
++ |
++LL | writeln!(&mut v, "2 + 1 = {}", 3);
++ | ^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:37:33
++ |
++LL | writeln!(&mut v, "{0} {1}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:37:42
++ |
++LL | writeln!(&mut v, "{0} {1}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:38:33
++ |
++LL | writeln!(&mut v, "{1} {0}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:38:42
++ |
++LL | writeln!(&mut v, "{1} {0}", "hello", "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:41:43
++ |
++LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:41:58
++ |
++LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:42:43
++ |
++LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: literal with an empty format string
++ --> $DIR/write_literal.rs:42:58
++ |
++LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++ | ^^^^^^^
++
++error: aborting due to 14 previous errors
++
--- /dev/null
--- /dev/null
++// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
++// // run-rustfix
++
++#![allow(clippy::write_literal)]
++#![warn(clippy::write_with_newline)]
++
++use std::io::Write;
++
++fn main() {
++ let mut v = Vec::new();
++
++ // These should fail
++ write!(&mut v, "Hello\n");
++ write!(&mut v, "Hello {}\n", "world");
++ write!(&mut v, "Hello {} {}\n", "world", "#2");
++ write!(&mut v, "{}\n", 1265);
++
++ // These should be fine
++ write!(&mut v, "");
++ write!(&mut v, "Hello");
++ writeln!(&mut v, "Hello");
++ writeln!(&mut v, "Hello\n");
++ writeln!(&mut v, "Hello {}\n", "world");
++ write!(&mut v, "Issue\n{}", 1265);
++ write!(&mut v, "{}", 1265);
++ write!(&mut v, "\n{}", 1275);
++ write!(&mut v, "\n\n");
++ write!(&mut v, "like eof\n\n");
++ write!(&mut v, "Hello {} {}\n\n", "world", "#2");
++ writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
++ writeln!(&mut v, "\nbla\n\n"); // #3126
++
++ // Escaping
++ write!(&mut v, "\\n"); // #3514
++ write!(&mut v, "\\\n"); // should fail
++ write!(&mut v, "\\\\n");
++
++ // Raw strings
++ write!(&mut v, r"\n"); // #3778
++
++ // Literal newlines should also fail
++ write!(
++ &mut v,
++ "
++"
++ );
++ write!(
++ &mut v,
++ r"
++"
++ );
++
++ // Don't warn on CRLF (#4208)
++ write!(&mut v, "\r\n");
++ write!(&mut v, "foo\r\n");
++ write!(&mut v, "\\r\n"); //~ ERROR
++ write!(&mut v, "foo\rbar\n");
++}
--- /dev/null
--- /dev/null
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:13:5
++ |
++LL | write!(&mut v, "Hello/n");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++ = note: `-D clippy::write-with-newline` implied by `-D warnings`
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "Hello");
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:14:5
++ |
++LL | write!(&mut v, "Hello {}/n", "world");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "Hello {}", "world");
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:15:5
++ |
++LL | write!(&mut v, "Hello {} {}/n", "world", "#2");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "Hello {} {}", "world", "#2");
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:16:5
++ |
++LL | write!(&mut v, "{}/n", 1265);
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "{}", 1265);
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:35:5
++ |
++LL | write!(&mut v, "//n"); // should fail
++ | ^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "/"); // should fail
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:42:5
++ |
++LL | / write!(
++LL | | &mut v,
++LL | | "
++LL | | "
++LL | | );
++ | |_____^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(
++LL | &mut v,
++LL | ""
++ |
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:47:5
++ |
++LL | / write!(
++LL | | &mut v,
++LL | | r"
++LL | | "
++LL | | );
++ | |_____^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(
++LL | &mut v,
++LL | r""
++ |
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:56:5
++ |
++LL | write!(&mut v, "/r/n"); //~ ERROR
++ | ^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "/r"); //~ ERROR
++ | ^^^^^^^ --
++
++error: using `write!()` with a format string that ends in a single newline
++ --> $DIR/write_with_newline.rs:57:5
++ |
++LL | write!(&mut v, "foo/rbar/n");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: use `writeln!()` instead
++ |
++LL | writeln!(&mut v, "foo/rbar");
++ | ^^^^^^^ --
++
++error: aborting due to 9 previous errors
++
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_must_use)]
++#![warn(clippy::writeln_empty_string)]
++use std::io::Write;
++
++fn main() {
++ let mut v = Vec::new();
++
++ // These should fail
++ writeln!(&mut v);
++
++ let mut suggestion = Vec::new();
++ writeln!(&mut suggestion);
++
++ // These should be fine
++ writeln!(&mut v);
++ writeln!(&mut v, " ");
++ write!(&mut v, "");
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++
++#![allow(unused_must_use)]
++#![warn(clippy::writeln_empty_string)]
++use std::io::Write;
++
++fn main() {
++ let mut v = Vec::new();
++
++ // These should fail
++ writeln!(&mut v, "");
++
++ let mut suggestion = Vec::new();
++ writeln!(&mut suggestion, "");
++
++ // These should be fine
++ writeln!(&mut v);
++ writeln!(&mut v, " ");
++ write!(&mut v, "");
++}
--- /dev/null
--- /dev/null
++error: using `writeln!(&mut v, "")`
++ --> $DIR/writeln_empty_string.rs:11:5
++ |
++LL | writeln!(&mut v, "");
++ | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)`
++ |
++ = note: `-D clippy::writeln-empty-string` implied by `-D warnings`
++
++error: using `writeln!(&mut suggestion, "")`
++ --> $DIR/writeln_empty_string.rs:14:5
++ |
++LL | writeln!(&mut suggestion, "");
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)`
++
++error: aborting due to 2 previous errors
++
--- /dev/null
--- /dev/null
++#![warn(clippy::wrong_self_convention)]
++#![warn(clippy::wrong_pub_self_convention)]
++#![allow(dead_code)]
++
++fn main() {}
++
++#[derive(Clone, Copy)]
++struct Foo;
++
++impl Foo {
++ fn as_i32(self) {}
++ fn as_u32(&self) {}
++ fn into_i32(self) {}
++ fn is_i32(self) {}
++ fn is_u32(&self) {}
++ fn to_i32(self) {}
++ fn from_i32(self) {}
++
++ pub fn as_i64(self) {}
++ pub fn into_i64(self) {}
++ pub fn is_i64(self) {}
++ pub fn to_i64(self) {}
++ pub fn from_i64(self) {}
++ // check whether the lint can be allowed at the function level
++ #[allow(clippy::wrong_self_convention)]
++ pub fn from_cake(self) {}
++
++ fn as_x<F: AsRef<Self>>(_: F) {}
++ fn as_y<F: AsRef<Foo>>(_: F) {}
++}
++
++struct Bar;
++
++impl Bar {
++ fn as_i32(self) {}
++ fn as_u32(&self) {}
++ fn into_i32(&self) {}
++ fn into_u32(self) {}
++ fn is_i32(self) {}
++ fn is_u32(&self) {}
++ fn to_i32(self) {}
++ fn to_u32(&self) {}
++ fn from_i32(self) {}
++
++ pub fn as_i64(self) {}
++ pub fn into_i64(&self) {}
++ pub fn is_i64(self) {}
++ pub fn to_i64(self) {}
++ pub fn from_i64(self) {}
++
++ // test for false positives
++ fn as_(self) {}
++ fn into_(&self) {}
++ fn is_(self) {}
++ fn to_(self) {}
++ fn from_(self) {}
++ fn to_mut(&mut self) {}
++}
++
++// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value
++#[allow(clippy::boxed_local)]
++mod issue4293 {
++ use std::rc::Rc;
++ use std::sync::Arc;
++
++ struct T;
++
++ impl T {
++ fn into_s1(self: Box<Self>) {}
++ fn into_s2(self: Rc<Self>) {}
++ fn into_s3(self: Arc<Self>) {}
++
++ fn into_t1(self: Box<T>) {}
++ fn into_t2(self: Rc<T>) {}
++ fn into_t3(self: Arc<T>) {}
++ }
++}
--- /dev/null
--- /dev/null
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:17:17
++ |
++LL | fn from_i32(self) {}
++ | ^^^^
++ |
++ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:23:21
++ |
++LL | pub fn from_i64(self) {}
++ | ^^^^
++
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:35:15
++ |
++LL | fn as_i32(self) {}
++ | ^^^^
++
++error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:37:17
++ |
++LL | fn into_i32(&self) {}
++ | ^^^^^
++
++error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:39:15
++ |
++LL | fn is_i32(self) {}
++ | ^^^^
++
++error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:41:15
++ |
++LL | fn to_i32(self) {}
++ | ^^^^
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:43:17
++ |
++LL | fn from_i32(self) {}
++ | ^^^^
++
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:45:19
++ |
++LL | pub fn as_i64(self) {}
++ | ^^^^
++
++error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:46:21
++ |
++LL | pub fn into_i64(&self) {}
++ | ^^^^^
++
++error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:47:19
++ |
++LL | pub fn is_i64(self) {}
++ | ^^^^
++
++error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:48:19
++ |
++LL | pub fn to_i64(self) {}
++ | ^^^^
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++ --> $DIR/wrong_self_convention.rs:49:21
++ |
++LL | pub fn from_i64(self) {}
++ | ^^^^
++
++error: aborting due to 12 previous errors
++
--- /dev/null
--- /dev/null
++#[allow(unused_variables)]
++#[warn(clippy::zero_divided_by_zero)]
++fn main() {
++ let nan = 0.0 / 0.0;
++ let f64_nan = 0.0 / 0.0f64;
++ let other_f64_nan = 0.0f64 / 0.0;
++ let one_more_f64_nan = 0.0f64 / 0.0f64;
++ let zero = 0.0;
++ let other_zero = 0.0;
++ let other_nan = zero / other_zero; // fine - this lint doesn't propegate constants.
++ let not_nan = 2.0 / 0.0; // not an error: 2/0 = inf
++ let also_not_nan = 0.0 / 2.0; // not an error: 0/2 = 0
++}
--- /dev/null
--- /dev/null
++error: equal expressions as operands to `/`
++ --> $DIR/zero_div_zero.rs:4:15
++ |
++LL | let nan = 0.0 / 0.0;
++ | ^^^^^^^^^
++ |
++ = note: `#[deny(clippy::eq_op)]` on by default
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++ --> $DIR/zero_div_zero.rs:4:15
++ |
++LL | let nan = 0.0 / 0.0;
++ | ^^^^^^^^^
++ |
++ = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings`
++ = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++ --> $DIR/zero_div_zero.rs:5:19
++ |
++LL | let f64_nan = 0.0 / 0.0f64;
++ | ^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++ --> $DIR/zero_div_zero.rs:5:19
++ |
++LL | let f64_nan = 0.0 / 0.0f64;
++ | ^^^^^^^^^^^^
++ |
++ = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++ --> $DIR/zero_div_zero.rs:6:25
++ |
++LL | let other_f64_nan = 0.0f64 / 0.0;
++ | ^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++ --> $DIR/zero_div_zero.rs:6:25
++ |
++LL | let other_f64_nan = 0.0f64 / 0.0;
++ | ^^^^^^^^^^^^
++ |
++ = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++ --> $DIR/zero_div_zero.rs:7:28
++ |
++LL | let one_more_f64_nan = 0.0f64 / 0.0f64;
++ | ^^^^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++ --> $DIR/zero_div_zero.rs:7:28
++ |
++LL | let one_more_f64_nan = 0.0f64 / 0.0f64;
++ | ^^^^^^^^^^^^^^^
++ |
++ = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: aborting due to 8 previous errors
++
--- /dev/null
--- /dev/null
++fn main() {
++ unsafe {
++ let x = &() as *const ();
++ x.offset(0);
++ x.wrapping_add(0);
++ x.sub(0);
++ x.wrapping_sub(0);
++
++ let y = &1 as *const u8;
++ y.offset(0);
++ }
++}
--- /dev/null
--- /dev/null
++error[E0606]: casting `&i32` as `*const u8` is invalid
++ --> $DIR/zero_offset.rs:9:17
++ |
++LL | let y = &1 as *const u8;
++ | ^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
++For more information about this error, try `rustc --explain E0606`.
--- /dev/null
--- /dev/null
++// run-rustfix
++pub fn foo(_const: *const f32, _mut: *mut i64) {}
++
++fn main() {
++ let _ = std::ptr::null::<usize>();
++ let _ = std::ptr::null_mut::<f64>();
++ let _: *const u8 = std::ptr::null();
++
++ foo(0 as _, 0 as _);
++ foo(std::ptr::null(), std::ptr::null_mut());
++
++ let z = 0;
++ let _ = z as *const usize; // this is currently not caught
++}
--- /dev/null
--- /dev/null
++// run-rustfix
++pub fn foo(_const: *const f32, _mut: *mut i64) {}
++
++fn main() {
++ let _ = 0 as *const usize;
++ let _ = 0 as *mut f64;
++ let _: *const u8 = 0 as *const _;
++
++ foo(0 as _, 0 as _);
++ foo(0 as *const _, 0 as *mut _);
++
++ let z = 0;
++ let _ = z as *const usize; // this is currently not caught
++}
--- /dev/null
--- /dev/null
++error: `0 as *const _` detected
++ --> $DIR/zero_ptr.rs:5:13
++ |
++LL | let _ = 0 as *const usize;
++ | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null::<usize>()`
++ |
++ = note: `-D clippy::zero-ptr` implied by `-D warnings`
++
++error: `0 as *mut _` detected
++ --> $DIR/zero_ptr.rs:6:13
++ |
++LL | let _ = 0 as *mut f64;
++ | ^^^^^^^^^^^^^ help: try: `std::ptr::null_mut::<f64>()`
++
++error: `0 as *const _` detected
++ --> $DIR/zero_ptr.rs:7:24
++ |
++LL | let _: *const u8 = 0 as *const _;
++ | ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
++
++error: `0 as *const _` detected
++ --> $DIR/zero_ptr.rs:10:9
++ |
++LL | foo(0 as *const _, 0 as *mut _);
++ | ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
++
++error: `0 as *mut _` detected
++ --> $DIR/zero_ptr.rs:10:24
++ |
++LL | foo(0 as *const _, 0 as *mut _);
++ | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()`
++
++error: aborting due to 5 previous errors
++
--- /dev/null
--- /dev/null
++#[test]
++fn check_that_clippy_lints_has_the_same_version_as_clippy() {
++ let clippy_meta = cargo_metadata::MetadataCommand::new()
++ .no_deps()
++ .exec()
++ .expect("could not obtain cargo metadata");
++ std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
++ let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
++ .no_deps()
++ .exec()
++ .expect("could not obtain cargo metadata");
++ assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
++ for package in &clippy_meta.packages[0].dependencies {
++ if package.name == "clippy_lints" {
++ assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
++ return;
++ }
++ }
++}
--- /dev/null
--- /dev/null
++[relabel]
++allow-unauthenticated = [
++ "C-*", "A-*", "E-*", "L-*", "M-*", "O-*",
++ "good first issue", "needs test"
++]
++
++[assign]
--- /dev/null
--- /dev/null
++#!/usr/bin/bash
++
++# This run `kcov` on Clippy. The coverage report will be at
++# `./target/cov/index.html`.
++# `compile-test` is special. `kcov` does not work directly on it so these files
++# are compiled manually.
++
++tests=$(find tests/ -maxdepth 1 -name '*.rs' ! -name compile-test.rs -exec basename {} .rs \;)
++tmpdir=$(mktemp -d)
++
++cargo test --no-run --verbose
++
++for t in $tests; do
++ kcov \
++ --verify \
++ --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++ "$tmpdir/$t" \
++ cargo test --test "$t"
++done
++
++for t in ./tests/compile-fail/*.rs; do
++ kcov \
++ --verify \
++ --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++ "$tmpdir/compile-fail-$(basename "$t")" \
++ cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t"
++done
++
++for t in ./tests/run-pass/*.rs; do
++ kcov \
++ --verify \
++ --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++ "$tmpdir/run-pass-$(basename "$t")" \
++ cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t"
++done
++
++kcov --verify --merge target/cov "$tmpdir"/*
--- /dev/null
--- /dev/null
++#!/usr/bin/env python
++
++# Build the gh-pages
++
++from collections import OrderedDict
++import re
++import sys
++import json
++
++from lintlib import parse_all, log
++
++lint_subheadline = re.compile(r'''^\*\*([\w\s]+?)[:?.!]?\*\*(.*)''')
++rust_code_block = re.compile(r'''```rust.+?```''', flags=re.DOTALL)
++
++CONF_TEMPLATE = """\
++This lint has the following configuration variables:
++
++* `%s: %s`: %s (defaults to `%s`)."""
++
++
++def parse_code_block(match):
++ lines = []
++
++ for line in match.group(0).split('\n'):
++ if not line.startswith('# '):
++ lines.append(line)
++
++ return '\n'.join(lines)
++
++
++def parse_lint_def(lint):
++ lint_dict = {}
++ lint_dict['id'] = lint.name
++ lint_dict['group'] = lint.group
++ lint_dict['level'] = lint.level
++ lint_dict['docs'] = OrderedDict()
++
++ last_section = None
++
++ for line in lint.doc:
++ match = re.match(lint_subheadline, line)
++ if match:
++ last_section = match.groups()[0]
++ text = match.groups()[1]
++ else:
++ text = line
++
++ if not last_section:
++ log.warning("Skipping comment line as it was not preceded by a heading")
++ log.debug("in lint `%s`, line `%s`", lint.name, line)
++
++ if last_section not in lint_dict['docs']:
++ lint_dict['docs'][last_section] = ""
++
++ lint_dict['docs'][last_section] += text + "\n"
++
++ for section in lint_dict['docs']:
++ lint_dict['docs'][section] = re.sub(rust_code_block, parse_code_block, lint_dict['docs'][section].strip())
++
++ return lint_dict
++
++
++def main():
++ lintlist, configs = parse_all()
++ lints = {}
++ for lint in lintlist:
++ lints[lint.name] = parse_lint_def(lint)
++ if lint.name in configs:
++ lints[lint.name]['docs']['Configuration'] = \
++ CONF_TEMPLATE % configs[lint.name]
++
++ outfile = sys.argv[1] if len(sys.argv) > 1 else "util/gh-pages/lints.json"
++ with open(outfile, "w") as fp:
++ lints = list(lints.values())
++ lints.sort(key=lambda x: x['id'])
++ json.dump(lints, fp, indent=2)
++ log.info("wrote JSON for great justice")
++
++
++if __name__ == "__main__":
++ main()
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++# Fetches the merge commits between two git commits and prints the PR URL
++# together with the full commit message
++#
++# If you want to use this to update the Clippy changelog, be sure to manually
++# exclude the non-user facing changes like 'rustup' PRs, typo fixes, etc.
++
++first=$1
++last=$2
++
++IFS='
++'
++for pr in $(git log --oneline --grep "Merge #" --grep "Merge pull request" --grep "Auto merge of" --grep "Rollup merge of" "$first...$last" | sort -rn | uniq); do
++ id=$(echo "$pr" | rg -o '#[0-9]{3,5}' | cut -c 2-)
++ commit=$(echo "$pr" | cut -d' ' -f 1)
++ message=$(git --no-pager show --pretty=medium "$commit")
++ if [[ -n $(echo "$message" | rg "^[\s]{4}changelog: [nN]one\.*$") ]]; then
++ continue
++ fi
++
++ echo "URL: https://github.com/rust-lang/rust-clippy/pull/$id"
++ echo "Markdown URL: [#$id](https://github.com/rust-lang/rust-clippy/pull/$id)"
++ echo "$message"
++ echo "---------------------------------------------------------"
++ echo
++done
--- /dev/null
--- /dev/null
++<!DOCTYPE html>
++<html lang="en">
++<head>
++ <meta charset="UTF-8"/>
++ <meta name="viewport" content="width=device-width, initial-scale=1"/>
++
++ <title>ALL the Clippy Lints</title>
++
++ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
++ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/>
++ <style>
++ blockquote { font-size: 1em; }
++ [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
++
++ .form-inline .checkbox { margin-right: 0.6em }
++
++ .panel-heading { cursor: pointer; }
++ .panel-heading:hover { background-color: #eee; }
++
++ .panel-title { display: flex; }
++ .panel-title .label { display: inline-block; }
++
++ .panel-title-name { flex: 1; }
++ .panel-title-name span { vertical-align: bottom; }
++
++ .panel .panel-title-name .anchor { display: none; }
++ .panel:hover .panel-title-name .anchor { display: inline; color: #fff; }
++ </style>
++</head>
++<body>
++ <div class="container" ng-app="clippy" ng-controller="lintList">
++ <div class="page-header">
++ <h1>ALL the Clippy Lints</h1>
++ </div>
++
++ <noscript>
++ <div class="alert alert-danger" role="alert">
++ Sorry, this site only works with JavaScript! :(
++ </div>
++ </noscript>
++
++ <div ng-cloak>
++
++ <div class="alert alert-info" role="alert" ng-if="loading">
++ Loading…
++ </div>
++ <div class="alert alert-danger" role="alert" ng-if="error">
++ Error loading lints!
++ </div>
++
++ <div class="panel panel-default" ng-show="data">
++ <div class="panel-body row">
++ <div class="col-md-6 form-inline">
++ <div class="form-group form-group-lg">
++ <p class="h4">Lint levels</p>
++ <div class="checkbox" ng-repeat="(level, enabled) in levels">
++ <label>
++ <input type="checkbox" ng-model="levels[level]" />
++ {{level}}
++ </label>
++ </div>
++ </div>
++ </div>
++ <div class="col-md-6 form-inline">
++ <div class="form-group form-group-lg">
++ <p class="h4">Lint groups</p>
++ <div class="checkbox" ng-repeat="(group, enabled) in groups">
++ <label class="text-capitalize">
++ <input type="checkbox" ng-model="groups[group]" />
++ {{group}}
++ </label>
++ </div>
++ </div>
++ </div>
++ </div>
++ <div class="panel-body row">
++ <div class="col-md-12 form-horizontal">
++ <div class="input-group">
++ <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label>
++ <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" />
++ <span class="input-group-btn">
++ <button class="btn btn-default" type="button" ng-click="search = ''">
++ Clear
++ </button>
++ </span>
++ </div>
++ </div>
++ </div>
++ </div>
++
++ <article class="panel panel-default" id="{{lint.id}}"
++ ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:search | orderBy:'id' track by lint.id" on-finish-render="ngRepeatFinished">
++ <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]">
++ <h2 class="panel-title">
++ <div class="panel-title-name">
++ <span>{{lint.id}}</span>
++ <a href="#{{lint.id}}" class="anchor label label-default" ng-click="open[lint.id] = true; $event.stopPropagation()">¶</a>
++ </div>
++
++ <div class="panel-title-addons">
++ <span class="label label-default text-capitalize">{{lint.group}}</span>
++
++ <span ng-if="lint.level == 'Allow'" class="label label-success">Allow</span>
++ <span ng-if="lint.level == 'Warn'" class="label label-warning">Warn</span>
++ <span ng-if="lint.level == 'Deny'" class="label label-danger">Deny</span>
++ <span ng-if="lint.level == 'Deprecated'" class="label label-default">Deprecated</span>
++
++ <button class="btn btn-default btn-xs">
++ <span ng-show="open[lint.id]">−</span>
++ <span ng-hide="open[lint.id]">+</span>
++ </button>
++ </div>
++ </h2>
++ </header>
++
++ <ul class="list-group lint-docs" ng-if="lint.docs" ng-class="{collapse: true, in: open[lint.id]}">
++ <li class="list-group-item" ng-repeat="(title, text) in lint.docs">
++ <h4 class="list-group-item-heading">
++ {{title}}
++ </h4>
++ <div class="list-group-item-text" ng-bind-html="text | markdown"></div>
++ </li>
++ </ul>
++ </article>
++ </div>
++ </div>
++
++ <a href="https://github.com/rust-lang/rust-clippy">
++ <img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/>
++ </a>
++
++ <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/7.0.0/markdown-it.min.js"></script>
++ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
++ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
++ <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
++ <script>
++ (function () {
++ var md = window.markdownit({
++ html: true,
++ linkify: true,
++ typographer: true,
++ highlight: function (str, lang) {
++ if (lang && hljs.getLanguage(lang)) {
++ try {
++ return '<pre class="hljs"><code>' +
++ hljs.highlight(lang, str, true).value +
++ '</code></pre>';
++ } catch (__) {}
++ }
++
++ return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
++ }
++ });
++
++ function scrollToLint(lintId) {
++ var target = document.getElementById(lintId);
++ if (!target) {
++ return;
++ }
++ target.scrollIntoView();
++ }
++
++ function scrollToLintByURL($scope) {
++ var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
++ scrollToLint(window.location.hash.slice(1));
++ removeListener();
++ });
++ }
++
++ function selectGroup($scope, selectedGroup) {
++ var groups = $scope.groups;
++ for (var group in groups) {
++ if (groups.hasOwnProperty(group)) {
++ if (group === selectedGroup) {
++ groups[group] = true;
++ } else {
++ groups[group] = false;
++ }
++ }
++ }
++ }
++
++ angular.module("clippy", [])
++ .filter('markdown', function ($sce) {
++ return function (text) {
++ return $sce.trustAsHtml(
++ md.render(text || '')
++ // Oh deer, what a hack :O
++ .replace('<table', '<table class="table"')
++ );
++ };
++ })
++ .directive('onFinishRender', function ($timeout) {
++ return {
++ restrict: 'A',
++ link: function (scope, element, attr) {
++ if (scope.$last === true) {
++ $timeout(function () {
++ scope.$emit(attr.onFinishRender);
++ });
++ }
++ }
++ };
++ })
++ .controller("lintList", function ($scope, $http, $timeout) {
++ // Level filter
++ var LEVEL_FILTERS_DEFAULT = {Allow: true, Warn: true, Deny: true, Deprecated: true};
++ $scope.levels = LEVEL_FILTERS_DEFAULT;
++ $scope.byLevels = function (lint) {
++ return $scope.levels[lint.level];
++ };
++
++ $scope.groups = {};
++ $scope.byGroups = function (lint) {
++ return $scope.groups[lint.group];
++ };
++
++ // Get data
++ $scope.open = {};
++ $scope.loading = true;
++
++ if (window.location.hash.length > 1) {
++ $scope.search = window.location.hash.slice(1);
++ $scope.open[window.location.hash.slice(1)] = true;
++ scrollToLintByURL($scope);
++ }
++
++ $http.get('./lints.json')
++ .success(function (data) {
++ $scope.data = data;
++ $scope.loading = false;
++
++ // Initialize lint groups (the same structure is also used to enable filtering)
++ $scope.groups = data.reduce(function (result, val) {
++ result[val.group] = true;
++ return result;
++ }, {});
++
++ var selectedGroup = getQueryVariable("sel");
++ if (selectedGroup) {
++ selectGroup($scope, selectedGroup.toLowerCase());
++ }
++
++ scrollToLintByURL($scope);
++ })
++ .error(function (data) {
++ $scope.error = data;
++ $scope.loading = false;
++ });
++
++ window.addEventListener('hashchange', function () {
++ // trigger re-render
++ $timeout(function () {
++ $scope.levels = LEVEL_FILTERS_DEFAULT;
++ $scope.search = window.location.hash.slice(1);
++ $scope.open[window.location.hash.slice(1)] = true;
++
++ scrollToLintByURL($scope);
++ });
++ return true;
++ }, false);
++ });
++ })();
++
++ function getQueryVariable(variable) {
++ var query = window.location.search.substring(1);
++ var vars = query.split('&');
++ for (var i = 0; i < vars.length; i++) {
++ var pair = vars[i].split('=');
++ if (decodeURIComponent(pair[0]) == variable) {
++ return decodeURIComponent(pair[1]);
++ }
++ }
++ }
++ </script>
++</body>
++</html>
--- /dev/null
--- /dev/null
++<!DOCTYPE html>
++<html lang="en">
++<head>
++ <meta charset="UTF-8"/>
++ <meta name="viewport" content="width=device-width, initial-scale=1"/>
++
++ <title>Clippy lints documentation</title>
++
++ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
++ <style>
++ [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
++ </style>
++</head>
++<body>
++ <div class="container" ng-app="clippy" ng-controller="docVersions">
++ <div class="page-header">
++ <h1>Clippy lints documentation</h1>
++ </div>
++
++ <div ng-cloak>
++ <div class="alert alert-info" role="alert" ng-if="loading">
++ Loading…
++ </div>
++ <div class="alert alert-danger" role="alert" ng-if="error">
++ Error loading versions!<br/>
++ You can always try to get <a href="master/index.html">the master branch docs</a>.
++ </div>
++
++ <article class="panel panel-default" ng-show="data">
++ <div class="panel-heading">
++ <h3 class="panel-title">
++ Available versions
++ </h3>
++ </div>
++
++ <ul class="list-group">
++ <a class="list-group-item" ng-repeat="version in data | orderBy:versionOrder:true"
++ href="./{{version}}/index.html">
++ {{normalizeVersionDisplay(version)}}
++ </a>
++ </ul>
++ </article>
++ </div>
++ </div>
++
++ <a href="https://github.com/rust-lang/rust-clippy">
++ <img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"/>
++ </a>
++
++
++ <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
++ <script>
++ angular.module('clippy', [])
++ .controller('docVersions', function ($scope, $http) {
++ $scope.loading = true;
++
++ $scope.normalizeVersionDisplay = function(v) {
++ return v.replace(/^v/, '');
++ };
++
++ $scope.normalizeVersion = function(v) {
++ return v.replace(/^v/, '').replace(/^rust-/, '');
++ };
++
++ $scope.versionOrder = function(v) {
++ if (v === 'master') { return Infinity; }
++ if (v === 'stable') { return Number.MAX_VALUE; }
++ if (v === 'beta') { return Number.MAX_VALUE - 1; }
++
++ return $scope.normalizeVersion(v)
++ .split('.')
++ .reverse()
++ .reduce(function(acc, val, index) {
++ return acc + (val * Math.pow(100, index));
++ }, 0);
++ }
++
++ $http.get('./versions.json')
++ .success(function (data) {
++ $scope.data = data;
++ $scope.loading = false;
++ })
++ .error(function (data) {
++ $scope.error = data;
++ $scope.loading = false;
++ });
++ })
++ ;
++ </script>
++</body>
++</html>
--- /dev/null
--- /dev/null
++# Common utils for the several housekeeping scripts.
++
++import os
++import re
++import collections
++
++import logging as log
++log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s')
++
++Lint = collections.namedtuple('Lint', 'name level doc sourcefile group')
++Config = collections.namedtuple('Config', 'name ty doc default')
++
++lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''')
++group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
++conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE)
++confvar_re = re.compile(
++ r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
++comment_re = re.compile(r'''\s*/// ?(.*)''')
++
++lint_levels = {
++ "correctness": 'Deny',
++ "style": 'Warn',
++ "complexity": 'Warn',
++ "perf": 'Warn',
++ "restriction": 'Allow',
++ "pedantic": 'Allow',
++ "nursery": 'Allow',
++ "cargo": 'Allow',
++}
++
++
++def parse_lints(lints, filepath):
++ comment = []
++ clippy = False
++ deprecated = False
++ name = ""
++
++ with open(filepath) as fp:
++ for line in fp:
++ if clippy or deprecated:
++ m = lintname_re.search(line)
++ if m:
++ name = m.group(1).lower()
++ line = next(fp)
++
++ if deprecated:
++ level = "Deprecated"
++ group = "deprecated"
++ else:
++ while True:
++ g = group_re.search(line)
++ if g:
++ group = g.group(1).lower()
++ level = lint_levels.get(group, None)
++ break
++ line = next(fp)
++
++ if level is None:
++ continue
++
++ log.info("found %s with level %s in %s",
++ name, level, filepath)
++ lints.append(Lint(name, level, comment, filepath, group))
++ comment = []
++
++ clippy = False
++ deprecated = False
++ name = ""
++ else:
++ m = comment_re.search(line)
++ if m:
++ comment.append(m.group(1))
++ elif line.startswith("declare_clippy_lint!"):
++ clippy = True
++ deprecated = False
++ elif line.startswith("declare_deprecated_lint!"):
++ clippy = False
++ deprecated = True
++ elif line.startswith("declare_lint!"):
++ import sys
++ print(
++ "don't use `declare_lint!` in Clippy, "
++ "use `declare_clippy_lint!` instead"
++ )
++ sys.exit(42)
++
++
++def parse_configs(path):
++ configs = {}
++ with open(os.path.join(path, 'utils/conf.rs')) as fp:
++ contents = fp.read()
++
++ match = re.search(conf_re, contents)
++ confvars = re.findall(confvar_re, match.group(1))
++
++ for (lints, doc, name, ty, default) in confvars:
++ for lint in lints.split(','):
++ configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default)
++ return configs
++
++
++def parse_all(path="clippy_lints/src"):
++ lints = []
++ for root, dirs, files in os.walk(path):
++ for fn in files:
++ if fn.endswith('.rs'):
++ parse_lints(lints, os.path.join(root, fn))
++
++ log.info("got %s lints", len(lints))
++
++ configs = parse_configs(path)
++ log.info("got %d configs", len(configs))
++
++ return lints, configs
--- /dev/null
--- /dev/null
++#!/usr/bin/env python
++
++import json
++import os
++import sys
++
++from lintlib import log
++
++
++def key(v):
++ if v == 'master':
++ return float('inf')
++ if v == 'stable':
++ return sys.maxsize
++ if v == 'beta':
++ return sys.maxsize - 1
++
++ v = v.replace('v', '').replace('rust-', '')
++
++ s = 0
++ for i, val in enumerate(v.split('.')[::-1]):
++ s += int(val) * 100**i
++
++ return s
++
++
++def main():
++ if len(sys.argv) < 2:
++ print("Error: specify output directory")
++ return
++
++ outdir = sys.argv[1]
++ versions = [
++ dir for dir in os.listdir(outdir) if not dir.startswith(".") and os.path.isdir(os.path.join(outdir, dir))
++ ]
++ versions.sort(key=key)
++
++ with open(os.path.join(outdir, "versions.json"), "w") as fp:
++ json.dump(versions, fp, indent=2)
++ log.info("wrote JSON for great justice")
++
++
++if __name__ == "__main__":
++ main()