diff --git a/.github/deploy.sh b/.github/deploy.sh old mode 100755 new mode 100644 index 19dc4017166c..9ef9678ee92d --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -1,81 +1,49 @@ #!/bin/bash -# Automatically deploy on gh-pages - set -ex -SOURCE_BRANCH="master" -TARGET_BRANCH="gh-pages" - -# Save some useful information -REPO=$(git config remote.origin.url) -SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:} -SHA=$(git rev-parse --verify HEAD) - -# Clone the existing gh-pages for this repo into out/ -git clone --quiet --single-branch --branch "$TARGET_BRANCH" "$REPO" out - 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 -python ./util/export.py out/master/lints.json +python3 ./util/export.py out/master/lints.json -if [[ -n "$TRAVIS_TAG" ]]; then - echo "Save the doc for the current tag ($TRAVIS_TAG) and point current/ to it" - cp -r out/master "out/$TRAVIS_TAG" - rm -f out/current - ln -s "$TRAVIS_TAG" out/current +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 # Generate version index that is shown as root index page cp util/gh-pages/versions.html out/index.html -pushd out - -cat <<-EOF | python - > versions.json -import os, json -print json.dumps([ - dir for dir in os.listdir(".") if not dir.startswith(".") and os.path.isdir(dir) -]) -EOF -popd -# Pull requests and commits to other branches shouldn't try to deploy, just build to verify -if [[ "$TRAVIS_PULL_REQUEST" != "false" ]] || [[ "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]]; then - # Tags should deploy - if [[ -z "$TRAVIS_TAG" ]]; then - echo "Generated, won't push" - exit 0 - fi -fi +echo "Making the versions.json file" +python3 ./util/versions.py out -# Now let's go have some fun with the cloned repo cd out -git config user.name "Travis CI" -git config user.email "travis@ci.invalid" +# 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 + echo "No changes to the output on this push; exiting." + exit 0 fi -cd - - -# Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc -ENCRYPTION_LABEL=e3a2d77100be -ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" -ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" -ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} -ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} -openssl aes-256-cbc -K "$ENCRYPTED_KEY" -iv "$ENCRYPTED_IV" -in .github/deploy_key.enc -out .github/deploy_key -d -chmod 600 .github/deploy_key -eval "$(ssh-agent -s)" -ssh-add .github/deploy_key -cd out -git add . -git commit -m "Automatic deploy to GitHub Pages: ${SHA}" +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}" +else + git add . + git commit -m "Automatic deploy to GitHub Pages: ${SHA}" +fi -# Now that we're all set up, we can push. git push "$SSH_REPO" "$TARGET_BRANCH" diff --git a/.github/deploy_key.enc b/.github/deploy_key.enc deleted file mode 100644 index 48cb3f5a1276..000000000000 Binary files a/.github/deploy_key.enc and /dev/null differ diff --git a/.github/driver.sh b/.github/driver.sh new file mode 100644 index 000000000000..a2e87f5eb374 --- /dev/null +++ b/.github/driver.sh @@ -0,0 +1,29 @@ +#!/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 diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 000000000000..8edf0c23860a --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,99 @@ +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 diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml new file mode 100644 index 000000000000..6675a1029bbc --- /dev/null +++ b/.github/workflows/clippy_bors.yml @@ -0,0 +1,329 @@ +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 diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml new file mode 100644 index 000000000000..9ca2e630cbbb --- /dev/null +++ b/.github/workflows/clippy_dev.yml @@ -0,0 +1,74 @@ +name: Clippy Dev Test + +on: + push: + branches: + - auto + - try + pull_request: + # Only run on paths, that get checked by the clippy_dev tool + paths: + - 'CAHNGELOG.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 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000000..5b7bec81999a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,41 @@ +name: Deploy + +on: + push: + branches: + - master + 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: Deploy + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${{ secrets.DEPLOY_KEY }}" + bash .github/deploy.sh diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml new file mode 100644 index 000000000000..cc175e8bf247 --- /dev/null +++ b/.github/workflows/remark.yml @@ -0,0 +1,55 @@ +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 diff --git a/.gitignore b/.gitignore index f1f4fa4e242a..adf5e8feddf4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# Used by Travis to be able to push: +# Used by CI to be able to push: /.github/deploy_key out diff --git a/.remarkrc b/.remarkrc new file mode 100644 index 000000000000..0ede7ac75cb6 --- /dev/null +++ b/.remarkrc @@ -0,0 +1,12 @@ +{ + "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 + } +} diff --git a/.remarkrc.json b/.remarkrc.json deleted file mode 100644 index 22bae13ccf88..000000000000 --- a/.remarkrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "plugins": { - "lint": { - "table-pipes": false, - "table-pipe-alignment": false, - "maximum-line-length": 120 - } - }, - "settings": { - "commonmark": true - } -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bd3003d45977..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,166 +0,0 @@ -dist: xenial -language: bash -git: - depth: 1 - quiet: true - -branches: - # Don't build these branches - except: - # Used by bors - - trying.tmp - - staging.tmp - -cache: - directories: - - $HOME/.cargo -before_cache: - - cargo install cargo-cache --debug - - find $HOME/.cargo/bin/ ! -type d -exec strip {} \; - - cargo cache --autoclean - -env: - global: - - RUST_BACKTRACE=1 - - secure: "OKulfkA5OGd/d1IhvBKzRkHQwMcWjzrzbimo7+5NhkUkWxndAzl+719TB3wWvIh1i2wXXrEXsyZkXM5FtRrHm55v1VKQ5ibjEvFg1w3NIg81iDyoLq186fLqywvxGkOAFPrsePPsBj5USd5xvhwwbrjO6L7/RK6Z8shBwOSc41s=" - -before_install: - - export CARGO_TARGET_DIR="$TRAVIS_BUILD_DIR/target" - - | - case "$TRAVIS_OS_NAME" in - linux ) HOST=x86_64-unknown-linux-gnu;; - osx ) HOST=x86_64-apple-darwin;; - windows ) HOST=x86_64-pc-windows-msvc;; - esac - - curl -sSL https://sh.rustup.rs | sh -s -- -y --default-host="$HOST" --default-toolchain=nightly --profile=minimal - - export PATH="$HOME/.cargo/bin:$PATH" -install: - - | - if [[ -z ${INTEGRATION} ]]; then - if ! rustup component add rustfmt; then - TARGET=$(rustc -Vv | awk '/host/{print $2}') - NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/rustfmt") - curl -sSL "https://static.rust-lang.org/dist/${NIGHTLY}/rustfmt-nightly-${TARGET}.tar.xz" | \ - tar -xJf - --strip-components=3 -C ~/.cargo/bin - rm -rf ~/.cargo/bin/doc - fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - . $HOME/.nvm/nvm.sh - nvm install stable - nvm use stable - npm install remark-cli remark-lint - elif [[ "$TRAVIS_OS_NAME" == "windows" ]]; then - choco install windows-sdk-10.1 - fi - fi - -# disabling the integration tests in forks should be done with -# if: fork = false -# but this is currently buggy travis-ci/travis-ci#9118 -matrix: - fast_finish: true - include: - # Builds that are executed for every PR - - os: linux - # i686 toolchain could run on x86_64 system. - - os: linux - env: HOST_TOOLCHAIN=i686-unknown-linux-gnu - addons: - apt: - packages: - - gcc-multilib - - libssl-dev:i386 # openssl dev in Cargo.toml - if: branch IN (auto, try) - - os: windows - env: CARGO_INCREMENTAL=0 OS_WINDOWS=true - - # Builds that are only executed when a PR is r+ed or a try build is started - # We don't want to run these always because they go towards - # the build limit within the Travis rust-lang account. - # The jobs are approximately sorted by execution time - - os: osx - if: branch IN (auto, try) - - env: INTEGRATION=rust-lang/rls - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/cargo - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/chalk - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=Geal/nom - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/rustfmt - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=hyperium/hyper - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-itertools/itertools - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - # FIXME: rustc ICE on `serde_test_suite` - # - env: INTEGRATION=serde-rs/serde - # if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/stdarch - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-random/rand - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/futures-rs - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=Marwes/combine - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang-nursery/failure - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=rust-lang/log - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - - env: INTEGRATION=chronotope/chrono - if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) - allow_failures: - - os: windows - env: CARGO_INCREMENTAL=0 OS_WINDOWS=true - -before_script: - - | - if [[ "$TRAVIS_BRANCH" == "auto" ]] || [[ "$TRAVIS_BRANCH" == "try" ]]; then - PR=$(echo "$TRAVIS_COMMIT_MESSAGE" | grep -o "#[0-9]*" | head -1 | sed 's/^#//g') - output=$(curl -H "Authorization: token $GITHUB_API_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 - fi - - | - rm rust-toolchain - ./setup-toolchain.sh - - | - SYSROOT=$(rustc --print sysroot) - case "$TRAVIS_OS_NAME" in - windows ) export PATH="${SYSROOT}/bin:${PATH}" ;; - linux ) export LD_LIBRARY_PATH="${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" ;; - osx ) - # See - sudo mkdir -p /usr/local/lib - sudo find "$SYSROOT/lib" -maxdepth 1 -name '*.dylib' -exec ln -s {} /usr/local/lib \; - ;; - esac - -script: - - | - if [[ -n ${INTEGRATION} ]]; then - cargo test --test integration --features integration && sleep 5 - else - ./ci/base-tests.sh && sleep 5 - fi - -after_success: - - | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - set -e - if [[ -z ${INTEGRATION} ]]; then - ./.github/deploy.sh - else - echo "Not deploying, because we're in an integration test run" - fi - set +e - fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 0953432bfad7..87d6339d6032 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,136 @@ document. ## Unreleased / In Rust Beta or Nightly -[69f99e7...master](https://github.com/rust-lang/rust-clippy/compare/69f99e7...master) +[329923e...master](https://github.com/rust-lang/rust-clippy/compare/329923e...master) + +## Rust 1.43 + +Current beta, release 2020-04-23 + +[4ee1206...329923e](https://github.com/rust-lang/rust-clippy/compare/4ee1206...329923e) + +### 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 -Current Beta +Current stable, 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 -Current stable, released 2020-01-30 +Released 2020-01-30 [c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7) @@ -203,29 +324,29 @@ 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_fn_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) +* 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 @@ -234,27 +355,27 @@ 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) +* 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 @@ -551,7 +672,7 @@ Released 2018-09-13 ## 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`] +* Removed `unit_expr` * Various false positive fixes for [`needless_pass_by_value`] ## 0.0.180 @@ -597,11 +718,13 @@ Released 2018-09-13 ## 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`] +* 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`] +* 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) @@ -636,7 +759,7 @@ Released 2018-09-13 ## 0.0.157 - 2017-09-04 * Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* -* New lint: [`unit_expr`] +* New lint: `unit_expr` ## 0.0.156 - 2017-09-03 * Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* @@ -884,7 +1007,7 @@ Released 2018-09-13 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`]. + through `cargo clippy`. ## 0.0.87 — 2016-08-31 * Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* @@ -897,7 +1020,7 @@ Released 2018-09-13 ## 0.0.85 — 2016-08-19 * Fix ICE with [`useless_attribute`] -* [`useless_attribute`] ignores [`unused_imports`] on `use` statements +* [`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)* @@ -936,7 +1059,7 @@ Released 2018-09-13 ## 0.0.77 — 2016-06-21 * Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* -* New lints: [`stutter`] and [`iter_nth`] +* New lints: `stutter` and [`iter_nth`] ## 0.0.76 — 2016-06-10 * Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* @@ -1056,6 +1179,7 @@ Released 2018-09-13 [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 + [`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 @@ -1166,6 +1290,7 @@ Released 2018-09-13 [`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 +[`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 @@ -1204,9 +1329,10 @@ Released 2018-09-13 [`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_mul_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_mul_add [`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 @@ -1276,6 +1402,7 @@ Released 2018-09-13 [`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 @@ -1316,6 +1443,7 @@ Released 2018-09-13 [`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_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 @@ -1344,6 +1472,7 @@ Released 2018-09-13 [`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 @@ -1409,11 +1538,13 @@ Released 2018-09-13 [`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 [`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 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d70b2b52aca1..dec13e44a17f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,35 +6,65 @@ A version of this document [can be found online](https://www.rust-lang.org/condu **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. +* 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 Citizen Code of Conduct; 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. +* 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 Citizen + Code of Conduct; 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]. +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.) +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. +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. +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. +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. +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/).* +*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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a828051185f..b1f9be44b73d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,15 @@ 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. +**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` IRC channel on `irc.mozilla.org` or meet us in `#clippy` on [Discord](https://discord.gg/rust-lang). +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](http://www.rust-lang.org/conduct.html). +All contributors are expected to follow the [Rust Code of Conduct]. * [Getting started](#getting-started) * [Finding something to fix/improve](#finding-something-to-fiximprove) @@ -20,68 +21,81 @@ All contributors are expected to follow the [Rust Code of Conduct](http://www.ru * [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. Run `cargo test` in the root directory and wiggle code until it passes -4. Open a PR (also can be done between 2. and 3. if you run into problems) +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, @llogiq, @mcarton or @oli-obk. -Some issues are easier than others. The [`good first issue`](https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue) -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! +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`](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed). +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`](https://github.com/rust-lang/rust-clippy/labels/T-AST) involve simple -matching of the syntax tree structure, and are generally easier than -[`T-middle`](https://github.com/rust-lang/rust-clippy/labels/T-middle) issues, which involve types +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`](https://github.com/rust-lang/rust-clippy/labels/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](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast). Usually -the lint will end up to be a nested series of matches and ifs, -[like so](https://github.com/rust-lang/rust-clippy/blob/de5ccdfab68a5e37689f3c950ed1532ba9d652a0/src/misc.rs#L34). +[`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`](https://github.com/rust-lang/rust-clippy/labels/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. +[`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`](https://github.com/rust-lang/rust-clippy/labels/T-middle) issues can -be more involved and require verifying types. The -[`ty`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty) module contains a +[`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/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](doc/adding_lints.md) for more details. [Llogiq's blog post on lints](https://llogiq.github.io/2015/06/04/workflows.html) is also a nice primer -to lint-writing, though it does get into advanced stuff and may be a bit -outdated. +Have a look at the [docs for writing lints][adding_lints] for more details. [Llogiq's blog post on lints] +is also a nice primer to lint-writing, though it does get into advanced stuff and may be a bit outdated. 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. -## How Clippy works +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[Llogiq's blog post on lints]: https://llogiq.github.io/2015/06/04/workflows.html +[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 -Clippy is a [rustc compiler plugin][compiler_plugin]. The main entry point is at [`src/lib.rs`][main_entry]. In there, the lint registration is delegated to the [`clippy_lints`][lint_crate] crate. +## How Clippy works -[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers them with the rustc plugin registry. For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this: +[`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 @@ -90,27 +104,29 @@ Clippy is a [rustc compiler plugin][compiler_plugin]. The main entry point is at pub mod else_if_without_else; // ... -pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry) { +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { // ... - reg.register_early_lint_pass(box else_if_without_else::ElseIfWithoutElse); + store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); // ... - reg.register_lint_group("clippy::restriction", vec![ + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ // ... - else_if_without_else::ELSE_IF_WITHOUT_ELSE, + LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), // ... ]); } ``` -The [`plugin::PluginRegistry`][plugin_registry] provides two methods to register lints: [register_early_lint_pass][reg_early_lint_pass] and [register_late_lint_pass][reg_late_lint_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` and you don't have to add anything by hand. When you are writing your own lint, you can use that script to save you some time. +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, LintArray, LintPass}; +use rustc_lint::{EarlyLintPass, EarlyContext}; // ... @@ -123,18 +139,34 @@ impl EarlyLintPass for ElseIfWithoutElse { } ``` -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. +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. -That's why the `else_if_without_else` example uses the `register_early_lint_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. +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. +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. +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]. @@ -143,14 +175,8 @@ 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 use [rustup-toolchain-install-master][rtim] to do that: - -```bash -cargo install rustup-toolchain-install-master -rustup-toolchain-install-master --force -n master -c rustc-dev -rustup override set master -cargo test -``` +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. @@ -167,6 +193,12 @@ git commit -m "Update Clippy" # 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 @@ -191,6 +223,12 @@ 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/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 @@ -198,32 +236,9 @@ Contributions to Clippy should be made in the form of GitHub pull requests. Each 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](http://www.apache.org/licenses/LICENSE-2.0>) -or the [MIT](http://opensource.org/licenses/MIT) license. +All code in this repository is under the [Apache-2.0] or the [MIT] license. -[main_entry]: https://github.com/rust-lang/rust-clippy/blob/c5b39a5917ffc0f1349b6e414fa3b874fdcf8429/src/lib.rs#L14 -[lint_crate]: https://github.com/rust-lang/rust-clippy/tree/c5b39a5917ffc0f1349b6e414fa3b874fdcf8429/clippy_lints/src -[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/c5b39a5917ffc0f1349b6e414fa3b874fdcf8429/clippy_lints/src/lib.rs -[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/c5b39a5917ffc0f1349b6e414fa3b874fdcf8429/clippy_lints/src/else_if_without_else.rs -[compiler_plugin]: https://doc.rust-lang.org/unstable-book/language-features/plugin.html#lint-plugins -[plugin_registry]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_plugin_impl/registry/struct.Registry.html -[reg_early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_plugin_impl/registry/struct.Registry.html#method.register_early_lint_pass -[reg_late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_plugin_impl/registry/struct.Registry.html#method.register_late_lint_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 -[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master -[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/6ce0459f6bfa7c528ae1886492a3e0b5ef0ee547 -[rtim]: https://github.com/kennytm/rustup-toolchain-install-master -[rustup_component_history]: https://mexus.github.io/rustup-components-history -[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 -[triage]: https://forge.rust-lang.org/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/servo/homu -[homu_instructions]: https://buildbot2.rust-lang.org/homu/ -[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy -[`rust-lang/rust`]: https://github.com/rust-lang/rust +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0 +[MIT]: https://opensource.org/licenses/MIT diff --git a/Cargo.toml b/Cargo.toml index 84b96a329140..8421670a1a46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,8 @@ build = "build.rs" edition = "2018" publish = false -[badges] -travis-ci = { repository = "rust-lang/rust-clippy" } -appveyor = { repository = "rust-lang/rust-clippy" } +# [badges] +# FIXME(flip1995): Add GHA badge once rust-lang/crates.io#1838 is merged [[bin]] name = "cargo-clippy" @@ -38,7 +37,6 @@ clippy_lints = { version = "0.0.212", path = "clippy_lints" } regex = "1" semver = "0.9" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} -git2 = { version = "0.11", optional = true } tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" @@ -61,4 +59,4 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} [features] deny-warnings = [] -integration = ["git2", "tempfile"] +integration = ["tempfile"] diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 16fe87b06e80..d821a4de2bed 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +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. diff --git a/LICENSE-MIT b/LICENSE-MIT index 31aa79387f27..b7c70dd4026d 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,3 +1,7 @@ +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 diff --git a/README.md b/README.md index 96584dfef5c6..2181c296e9b2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Clippy -[![Build Status](https://travis-ci.com/rust-lang/rust-clippy.svg?branch=master)](https://travis-ci.com/rust-lang/rust-clippy) -[![Windows Build status](https://ci.appveyor.com/api/projects/status/id677xpw1dguo7iw?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/rust-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 354 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 361 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: @@ -30,7 +29,9 @@ Only the following of those categories are enabled by default: 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. +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: @@ -140,15 +141,16 @@ line. (You can swap `clippy::all` with the specific lint category you are target ## Configuration -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = value` mapping eg. +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. +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. @@ -169,7 +171,10 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: 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 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` ## Contributing @@ -177,7 +182,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT ## License -Copyright 2014-2019 The Rust Project Developers +Copyright 2014-2020 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index bf6133e98a06..000000000000 --- a/appveyor.yml +++ /dev/null @@ -1,47 +0,0 @@ -environment: - global: - PROJECT_NAME: rust-clippy - RUST_BACKTRACE: 1 - matrix: - #- TARGET: i686-pc-windows-gnu - #- TARGET: i686-pc-windows-msvc - #- TARGET: x86_64-pc-windows-gnu - - TARGET: x86_64-pc-windows-msvc - -branches: - # Only build AppVeyor on r+ and try branch - only: - - auto - - try - -cache: - - '%USERPROFILE%\.cargo' -# before cache -after_test: - - cargo install cargo-cache --debug - - cargo cache --autoclean - -install: - - curl -sSf -o rustup-init.exe https://win.rustup.rs/ - - rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly --profile=minimal - - set PATH=%USERPROFILE%\.cargo\bin;%PATH% - - rustup component add rustfmt --toolchain nightly & exit 0 # Format test handles missing rustfmt - - del rust-toolchain - - cargo install rustup-toolchain-install-master - - rustup-toolchain-install-master -f -n master -c rustc-dev - - rustup override set master - - rustc -V - - cargo -V - -# Build settings, not to be confused with "before_build" and "after_build". -build: false - -build_script: - - cargo build --features deny-warnings - -test_script: - - cargo test --features deny-warnings - -notifications: - - provider: Email - on_build_success: false diff --git a/ci/base-tests.sh b/ci/base-tests.sh deleted file mode 100755 index 125a566271d0..000000000000 --- a/ci/base-tests.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -set -ex - -echo "Running clippy base tests" - -PATH=$PATH:./node_modules/.bin -if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - remark -f ./*.md -f doc/*.md > /dev/null -fi -# build clippy in debug mode and run tests -cargo build --features deny-warnings -cargo test --features deny-warnings - -(cd clippy_lints && cargo test --features deny-warnings) -(cd rustc_tools_util && cargo test --features deny-warnings) -(cd clippy_dev && cargo test --features deny-warnings) - -# make sure clippy can be called via ./path/to/cargo-clippy -( - cd clippy_workspace_tests - ../target/debug/cargo-clippy -) - -# Perform various checks for lint registration -cargo dev update_lints --check -cargo dev --limit-stderr-length - -# Check running clippy-driver without cargo -( - # Check sysroot handling - sysroot=$(./target/debug/clippy-driver --print sysroot) - test "$sysroot" = "$(rustc --print sysroot)" - - if [[ -z "$OS_WINDOWS" ]]; then - desired_sysroot=/tmp - else - desired_sysroot=C:/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 -) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index c58b80e4318e..6fe7bb155ac5 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -6,7 +6,6 @@ use regex::Regex; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::io::prelude::*; use std::path::{Path, PathBuf}; use walkdir::WalkDir; @@ -31,9 +30,10 @@ lazy_static! { ) .unwrap(); static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap(); - pub static ref DOCS_LINK: String = "https://rust-lang.github.io/rust-clippy/master/index.html".to_string(); } +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 { @@ -61,13 +61,15 @@ impl Lint { lints.filter(|l| l.deprecation.is_none() && !l.is_internal()) } + /// Returns all internal lints (not `internal_warn` lints) + pub fn internal_lints(lints: impl Iterator) -> impl Iterator { + lints.filter(|l| l.group == "internal") + } + /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] - pub fn by_lint_group(lints: &[Self]) -> HashMap> { - lints - .iter() - .map(|lint| (lint.group.to_string(), lint.clone())) - .into_group_map() + pub fn by_lint_group(lints: impl Iterator) -> HashMap> { + lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() } #[must_use] @@ -82,7 +84,7 @@ pub fn gen_lint_group_list(lints: Vec) -> Vec { lints .into_iter() .filter_map(|l| { - if l.is_internal() || l.deprecation.is_some() { + if l.deprecation.is_some() { None } else { Some(format!(" LintId::of(&{}::{}),", l.module, l.name.to_uppercase())) @@ -121,7 +123,7 @@ pub fn gen_changelog_lint_list(lints: Vec) -> Vec { if l.is_internal() { None } else { - Some(format!("[`{}`]: {}#{}", l.name, DOCS_LINK.clone(), l.name)) + Some(format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name)) } }) .collect() @@ -172,32 +174,35 @@ pub fn gather_all() -> impl Iterator { } fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator { - let mut file = fs::File::open(dir_entry.path()).unwrap(); - let mut content = String::new(); - file.read_to_string(&mut content).unwrap(); - let mut filename = dir_entry.path().file_stem().unwrap().to_str().unwrap(); + 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" { - filename = dir_entry - .path() - .parent() - .unwrap() - .file_stem() - .unwrap() - .to_str() - .unwrap() + rel_path = rel_path.parent().unwrap(); } - parse_contents(&content, filename) + + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + parse_contents(&content, &module) } -fn parse_contents(content: &str, filename: &str) -> impl Iterator { +fn parse_contents(content: &str, module: &str) -> impl Iterator { let lints = DEC_CLIPPY_LINT_RE .captures_iter(content) - .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, filename)); + .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"]), filename)); + .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module)); // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map lints.chain(deprecated).collect::>().into_iter() } @@ -209,7 +214,7 @@ fn lint_files() -> impl Iterator { let path = clippy_project_root().join("clippy_lints/src"); WalkDir::new(path) .into_iter() - .filter_map(std::result::Result::ok) + .filter_map(Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } @@ -225,7 +230,6 @@ pub struct FileChange { /// `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. -#[allow(clippy::expect_fun_call)] pub fn replace_region_in_file( path: &Path, start: &str, @@ -235,22 +239,15 @@ pub fn replace_region_in_file( replacements: F, ) -> FileChange where - F: Fn() -> Vec, + F: FnOnce() -> Vec, { - let path = clippy_project_root().join(path); - let mut f = fs::File::open(&path).expect(&format!("File not found: {}", path.to_string_lossy())); - let mut contents = String::new(); - f.read_to_string(&mut contents) - .expect("Something went wrong reading the file"); + 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 { - let mut f = fs::File::create(&path).expect(&format!("File not found: {}", path.to_string_lossy())); - f.write_all(file_change.new_lines.as_bytes()) - .expect("Unable to write file"); - // Ensure we write the changes with a trailing newline so that - // the file has the proper line endings. - f.write_all(b"\n").expect("Unable to write file"); + if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) { + panic!("Cannot write to {}: {}", path.display(), e); + } } file_change } @@ -273,31 +270,32 @@ where /// /// ``` /// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; -/// let result = clippy_dev::replace_region_in_text(the_text, r#"replace_start"#, r#"replace_end"#, false, || { -/// vec!["a different".to_string(), "text".to_string()] -/// }) -/// .new_lines; +/// 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(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange where - F: Fn() -> Vec, + F: FnOnce() -> Vec, { - let lines = text.lines(); + 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 lines.clone() { + for line in text.lines() { if in_old_region { - if end.is_match(&line) { + if end.is_match(line) { in_old_region = false; - new_lines.extend(replacements()); + new_lines.extend(replace_it.clone()); new_lines.push(line.to_string()); } - } else if start.is_match(&line) { + } else if start.is_match(line) { if !replace_start { new_lines.push(line.to_string()); } @@ -315,10 +313,12 @@ where eprintln!("error: regex `{:?}` not found. You may have to update it.", start); } - FileChange { - changed: lines.ne(new_lines.clone()), - new_lines: new_lines.join("\n"), + 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 @@ -456,7 +456,7 @@ fn test_by_lint_group() { "group2".to_string(), vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")], ); - assert_eq!(expected, Lint::by_lint_group(&lints)); + assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } #[test] @@ -529,10 +529,11 @@ fn test_gen_lint_group_list() { Lint::new("abc", "group1", "abc", None, "module_name"), Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), Lint::new("should_assert_eq2", "group2", "abc", Some("abc"), "deprecated"), - Lint::new("incorrect_internal", "internal_style", "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)); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 7369db1f078d..901e663ded38 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,14 +1,17 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::*; +use clippy_dev::{ + 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; mod fmt; mod new_lint; mod stderr_length_check; -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] enum UpdateMode { Check, Change, @@ -113,9 +116,9 @@ fn main() { if matches.is_present("print-only") { print_lints(); } else if matches.is_present("check") { - update_lints(&UpdateMode::Check); + update_lints(UpdateMode::Check); } else { - update_lints(&UpdateMode::Change); + update_lints(UpdateMode::Change); } }, ("new_lint", Some(matches)) => { @@ -124,7 +127,7 @@ fn main() { matches.value_of("name"), matches.value_of("category"), ) { - Ok(_) => update_lints(&UpdateMode::Change), + Ok(_) => update_lints(UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), } }, @@ -135,8 +138,8 @@ fn main() { fn print_lints() { let lint_list = gather_all(); let usable_lints: Vec = Lint::usable_lints(lint_list).collect(); - let lint_count = usable_lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(&usable_lints); + 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" { @@ -150,22 +153,24 @@ fn print_lints() { println!( "* [{}]({}#{}) ({})", lint.name, - clippy_dev::DOCS_LINK.clone(), + clippy_dev::DOCS_LINK, lint.name, lint.desc ); } } - println!("there are {} lints", lint_count); + println!("there are {} lints", usable_lint_count); } #[allow(clippy::too_many_lines)] -fn update_lints(update_mode: &UpdateMode) { +fn update_lints(update_mode: UpdateMode) { let lint_list: Vec = gather_all().collect(); + let internal_lints = Lint::internal_lints(lint_list.clone().into_iter()); + let usable_lints: Vec = Lint::usable_lints(lint_list.clone().into_iter()).collect(); - let lint_count = usable_lints.len(); + let usable_lint_count = usable_lints.len(); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); @@ -175,7 +180,7 @@ fn update_lints(update_mode: &UpdateMode) { "begin lint list", "end lint list", false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || { format!( "pub const ALL_LINTS: [Lint; {}] = {:#?};", @@ -191,23 +196,25 @@ fn update_lints(update_mode: &UpdateMode) { file_change |= replace_region_in_file( Path::new("README.md"), - r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang.github.io/rust-clippy/master/index.html\)"#, + &format!(r#"\[There are \d+ lints included in this crate!\]\({}\)"#, DOCS_LINK), "", true, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || { - vec![ - format!("[There are {} lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)", lint_count) - ] - } - ).changed; + vec![format!( + "[There are {} lints included in this crate!]({})", + usable_lint_count, DOCS_LINK + )] + }, + ) + .changed; file_change |= replace_region_in_file( Path::new("CHANGELOG.md"), "", "", false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || gen_changelog_lint_list(lint_list.clone()), ) .changed; @@ -217,7 +224,7 @@ fn update_lints(update_mode: &UpdateMode) { "begin deprecated lints", "end deprecated lints", false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || gen_deprecated(&lint_list), ) .changed; @@ -227,7 +234,7 @@ fn update_lints(update_mode: &UpdateMode) { "begin register lints", "end register lints", false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || gen_register_lint_list(&lint_list), ) .changed; @@ -237,7 +244,7 @@ fn update_lints(update_mode: &UpdateMode) { "begin lints modules", "end lints modules", false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || gen_modules_list(lint_list.clone()), ) .changed; @@ -248,7 +255,7 @@ fn update_lints(update_mode: &UpdateMode) { r#"store.register_group\(true, "clippy::all""#, r#"\]\);"#, false, - update_mode == &UpdateMode::Change, + update_mode == UpdateMode::Change, || { // clippy::all should only include the following lint groups: let all_group_lints = usable_lints @@ -265,19 +272,19 @@ fn update_lints(update_mode: &UpdateMode) { .changed; // Generate the list of lints for all other lint groups - for (lint_group, lints) in Lint::by_lint_group(&usable_lints) { + 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, + update_mode == UpdateMode::Change, || gen_lint_group_list(lints.clone()), ) .changed; } - if update_mode == &UpdateMode::Check && file_change { + 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." diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 9d1f19f954b0..9e2a4617cde5 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -13,7 +13,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str 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 syntax::ast::*;", "EarlyContext"), + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), _ => { unreachable!("`pass_type` should only ever be `early` or `late`!"); @@ -117,7 +117,7 @@ fn get_lint_file_contents( context_import: &str, ) -> String { format!( - "use rustc_lint::{{LintArray, LintPass, {type}, {context_import}}}; + "use rustc_lint::{{{type}, {context_import}}}; use rustc_session::{{declare_lint_pass, declare_tool_lint}}; {pass_import} @@ -131,7 +131,11 @@ declare_clippy_lint! {{ /// **Example:** /// /// ```rust - /// // example code + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning /// ``` pub {name_upper}, {category}, diff --git a/clippy_dev/src/stderr_length_check.rs b/clippy_dev/src/stderr_length_check.rs index 041ee6911377..c511733f7bf4 100644 --- a/clippy_dev/src/stderr_length_check.rs +++ b/clippy_dev/src/stderr_length_check.rs @@ -4,6 +4,8 @@ use std::path::{Path, PathBuf}; use walkdir::WalkDir; +use clippy_dev::clippy_project_root; + // The maximum length allowed for stderr files. // // We limit this because small files are easier to deal with than bigger files. @@ -14,22 +16,24 @@ pub fn check() { if !exceeding_files.is_empty() { eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT); - for path in exceeding_files { - println!("{}", path.display()); + for (path, count) in exceeding_files { + println!("{}: {}", path.display(), count); } std::process::exit(1); } } -fn exceeding_stderr_files() -> Vec { +fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> { // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - WalkDir::new("../tests/ui") + 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(); - if p.extension() == Some(OsStr::new("stderr")) && count_linenumbers(&p) > LENGTH_LIMIT { - Some(p) + let count = count_linenumbers(&p); + if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT { + Some((p, count)) } else { None } diff --git a/clippy_dummy/PUBLISH.md b/clippy_dummy/PUBLISH.md index 535f11bd2ee4..8e420ec959a2 100644 --- a/clippy_dummy/PUBLISH.md +++ b/clippy_dummy/PUBLISH.md @@ -1,4 +1,6 @@ -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. +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. - \ No newline at end of file +Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`, +it has a different name to avoid workspace issues. diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index dae403cbb4f1..99eebcdd3d59 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -19,10 +19,10 @@ edition = "2018" [dependencies] cargo_metadata = "0.9.0" if_chain = "1.0.0" -itertools = "0.8" +itertools = "0.9" lazy_static = "1.0.2" matches = "0.1.7" -pulldown-cmark = "0.6.0" +pulldown-cmark = { version = "0.7", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } diff --git a/clippy_lints/README.md b/clippy_lints/README.md index 2724aa6a0523..513583b7e349 100644 --- a/clippy_lints/README.md +++ b/clippy_lints/README.md @@ -1,3 +1 @@ -This crate contains Clippy lints. For the main crate, check -[*crates.io*](https://crates.io/crates/clippy) or -[GitHub](https://github.com/rust-lang/rust-clippy). +This crate contains Clippy lints. For the main crate, check [GitHub](https://github.com/rust-lang/rust-clippy). diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 123a0a9d1894..7e6e2c7eaebe 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,10 +1,10 @@ use crate::utils::span_lint; -use rustc_hir::*; +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; -use syntax::ast::{FloatTy, LitFloatType, LitKind}; declare_clippy_lint! { /// **What it does:** Checks for floating point literals that approximate @@ -23,11 +23,11 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// // Bad /// let x = 3.14; /// let y = 1_f64 / x; - /// - /// // Good + /// ``` + /// Use predefined constants instead: + /// ```rust /// let x = std::f32::consts::PI; /// let y = std::f64::consts::FRAC_1_PI; /// ``` @@ -37,7 +37,7 @@ declare_clippy_lint! { } // Tuples are of the form (constant, name, min_digits) -const KNOWN_CONSTS: [(f64, &str, usize); 16] = [ +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), @@ -52,6 +52,8 @@ const KNOWN_CONSTS: [(f64, &str, usize); 16] = [ (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), ]; diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index fd252b9ee109..a138c9d3545c 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -81,11 +81,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic { | hir::BinOpKind::Gt => return, _ => (), } + let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r)); - if l_ty.is_integral() && r_ty.is_integral() { + 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.is_floating_point() && r_ty.is_floating_point() { + } 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); } diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index d90a0d67d5a5..1ec3377193ec 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -1,7 +1,7 @@ use rustc::lint::in_external_macro; +use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::*; use crate::utils::span_lint_and_help; diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index e399229b43ca..e2fe5f2400f4 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -2,25 +2,23 @@ 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_hir::*; +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}; -use syntax::ast::LitKind; 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!() + /// `panic!()` or `unreachable!()` /// /// **Known problems:** None /// /// **Example:** /// ```rust,ignore /// assert!(false) - /// // or /// assert!(true) - /// // or /// const B: bool = false; /// assert!(B) /// ``` diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 7ad0ce232023..b6fb6fdd2cc0 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -231,7 +231,9 @@ fn lint_misrefactored_assign_op( #[must_use] fn is_commutative(op: hir::BinOpKind) -> bool { - use rustc_hir::BinOpKind::*; + 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, @@ -254,7 +256,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 178ee45298e3..871ca029b1aa 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -2,7 +2,7 @@ use crate::utils::{match_def_path, span_lint_and_help}; use if_chain::if_chain; use rustc::ty; use rustc_hir::def_id::DefId; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 515e5af339bc..35cde05e7f57 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,22 +1,24 @@ //! checks for attributes -use crate::reexport::*; +use crate::reexport::Name; use crate::utils::{ - is_present_in_source, last_line_of_span, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg, + 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::lint::in_external_macro; use rustc::ty; +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::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; use semver::Version; -use syntax::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; -use syntax::util::lev_distance::find_best_match_for_name; declare_clippy_lint! { /// **What it does:** Checks for items annotated with `#[inline(always)]`, @@ -246,6 +248,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes { if is_word(lint, sym!(unused_imports)) || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) + || is_word(lint, sym!(unused)) { return; } @@ -261,7 +264,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes { _ => {}, } } - let line_span = last_line_of_span(cx, attr.span); + let line_span = first_line_of_span(cx, attr.span); if let Some(mut sugg) = snippet_opt(cx, line_span) { if sugg.contains("#[") { @@ -369,15 +372,15 @@ fn is_relevant_item(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { fn is_relevant_impl(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) -> bool { match item.kind { - ImplItemKind::Method(_, eid) => is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value), + 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::Method(_, TraitMethod::Required(_)) => true, - TraitItemKind::Method(_, TraitMethod::Provided(eid)) => { + 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, diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 926b1c63ad12..780535423499 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -2,12 +2,12 @@ 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::*; +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; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for incompatible bit masks in comparisons. diff --git a/clippy_lints/src/blacklisted_name.rs b/clippy_lints/src/blacklisted_name.rs index cb68ba598868..6f26f031431d 100644 --- a/clippy_lints/src/blacklisted_name.rs +++ b/clippy_lints/src/blacklisted_name.rs @@ -1,6 +1,6 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::*; +use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs index 5a64e444136e..50c1262f2d6d 100644 --- a/clippy_lints/src/block_in_if_condition.rs +++ b/clippy_lints/src/block_in_if_condition.rs @@ -1,9 +1,10 @@ -use crate::utils::*; +use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; use matches::matches; use rustc::hir::map::Map; use rustc::lint::in_external_macro; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,7 +35,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```ignore + /// ```rust,ignore /// if { let x = somefunc(); x } {} /// // or /// if somefunc(|x| { x == 47 }) {} @@ -65,7 +66,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { } walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -79,8 +80,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { if in_external_macro(cx.sess(), expr.span) { return; } - if let Some((check, then, _)) = higher::if_block(&expr) { - if let ExprKind::Block(block, _) = &check.kind { + 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 { @@ -89,16 +90,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { return; } - span_lint_and_help( + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( cx, BLOCK_IN_IF_CONDITION_EXPR, - check.span, + cond.span, BRACED_EXPR_MESSAGE, - &format!( - "try\nif {} {} ... ", - snippet_block(cx, ex.span, ".."), - snippet_block(cx, then.span, "..") + "try", + format!( + "{}", + snippet_block_with_applicability( + cx, + ex.span, + "..", + Some(expr.span), + &mut applicability + ) ), + applicability, ); } } else { @@ -107,22 +116,30 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { return; } // move block higher - span_lint_and_help( + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( cx, BLOCK_IN_IF_CONDITION_STMT, - check.span, + expr.span.with_hi(cond.span.hi()), COMPLEX_BLOCK_MESSAGE, - &format!( - "try\nlet res = {};\nif res {} ... ", - snippet_block(cx, block.span, ".."), - snippet_block(cx, then.span, "..") + "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, check); + 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); } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 60e62542a725..b580240be175 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -4,14 +4,13 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::hir::map::Map; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::intravisit; -use rustc_hir::intravisit::*; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for boolean expressions that can be written more @@ -60,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool { fn check_fn( &mut self, cx: &LateContext<'a, 'tcx>, - _: intravisit::FnKind<'tcx>, + _: FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, @@ -162,7 +161,7 @@ struct SuggestContext<'a, 'tcx, 'v> { impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { fn recurse(&mut self, suggestion: &Bool) -> Option<()> { - use quine_mc_cluskey::Bool::*; + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; match suggestion { True => { self.output.push_str("true"); @@ -278,7 +277,7 @@ fn suggest(cx: &LateContext<'_, '_>, suggestion: &Bool, terminals: &[&Expr<'_>]) } fn simple_negate(b: Bool) -> Bool { - use quine_mc_cluskey::Bool::*; + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; match b { True => False, False => True, @@ -326,7 +325,7 @@ fn terminal_stats(b: &Bool) -> Stats { &Term(n) => stats.terminals[n as usize] += 1, } } - use quine_mc_cluskey::Bool::*; + use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True}; let mut stats = Stats::default(); recurse(b, &mut stats); stats @@ -359,7 +358,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } simplified.push(simple_negated); } - let mut improvements = Vec::new(); + let mut improvements = Vec::with_capacity(simplified.len()); 'simplified: for suggestion in &simplified { let simplified_stats = terminal_stats(suggestion); let mut improvement = false; @@ -458,7 +457,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { _ => walk_expr(self, e), } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -492,7 +491,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 2adc3b429067..9c06eb962d7b 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -4,11 +4,11 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::ty; +use rustc_ast::ast::{Name, UintTy}; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::{Name, UintTy}; declare_clippy_lint! { /// **What it does:** Checks for naive byte counts diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index ec53e08f50cd..06e866dd72a0 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -3,10 +3,10 @@ use std::path::PathBuf; use crate::utils::span_lint; +use rustc_ast::ast::Crate; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks to see if all common metadata is defined in diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 88df0772ae5e..23992ae89a70 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,11 +2,11 @@ use if_chain::if_chain; use rustc::lint::in_external_macro; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::LitKind; use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 2b6315255016..fcab45901884 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,13 +1,13 @@ //! calculate cognitive complexity and warn about overly complex functions use rustc::hir::map::Map; +use rustc_ast::ast::Attribute; use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use syntax::ast::Attribute; use crate::utils::{match_type, paths, snippet_opt, span_lint_and_help, LimitStack}; @@ -156,7 +156,7 @@ impl<'tcx> Visitor<'tcx> for CCHelper { _ => {}, } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index af309b512699..25fb773a6d67 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -13,9 +13,9 @@ //! 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 syntax::ast; use crate::utils::sugg::Sugg; use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then}; @@ -95,7 +95,7 @@ fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { 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, "..") + 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("/*") @@ -116,7 +116,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { block.span, "this `else { if .. }` block can be collapsed", "try", - snippet_block_with_applicability(cx, else_.span, "..", &mut applicability).into_owned(), + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), applicability, ); } @@ -146,7 +146,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & format!( "if {} {}", lhs.and(&rhs), - snippet_block(cx, content.span, ".."), + snippet_block(cx, content.span, "..", Some(expr.span)), ), Applicability::MachineApplicable, // snippet ); diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 684d3448ea43..e0c381ddbfc4 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ 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::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 211625aafbf8..fc26755a3754 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -5,16 +5,15 @@ use if_chain::if_chain; use rustc::ty::subst::{Subst, SubstsRef}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::{bug, span_bug}; +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; -use std::cmp::PartialOrd; use std::convert::TryInto; use std::hash::{Hash, Hasher}; -use syntax::ast::{FloatTy, LitKind}; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] @@ -153,8 +152,6 @@ impl Constant { /// Parses a `LitKind` to a `Constant`. pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { - use syntax::ast::*; - match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -227,14 +224,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { return self.ifthenelse(cond, then, otherwise); } match e.kind { - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id), + 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.eval_usize(self.lcx.tcx, self.lcx.param_env), + 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)) @@ -278,7 +275,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { #[allow(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::*; + use self::Constant::{Bool, Int}; match *o { Bool(b) => Some(Bool(!b)), Int(value) => { @@ -294,7 +291,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { } fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::*; + use self::Constant::{Int, F32, F64}; match *o { Int(value) => { let ity = match ty.kind { @@ -320,10 +317,10 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { } /// Lookup a possibly constant expression from a `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId) -> Option { + fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'cc>) -> Option { let res = self.tables.qpath_res(qpath, id); match res { - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { let substs = self.tables.node_substs(id); let substs = if self.substs.is_empty() { substs @@ -335,7 +332,8 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { .lcx .tcx .const_eval_resolve(self.param_env, def_id, substs, None, None) - .ok()?; + .ok() + .map(|val| rustc::ty::Const::from_value(self.lcx.tcx, val, ty))?; let result = miri_to_const(&result); if result.is_some() { self.needed_resolution = true; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d2d20375dafa..996f80a757e7 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -2,7 +2,7 @@ use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span use crate::utils::{SpanlessEq, SpanlessHash}; use rustc::ty::Ty; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::*; +use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index eb785e4c3cb2..f9bf1141a8b2 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,10 +1,10 @@ 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; -use syntax::ast; -use syntax::tokenstream::TokenStream; declare_clippy_lint! { /// **What it does:** Checks for usage of dbg!() macro. @@ -30,7 +30,7 @@ declare_clippy_lint! { declare_lint_pass!(DbgMacro => [DBG_MACRO]); impl EarlyLintPass for DbgMacro { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) { + 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( diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 0e1380df9216..f99e6d96e030 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -1,7 +1,7 @@ use if_chain::if_chain; use rustc::ty; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index efb44ab6dbb9..f130a69778f6 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -2,7 +2,7 @@ use crate::utils::paths; use crate::utils::{is_automatically_derived, is_copy, match_path, span_lint_and_then}; use if_chain::if_chain; use rustc::ty::{self, Ty}; -use rustc_hir::*; +use rustc_hir::{Item, ItemKind, TraitRef}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 5db1886bb52d..63b8d519c9d0 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,6 +1,9 @@ -use crate::utils::{is_entrypoint_fn, match_type, paths, return_ty, span_lint}; +use crate::utils::{implements_trait, is_entrypoint_fn, match_type, paths, return_ty, span_lint}; +use if_chain::if_chain; use itertools::Itertools; use rustc::lint::in_external_macro; +use rustc::ty; +use rustc_ast::ast::{AttrKind, Attribute}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -8,7 +11,6 @@ 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 syntax::ast::{AttrKind, Attribute}; use url::Url; declare_clippy_lint! { @@ -146,17 +148,17 @@ impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS 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.attrs); + 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, ..) => { + hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id)) || in_external_macro(cx.tcx.sess, item.span)) { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); } }, hir::ItemKind::Impl { @@ -177,9 +179,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown { 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::Method(ref sig, ..) = item.kind { + 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); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None); } } } @@ -189,8 +191,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown { if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } - if let hir::ImplItemKind::Method(ref sig, ..) = item.kind { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers); + 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)); } } } @@ -201,6 +203,7 @@ fn lint_for_missing_headers<'a, 'tcx>( span: impl Into + Copy, sig: &hir::FnSig<'_>, headers: DocHeaders, + body_id: Option, ) { if !cx.access_levels.is_exported(hir_id) { return; // Private functions do not require doc comments @@ -213,20 +216,43 @@ fn lint_for_missing_headers<'a, 'tcx>( "unsafe function's docs miss `# Safety` section", ); } - if !headers.errors && match_type(cx, return_ty(cx, hir_id), &paths::RESULT) { - span_lint( - cx, - MISSING_ERRORS_DOC, - span, - "docs for function returning `Result` missing `# Errors` section", - ); + if !headers.errors { + if match_type(cx, return_ty(cx, hir_id), &paths::RESULT) { + 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); + 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 match_type(cx, subs.as_generator().return_ty(), &paths::RESULT); + 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 `syntax::attr::AttributeMethods::with_desugared_doc` or -/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we +/// 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)] @@ -324,7 +350,7 @@ fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet, a 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::*; + use pulldown_cmark::Event::Text; let previous_range = previous.1; let current_range = current.1; @@ -341,6 +367,8 @@ fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet, a 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, Range)>>( cx: &LateContext<'_, '_>, valid_idents: &FxHashSet, @@ -348,8 +376,11 @@ fn check_doc<'a, Events: Iterator, Range DocHeaders { // true if a safety header was found - use pulldown_cmark::Event::*; - use pulldown_cmark::Tag::*; + 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, @@ -358,11 +389,20 @@ fn check_doc<'a, Events: Iterator, Range in_code = true, - End(CodeBlock(_)) => in_code = false, + 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, @@ -385,7 +425,9 @@ fn check_doc<'a, Events: Iterator, Range, Range, text: &str, span: Span) { if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 934e3ccdf87c..44f85d1ea6e1 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -1,7 +1,7 @@ //! Lint on unnecessary double comparisons. Some examples: use rustc_errors::Applicability; -use rustc_hir::*; +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; diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index d08981e8badc..7f2ff8b9b26f 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,7 +1,7 @@ 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}; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for unnecessary double parentheses. diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 1d445a153231..f49668082791 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -1,6 +1,6 @@ use crate::utils::{match_def_path, paths, span_lint}; use if_chain::if_chain; -use rustc_hir::*; +use rustc_hir::{GenericBound, GenericParam, WhereBoundPredicate, WherePredicate}; use rustc_lint::LateLintPass; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 29351deea286..bd9bdfef090b 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,7 +1,7 @@ use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note}; use if_chain::if_chain; use rustc::ty; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 366996fb381b..b35a8facf8b9 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -1,6 +1,6 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +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; diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index b87900180f26..ccaa35350cc2 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -1,9 +1,9 @@ //! Lint on if expressions with an else if, but without a final else branch. use rustc::lint::in_external_macro; +use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::*; use crate::utils::span_lint_and_help; diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index c43db68d20f2..010d208c9e79 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -1,7 +1,7 @@ //! lint when there is an enum with no variants use crate::utils::span_lint_and_then; -use rustc_hir::*; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index d32e78798afe..88647be02641 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc::hir::map::Map; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -181,7 +181,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { walk_expr(self, expr); } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 52ac1ff71250..5bc8a446cf0a 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -5,11 +5,11 @@ use crate::consts::{miri_to_const, Constant}; use crate::utils::span_lint; use rustc::ty; use rustc::ty::util::IntTypeExt; -use rustc_hir::*; +use rustc_ast::ast::{IntTy, UintTy}; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::convert::TryFrom; -use syntax::ast::{IntTy, UintTy}; declare_clippy_lint! { /// **What it does:** Checks for C-like enumerations that are @@ -46,9 +46,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant { 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 constant = cx.tcx.const_eval_poly(def_id).ok(); + let mut ty = cx.tcx.type_of(def_id); + let constant = cx + .tcx + .const_eval_poly(def_id) + .ok() + .map(|val| rustc::ty::Const::from_value(cx.tcx, val, ty)); if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { - let mut ty = cx.tcx.type_of(def_id); if let ty::Adt(adt, _) = ty.kind { if adt.is_enum() { ty = adt.repr.discr_type().to_ty(cx.tcx); diff --git a/clippy_lints/src/enum_glob_use.rs b/clippy_lints/src/enum_glob_use.rs deleted file mode 100644 index 4ffc73961a8c..000000000000 --- a/clippy_lints/src/enum_glob_use.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! lint on `use`ing all variants of an enum - -use crate::utils::span_lint; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; -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 `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_lint_pass!(EnumGlobUse => [ENUM_GLOB_USE]); - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EnumGlobUse { - fn check_mod(&mut self, cx: &LateContext<'a, 'tcx>, m: &'tcx Mod<'_>, _: Span, _: HirId) { - let map = cx.tcx.hir(); - // only check top level `use` statements - for item in m.item_ids { - lint_item(cx, map.expect_item(item.id)); - } - } -} - -fn lint_item(cx: &LateContext<'_, '_>, item: &Item<'_>) { - if item.vis.node.is_pub() { - return; // re-exports are fine - } - if let ItemKind::Use(ref path, UseKind::Glob) = item.kind { - if let Res::Def(DefKind::Enum, _) = path.res { - span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants"); - } - } -} diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 75205f964008..882020a7adc7 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -2,11 +2,11 @@ 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; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Detects enumeration variants that are prefixed or suffixed diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 01b04220a069..1df0bcc6b7e8 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -2,7 +2,7 @@ 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::*; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index e08e01c1d67e..3ff0506e28d0 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -1,4 +1,4 @@ -use rustc_hir::*; +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; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 6d0a1f673e77..db55e5ed141e 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,12 +1,12 @@ use rustc::ty::layout::LayoutOf; use rustc::ty::{self, Ty}; -use rustc_hir::intravisit as visit; -use rustc_hir::HirIdSet; -use rustc_hir::{self, *}; +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_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_typeck::expr_use_visitor::*; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; use crate::utils::span_lint; @@ -53,7 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { fn check_fn( &mut self, cx: &LateContext<'a, 'tcx>, - _: visit::FnKind<'tcx>, + _: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, @@ -92,7 +92,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal { } // TODO: Replace with Map::is_argument(..) when it's fixed -fn is_argument(map: &rustc::hir::map::Map<'_>, id: HirId) -> bool { +fn is_argument(map: rustc::hir::map::Map<'_>, id: HirId) -> bool { match map.find(id) { Some(Node::Binding(_)) => (), _ => return false, @@ -136,7 +136,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { 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) { + 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)) { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5ef933845005..8c324e09df55 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use matches::matches; use rustc::lint::in_external_macro; use rustc::ty::{self, Ty}; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index f143c7462ad2..9cf0d3ba26f6 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -3,7 +3,7 @@ use if_chain::if_chain; use rustc::hir::map::Map; use rustc::ty; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -106,10 +106,8 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { ExprKind::Match(ref e, arms, _) => { self.visit_expr(e); for arm in arms { - if let Some(ref guard) = arm.guard { - match guard { - Guard::If(if_expr) => self.visit_expr(if_expr), - } + 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); @@ -158,7 +156,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { 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> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -341,7 +339,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index a1bfb0a71010..ddbc3c377a27 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,8 @@ 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 syntax::ast::{AssocItemKind, Extern, FnSig, Item, ItemKind, Ty, TyKind}; use std::convert::TryInto; @@ -162,12 +162,12 @@ impl EarlyLintPass for ExcessiveBools { } | ItemKind::Trait(_, _, _, _, items) => { for item in items { - if let AssocItemKind::Fn(fn_sig, _) = &item.kind { + 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), + ItemKind::Fn(_, fn_sig, _, _) => self.check_fn_sig(cx, fn_sig, item.span), _ => (), } } diff --git a/clippy_lints/src/excessive_precision.rs b/clippy_lints/src/excessive_precision.rs deleted file mode 100644 index 2b63d5e9088e..000000000000 --- a/clippy_lints/src/excessive_precision.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::utils::span_lint_and_sugg; -use crate::utils::sugg::format_numeric_literal; -use if_chain::if_chain; -use rustc::ty; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; -use std::f32; -use std::f64; -use std::fmt; -use syntax::ast::*; - -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_lint_pass!(ExcessivePrecision => [EXCESSIVE_PRECISION]); - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision { - 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.node; - if let Some(sugg) = Self::check(sym, fty); - then { - span_lint_and_sugg( - cx, - EXCESSIVE_PRECISION, - expr.span, - "float has excessive precision", - "consider changing the type or truncating it to", - sugg, - Applicability::MachineApplicable, - ); - } - } - } -} - -impl ExcessivePrecision { - // None if nothing to lint, Some(suggestion) if lint necessary - #[must_use] - fn check(sym: Symbol, fty: FloatTy) -> Option { - let max = max_digits(fty); - let sym_str = sym.as_str(); - if dot_zero_exclusion(&sym_str) { - return None; - } - // 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); - if digits > max as usize { - let formatter = FloatFormat::new(&sym_str); - let sr = match fty { - FloatTy::F32 => sym_str.parse::().map(|f| formatter.format(f)), - FloatTy::F64 => sym_str.parse::().map(|f| formatter.format(f)), - }; - // We know this will parse since we are in LatePass - let s = sr.unwrap(); - - if sym_str == s { - None - } else { - Some(format_numeric_literal(&s, None, true)) - } - } else { - None - } - } -} - -/// Should we exclude the float because it has a `.0` or `.` suffix -/// Ex `1_000_000_000.0` -/// Ex `1_000_000_000.` -#[must_use] -fn dot_zero_exclusion(s: &str) -> bool { - s.split('.').nth(1).map_or(false, |after_dec| { - let mut decpart = after_dec.chars().take_while(|c| *c != 'e' || *c != 'E'); - - match decpart.next() { - Some('0') => decpart.count() == 0, - Some(_) => false, - None => true, - } - }) -} - -#[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(&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), - } - } -} diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4aefa941dabe..320121b27714 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,10 +1,10 @@ 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::*; +use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 87792b9fba47..06608772c8af 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom { 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::*; + use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; struct FindPanicUnwrap<'a, 'tcx> { lcx: &'a LateContext<'a, 'tcx>, @@ -85,7 +85,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it intravisit::walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -93,7 +93,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it for impl_item in impl_items { if_chain! { if impl_item.ident.name == sym!(from); - if let ImplItemKind::Method(_, body_id) = + if let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind; then { // check the body for `begin_panic` or `unwrap` diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs new file mode 100644 index 000000000000..55a9f61242b4 --- /dev/null +++ b/clippy_lints/src/float_literal.rs @@ -0,0 +1,182 @@ +use crate::utils::{numeric_literal, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc::ty; +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::{f32, f64, 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::().unwrap(); + + (value.fract() == 0.0, formatter.format(value)) + }, + FloatTy::F64 => { + let value = sym_str.parse::().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(&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), + } + } +} diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs new file mode 100644 index 000000000000..cee14dc893b5 --- /dev/null +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -0,0 +1,503 @@ +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::ty; +use rustc_errors::Applicability; +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::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 { + 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); + } + } +} diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 36ed7c475ed3..10bc2f12f252 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -4,12 +4,12 @@ use crate::utils::{ walk_ptrs_ty, }; use if_chain::if_chain; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::*; +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; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for the use of `format!("string literal with no diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 31e924e36abe..a05f911c4c5e 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,10 +1,10 @@ use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note}; use if_chain::if_chain; use rustc::lint::in_external_macro; +use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index ee9221c811e8..697486c1af09 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -6,6 +6,7 @@ use crate::utils::{ use rustc::hir::map::Map; use rustc::lint::in_external_macro; use rustc::ty::{self, Ty}; +use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -15,7 +16,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; -use syntax::ast::Attribute; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -250,7 +250,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { } fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) { - if let hir::ImplItemKind::Method(ref sig, ref body_id) = item.kind { + 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()); @@ -273,7 +273,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { } fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) { - if let hir::TraitItemKind::Method(ref sig, ref eid) = item.kind { + 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())); @@ -284,7 +284,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { 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::TraitMethod::Provided(eid) = *eid { + 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); @@ -468,8 +468,8 @@ fn check_must_use_candidate<'a, 'tcx>( fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { match decl.output { - hir::FunctionRetTy::DefaultReturn(_) => true, - hir::FunctionRetTy::Return(ref ty) => match ty.kind { + 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, @@ -486,7 +486,7 @@ fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, tys: &mut FxHash if let hir::PatKind::Wild = pat.kind { return false; // ignore `_` patterns } - let def_id = pat.hir_id.owner_def_id(); + 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).pat_ty(pat), pat.span, tys) } else { @@ -497,18 +497,17 @@ fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, tys: &mut FxHash 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) -> bool { - use ty::TyKind::*; match ty.kind { // primitive types are never mutable - Bool | Char | Int(_) | Uint(_) | Float(_) | Str => false, - Adt(ref adt, ref substs) => { + 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)) }, - Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), - Array(ty, _) | Slice(ty) => is_mutable_ty(cx, ty, span, tys), - RawPtr(ty::TypeAndMut { ty, mutbl }) | Ref(_, ty, mutbl) => { + 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 @@ -562,7 +561,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { intravisit::walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } @@ -593,7 +592,7 @@ 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::*; + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; if self.mutates_static { return; @@ -602,7 +601,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { Call(_, args) | MethodCall(_, _, args) => { let mut tys = FxHashSet::default(); for arg in args { - let def_id = arg.hir_id.owner_def_id(); + let def_id = arg.hir_id.owner.to_def_id(); if self.cx.tcx.has_typeck_tables(def_id) && is_mutable_ty( self.cx, @@ -625,13 +624,13 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } fn is_mutated_static(cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::*; + use hir::ExprKind::{Field, Index, Path}; match e.kind { Path(ref qpath) => { diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index 19dd103247da..e4bad5ca5c51 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -2,13 +2,13 @@ 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; use rustc_span::symbol::Symbol; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of diff --git a/clippy_lints/src/identity_conversion.rs b/clippy_lints/src/identity_conversion.rs index 86478f093c9e..3d0734219846 100644 --- a/clippy_lints/src/identity_conversion.rs +++ b/clippy_lints/src/identity_conversion.rs @@ -2,7 +2,7 @@ use crate::utils::{ match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_then, }; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 1896751ff6ff..abd546870aae 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,5 +1,5 @@ use rustc::ty; -use rustc_hir::*; +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; diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index a9826d586339..33e2471d5b8f 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -1,7 +1,7 @@ use crate::utils::{match_type, method_chain_args, paths, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{print, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 8deed2142038..9b6e243f5f04 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -2,9 +2,9 @@ //! on the condition use rustc::lint::in_external_macro; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::*; use crate::utils::span_lint_and_help; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index b0d80aa0d539..d968a928c33b 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,5 +1,5 @@ use crate::utils::{ - match_def_path, + fn_has_unsatisfiable_preds, match_def_path, paths::{BEGIN_PANIC, BEGIN_PANIC_FMT}, snippet_opt, span_lint_and_then, }; @@ -133,6 +133,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn { _: 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) { + return; + } + let mir = cx.tcx.optimized_mir(def_id); // checking return type through MIR, HIR is not able to determine inferred closure return types diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 30bb09fd8081..65d2dd581068 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,14 +1,12 @@ //! lint on indexing and slicing operations use crate::consts::{constant, Constant}; -use crate::utils; -use crate::utils::higher; -use crate::utils::higher::Range; +use crate::utils::{higher, span_lint, span_lint_and_help}; use rustc::ty; -use rustc_hir::*; +use rustc_ast::ast::RangeLimits; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::RangeLimits; declare_clippy_lint! { /// **What it does:** Checks for out of bounds array indexing with a constant @@ -94,13 +92,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { 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 = s.eval_usize(cx.tcx, cx.param_env).into(); + 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 { - utils::span_lint( + span_lint( cx, OUT_OF_BOUNDS_INDEXING, range.start.map_or(expr.span, |start| start.span), @@ -112,7 +114,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { if let (_, Some(end)) = const_range { if end > size { - utils::span_lint( + span_lint( cx, OUT_OF_BOUNDS_INDEXING, range.end.map_or(expr.span, |end| end.span), @@ -136,7 +138,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { (None, None) => return, // [..] is ok. }; - utils::span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", help_msg); + span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", help_msg); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind { @@ -147,7 +149,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { } } - utils::span_lint_and_help( + span_lint_and_help( cx, INDEXING_SLICING, expr.span, @@ -163,7 +165,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing { /// the range. If the start or end is not constant, None is returned. fn to_const_range<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, - range: Range<'_>, + range: higher::Range<'_>, array_size: u128, ) -> (Option, Option) { let s = range.start.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c)); diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index cee54488df48..cd989c0ea6f6 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,4 +1,4 @@ -use rustc_hir::*; +use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 058ee7265fbc..cd034f916cef 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::*; +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; @@ -59,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl { // 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_def_id(), item.span); + self.impls.insert(item.hir_id.owner.to_def_id(), item.span); } } } @@ -69,7 +69,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl { // Retrieve all inherent implementations from the crate, grouped by type for impls in cx .tcx - .crate_inherent_impls(item.hir_id.owner_def_id().krate) + .crate_inherent_impls(item.hir_id.owner.to_def_id().krate) .inherent_impls .values() { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 10fe8f28eaba..ca8e834e347c 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -100,7 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString { if_chain! { // Check if item is a method, called to_string and has a parameter 'self' - if let ImplItemKind::Method(ref signature, _) = impl_item.kind; + 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(); @@ -120,8 +120,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString { } fn show_lint(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) { - let display_trait_id = - get_trait_def_id(cx, &["core", "fmt", "Display"]).expect("Failed to get trait ID of `Display`!"); + 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); diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index ed9a958f91b6..08fc6287b776 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -2,11 +2,11 @@ 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::*; +use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::{Attribute, Name}; declare_clippy_lint! { /// **What it does:** Checks for `#[inline]` on trait methods without bodies @@ -32,7 +32,7 @@ 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::Method(_, TraitMethod::Required(_)) = item.kind { + if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind { check_attrs(cx, item.ident.name, &item.attrs); } } diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index cb2d62407ce8..f2781b734cf6 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,9 +1,9 @@ //! 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 syntax::ast::*; use crate::utils::{snippet_opt, span_lint_and_then}; diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 6d944ae8c33a..8d6a0212b5c1 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -2,9 +2,9 @@ use crate::utils::span_lint; use matches::matches; +use rustc_ast::ast::{Block, ItemKind, StmtKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for items declared after some statement in a block. diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 0cd02f21238d..39c8c4cfa693 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -3,7 +3,7 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use rustc::ty::layout::LayoutOf; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index aa394c85e308..e08042bcbb26 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -1,6 +1,6 @@ use rustc::mir::interpret::ConstValue; use rustc::ty::{self, ConstKind}; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index b522a74fb0e8..00cfb3b718d7 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,13 +1,13 @@ use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc::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::*; +use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{Span, Spanned}; -use syntax::ast::{LitKind, Name}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -134,7 +134,7 @@ fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item<'_>, trait_i // fill the set with current and super traits fn fill_trait_set(traitt: DefId, set: &mut FxHashSet, cx: &LateContext<'_, '_>) { if set.insert(traitt) { - for supertrait in rustc::traits::supertrait_def_ids(cx.tcx, traitt) { + for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) { fill_trait_set(supertrait, set, cx); } } @@ -147,7 +147,7 @@ fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item<'_>, trait_i let is_empty_method_found = current_and_super_traits .iter() - .flat_map(|&i| cx.tcx.associated_items(i)) + .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order()) .any(|i| { i.kind == ty::AssocKind::Method && i.method_has_self_argument @@ -276,10 +276,12 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { /// 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).any(|item| is_is_empty(cx, &item))) + 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)); @@ -288,6 +290,7 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { 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 diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index a4c5c003b7f8..d5343852208b 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> { } intravisit::walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index c2a404ebee7c..5690a12141b9 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,6 +1,6 @@ use if_chain::if_chain; use rustc::lint::in_external_macro; -use rustc_hir::*; +use rustc_hir::{PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5084e4e599c2..1c81d78a2b95 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -2,6 +2,7 @@ #![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)] @@ -19,6 +20,8 @@ extern crate fmt_macros; #[allow(unused_extern_crates)] extern crate rustc; #[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; @@ -33,6 +36,8 @@ extern crate rustc_hir; #[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; @@ -47,13 +52,13 @@ extern crate rustc_span; #[allow(unused_extern_crates)] extern crate rustc_target; #[allow(unused_extern_crates)] -extern crate rustc_typeck; +extern crate rustc_trait_selection; #[allow(unused_extern_crates)] -extern crate syntax; +extern crate rustc_typeck; -use rustc::session::Session; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; +use rustc_session::Session; /// Macro used to declare a Clippy lint. /// @@ -195,7 +200,6 @@ pub mod else_if_without_else; pub mod empty_enum; pub mod entry; pub mod enum_clike; -pub mod enum_glob_use; pub mod enum_variants; pub mod eq_op; pub mod erasing_op; @@ -203,10 +207,11 @@ pub mod escape; pub mod eta_reduction; pub mod eval_order_dependence; pub mod excessive_bools; -pub mod excessive_precision; pub mod exit; pub mod explicit_write; pub mod fallible_impl_from; +pub mod float_literal; +pub mod floating_point_arithmetic; pub mod format; pub mod formatting; pub mod functions; @@ -232,6 +237,7 @@ pub mod let_underscore; pub mod lifetimes; pub mod literal_representation; pub mod loops; +pub mod macro_use; pub mod main_recursion; pub mod map_clone; pub mod map_unit_fn; @@ -247,7 +253,6 @@ pub mod missing_const_for_fn; pub mod missing_doc; pub mod missing_inline; pub mod modulo_arithmetic; -pub mod mul_add; pub mod multiple_crate_versions; pub mod mut_key; pub mod mut_mut; @@ -267,6 +272,7 @@ pub mod no_effect; pub mod non_copy_const; pub mod non_expressive_names; pub mod open_options; +pub mod option_env_unwrap; pub mod overflow_check_conditional; pub mod panic_unimplemented; pub mod partialeq_ne_impl; @@ -307,7 +313,9 @@ pub mod unused_self; pub mod unwrap; pub mod use_self; pub mod vec; +pub mod verbose_file_reads; pub mod wildcard_dependencies; +pub mod wildcard_imports; pub mod write; pub mod zero_div_zero; // end lints modules, do not remove this comment, it’s used in `update_lints` @@ -315,7 +323,7 @@ pub mod zero_div_zero; pub use crate::utils::conf::Conf; mod reexport { - crate use syntax::ast::Name; + crate use rustc_ast::ast::Name; } /// Register all pre expansion lints @@ -327,7 +335,7 @@ mod reexport { /// /// 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); + 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 { @@ -338,7 +346,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Co } #[doc(hidden)] -pub fn read_conf(args: &[syntax::ast::NestedMetaItem], sess: &Session) -> Conf { +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) => { @@ -516,7 +524,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &empty_enum::EMPTY_ENUM, &entry::MAP_ENTRY, &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - &enum_glob_use::ENUM_GLOB_USE, &enum_variants::ENUM_VARIANT_NAMES, &enum_variants::MODULE_INCEPTION, &enum_variants::MODULE_NAME_REPETITIONS, @@ -531,10 +538,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, - &excessive_precision::EXCESSIVE_PRECISION, &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, @@ -594,6 +604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -605,6 +616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -687,7 +699,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, &modulo_arithmetic::MODULO_ARITHMETIC, - &mul_add::MANUAL_MUL_ADD, &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, &mut_key::MUTABLE_KEY_TYPE, &mut_mut::MUT_MUT, @@ -713,6 +724,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -807,7 +819,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, &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, @@ -830,9 +845,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_glob_use::EnumGlobUse); store.register_late_pass(|| box enum_clike::UnportableVariant); - store.register_late_pass(|| box excessive_precision::ExcessivePrecision); + 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); @@ -961,7 +975,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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 mul_add::MulAddCheck); store.register_late_pass(|| box mut_key::MutableKeyType); store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic); store.register_early_pass(|| box reference::DerefAddrOf); @@ -995,6 +1008,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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 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); @@ -1003,6 +1017,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1011,12 +1029,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1028,6 +1048,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1053,7 +1074,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&doc::DOC_MARKDOWN), LintId::of(&doc::MISSING_ERRORS_DOC), LintId::of(&empty_enum::EMPTY_ENUM), - LintId::of(&enum_glob_use::ENUM_GLOB_USE), LintId::of(&enum_variants::MODULE_NAME_REPETITIONS), LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES), LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), @@ -1068,6 +1088,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), + LintId::of(¯o_use::MACRO_USE_IMPORTS), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), @@ -1097,15 +1118,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::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), - LintId::of(&utils::internal_lints::DEFAULT_LINT), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ @@ -1154,8 +1177,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&eta_reduction::REDUNDANT_CLOSURE), LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(&excessive_precision::EXCESSIVE_PRECISION), 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), @@ -1265,7 +1288,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_FIELD_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&mut_key::MUTABLE_KEY_TYPE), @@ -1285,6 +1307,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1355,6 +1378,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&vec::USELESS_VEC), + LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), @@ -1380,7 +1404,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_variants::MODULE_INCEPTION), LintId::of(&eq_op::OP_REF), LintId::of(&eta_reduction::REDUNDANT_CLOSURE), - LintId::of(&excessive_precision::EXCESSIVE_PRECISION), + 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), @@ -1408,6 +1432,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1427,7 +1452,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), - LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), @@ -1490,7 +1514,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_SINGLE_BINDING), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), @@ -1539,6 +1562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); @@ -1590,6 +1614,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1638,8 +1663,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), + LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), + LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN), - LintId::of(&mul_add::MANUAL_MUL_ADD), LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(&mutex_atomic::MUTEX_INTEGER), LintId::of(&needless_borrow::NEEDLESS_BORROW), diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 13484ba0fa76..b510b293617a 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -3,15 +3,21 @@ use rustc::hir::map::Map; use rustc::lint::in_external_macro; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::*; -use rustc_hir::FunctionRetTy::Return; -use rustc_hir::*; +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, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::kw; -use crate::reexport::*; +use crate::reexport::Name; use crate::utils::{last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { @@ -80,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Lifetimes { } fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Method(ref sig, id) = item.kind { + 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, @@ -94,10 +100,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Lifetimes { } fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Method(ref sig, ref body) = item.kind { + if let TraitItemKind::Fn(ref sig, ref body) = item.kind { let body = match *body { - TraitMethod::Required(_) => None, - TraitMethod::Provided(id) => Some(id), + TraitFn::Required(_) => None, + TraitFn::Provided(id) => Some(id), }; check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true); } @@ -163,7 +169,7 @@ fn check_fn_inner<'a, 'tcx>( span_lint( cx, NEEDLESS_LIFETIMES, - span, + 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)", ); @@ -340,7 +346,7 @@ impl<'v, 't> RefVisitor<'v, 't> { { let hir_id = ty.hir_id; match self.cx.tables.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias, def_id) | Res::Def(DefKind::Struct, def_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); @@ -401,7 +407,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { } walk_ty(self, ty); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -473,7 +479,7 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { walk_generic_param(self, param) } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -516,7 +522,7 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 27cb7d219578..9e6b63fafd09 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,13 +1,17 @@ //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. -use crate::utils::{in_macro, snippet_opt, span_lint_and_sugg}; +use crate::utils::{ + in_macro, + numeric_literal::{NumericLiteral, Radix}, + snippet_opt, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc::lint::in_external_macro; +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Warns if a long integral or floating-point constant does @@ -35,7 +39,7 @@ declare_clippy_lint! { /// **Known problems:** /// - Recommends a signed suffix, even though the number might be too big and an unsigned /// suffix is required - /// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers + /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// **Example:** /// @@ -103,224 +107,6 @@ declare_clippy_lint! { "using decimal representation when hexadecimal would be better" } -#[derive(Debug, PartialEq)] -pub(super) 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_numeric_literal(lit: &str, type_suffix: Option<&str>, float: bool) -> String { - NumericLiteral::new(lit, type_suffix, float).format() -} - -#[derive(Debug)] -pub(super) struct NumericLiteral<'a> { - /// Which radix the literal was represented in. - radix: Radix, - /// The radix prefix, if present. - prefix: Option<&'a str>, - - /// The integer part of the number. - integer: &'a str, - /// The fraction part of the number. - fraction: Option<&'a str>, - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. - exponent: Option<(char, &'a str)>, - - /// The type suffix, including preceding underscore if present. - suffix: Option<&'a str>, -} - -impl<'a> NumericLiteral<'a> { - fn from_lit(src: &'a str, lit: &Lit) -> Option> { - 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] - 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, - } - } - - 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. - 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 - } - - 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 { - 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) -} - enum WarningType { UnreadableLiteral, InconsistentDigitGrouping, diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1aea630efe4e..475e60736e07 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,5 +1,5 @@ use crate::consts::{constant, Constant}; -use crate::reexport::*; +use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ @@ -15,20 +15,23 @@ use rustc::hir::map::Map; use rustc::lint::in_external_macro; use rustc::middle::region; use rustc::ty::{self, Ty}; +use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id; use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::{BytePos, Symbol}; -use rustc_typeck::expr_use_visitor::*; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; use std::iter::{once, Iterator}; use std::mem; -use syntax::ast; declare_clippy_lint! { /// **What it does:** Checks for for-loops that manually copy items between @@ -1676,7 +1679,7 @@ fn check_for_mutation( span_low: None, span_high: None, }; - let def_id = def_id::DefId::local(body.hir_id.owner); + let def_id = body.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new(&mut delegate, &infcx, def_id, cx.param_env, cx.tables).walk_expr(body); }); @@ -1709,7 +1712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -1777,7 +1780,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } return false; // no need to walk further *on the variable* } - Res::Def(DefKind::Static, ..) | Res::Def(DefKind::Const, ..) => { + Res::Def(DefKind::Static | DefKind::Const, ..) => { if indexed_indirectly { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } @@ -1882,7 +1885,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.prefer_mutable = old; } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -1939,7 +1942,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> { } walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -2081,7 +2084,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -2173,8 +2176,8 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } @@ -2309,7 +2312,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { walk_pat(self, pat) } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -2395,7 +2398,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -2446,7 +2449,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs new file mode 100644 index 000000000000..b1345d0b751a --- /dev/null +++ b/clippy_lints/src/macro_use.rs @@ -0,0 +1,53 @@ +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 {}::", 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, + ); + } + } + } +} diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index f96fb00acd04..72227555b259 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -4,12 +4,12 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::ty; +use rustc_ast::ast::Ident; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use syntax::ast::Ident; declare_clippy_lint! { /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 8bc08c342dc9..d7ea022d8886 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -186,19 +186,19 @@ fn unit_closure<'a, 'tcx>( /// `x.field` => `x_field` /// `y` => `_y` /// -/// Anything else will return `_`. +/// 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, "")), - _ => "_".to_string(), + _ => "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 unit {1}", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", map_type, function_type ) } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 1d8c5ec9038d..40880f78270c 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -3,22 +3,26 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, in_macro, is_allowed, is_expn_of, is_refutable, 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, + expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, 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::lint::in_external_macro; use rustc::ty::{self, Ty}; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; -use rustc_hir::*; +use rustc_hir::{ + print, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, + PatKind, QPath, RangeEnd, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use std::cmp::Ordering; use std::collections::Bound; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for matches with a single arm where an `if let` @@ -308,6 +312,36 @@ declare_clippy_lint! { "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, @@ -324,7 +358,8 @@ impl_lint_pass!(Matches => [ WILDCARD_ENUM_MATCH_ARM, WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, - INFALLIBLE_DESTRUCTURING_MATCH + INFALLIBLE_DESTRUCTURING_MATCH, + REST_PAT_IN_FULLY_BOUND_STRUCTS ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches { @@ -385,6 +420,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches { } } } + + fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) { + if_chain! { + 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", + "consider removing `..` from this binding", + ); + } + } + } } #[rustfmt::skip] @@ -434,7 +491,7 @@ fn report_single_match_single_pattern( ) { 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, "..")) + format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) }); span_lint_and_sugg( cx, @@ -447,7 +504,7 @@ fn report_single_match_single_pattern( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), els_str, ), Applicability::HasPlaceholders, @@ -523,17 +580,21 @@ fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], e (false, false) => Some(format!( "if {} {} else {}", snippet(cx, ex.span, "b"), - expr_block(cx, true_expr, None, ".."), - expr_block(cx, false_expr, None, "..") + 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, "..") + 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(format!( + "if {} {}", + !test, + expr_block(cx, false_expr, None, "..", Some(expr.span)) + )) }, (true, true) => None, }; @@ -720,7 +781,7 @@ fn is_panic_block(block: &Block<'_>) -> bool { fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if has_only_ref_pats(arms) { - let mut suggs = Vec::new(); + 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())); @@ -822,7 +883,7 @@ fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) { } } -fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { +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; } @@ -832,7 +893,7 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A 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, "..").to_owned().to_string() + snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string() }; // Do we need to add ';' to suggestion ? @@ -854,18 +915,51 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A 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, - expr.span, + target_span, "this match could be written as a `let` statement", "consider using `let` statement", - format!( - "let {} = {};\n{}", - snippet_with_applicability(cx, bind_names, "..", &mut applicability), - snippet_with_applicability(cx, matched_vars, "..", &mut applicability), - snippet_body - ), + sugg, applicability, ); }, @@ -884,6 +978,19 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A } } +/// 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>, diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 4e3c95f9f739..aaed6d75048c 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,10 +1,10 @@ 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; -use syntax::ast; pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { let unwrap_arg = &args[0][1]; @@ -23,7 +23,10 @@ pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr< }; if ty.is_signed() { - use self::{MinMax::*, Sign::*}; + use self::{ + MinMax::{Max, Min}, + Sign::{Neg, Pos}, + }; let sign = if let Some(sign) = lit_sign(arith_rhs) { sign diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 82bc8740d1af..8bba56c675ff 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,6 +12,7 @@ use matches::matches; use rustc::hir::map::Map; use rustc::lint::in_external_macro; use rustc::ty::{self, Predicate, Ty}; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -19,7 +20,6 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol, SymbolStr}; -use syntax::ast; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; @@ -551,7 +551,7 @@ declare_clippy_lint! { /// if name.starts_with('_') {}; /// ``` pub CHARS_NEXT_CMP, - complexity, + style, "using `.chars().next()` to check if a string starts with a char" } @@ -1410,7 +1410,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { let def_id = cx.tcx.hir().local_def_id(item.hir_id); let ty = cx.tcx.type_of(def_id); if_chain! { - if let hir::ImplItemKind::Method(ref sig, id) = impl_item.kind; + 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; @@ -1469,7 +1469,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { } } - if let hir::ImplItemKind::Method(_, _) = impl_item.kind { + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); // walk the return type and check for Self (this does not check associated types) @@ -1545,7 +1545,7 @@ fn lint_or_fun_call<'a, 'tcx>( } } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } @@ -1721,14 +1721,47 @@ fn lint_expect_fun_call( if match_type(cx, arg_ty, &paths::STRING) { return false; } - if let ty::Ref(ty::ReStatic, ty, ..) = arg_ty.kind { - if ty.kind == ty::Str { + 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<'_>, @@ -2289,7 +2322,13 @@ fn derefs_to_slice<'a, 'tcx>( ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")), - ty::Array(_, size) => size.eval_usize(cx.tcx, cx.param_env) < 32, + 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, } @@ -3371,14 +3410,14 @@ enum OutType { } impl OutType { - fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FunctionRetTy<'_>) -> bool { + 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::FunctionRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FunctionRetTy::Return(ref ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FunctionRetTy::Return(ref ty)) if is_bool(ty) => true, - (Self::Any, &hir::FunctionRetTy::Return(ref ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FunctionRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (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, } } @@ -3412,7 +3451,7 @@ fn contains_return(expr: &hir::Expr<'_>) -> bool { } } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 80a937aadb06..c40ab7beba44 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -4,7 +4,7 @@ use rustc::hir::map::Map; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; -use rustc_hir::{self, *}; +use rustc_hir::{self, HirId, Path}; use rustc_lint::LateContext; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -99,8 +99,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { walk_path(self, path); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::All(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) } } @@ -121,8 +121,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> { walk_path(self, path); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::All(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index fdfae8cd190c..ee6c6ef12e56 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index b3431c053783..b02c993de526 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_simple, Constant}; use crate::utils::{match_def_path, paths, span_lint}; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::cmp::Ordering; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 583bdd43df83..6618876b3751 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,13 +1,16 @@ use if_chain::if_chain; use matches::matches; use rustc::ty; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{ExpnKind, Span}; -use syntax::ast::LitKind; use crate::consts::{constant, Constant}; use crate::utils::sugg::Sugg; diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 6f54750cb9b9..718ec1c00bb4 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -4,13 +4,16 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::lint::in_external_macro; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use syntax::ast::*; -use syntax::visit::{walk_expr, FnKind, Visitor}; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -25,7 +28,7 @@ declare_clippy_lint! { /// let { a: _, b: ref b, c: _ } = .. /// ``` pub UNNEEDED_FIELD_PATTERN, - style, + restriction, "struct fields bound to a wildcard instead of using `..`" } @@ -315,18 +318,6 @@ impl EarlyLintPass for MiscEarlyLints { return; } if wilds > 0 { - 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); - } - }, - } - } for field in pfields { if let PatKind::Wild = field.pat.kind { wilds -= 1; @@ -338,6 +329,19 @@ impl EarlyLintPass for MiscEarlyLints { "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, @@ -352,7 +356,13 @@ impl EarlyLintPass for MiscEarlyLints { } } - if let PatKind::Ident(_, ident, Some(ref right)) = pat.kind { + 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, @@ -363,7 +373,7 @@ impl EarlyLintPass for MiscEarlyLints { ident.name, ident.name, ), "try", - format!("{}", ident.name), + format!("{}{}", left_binding, ident.name), Applicability::MachineApplicable, ); } @@ -372,10 +382,10 @@ impl EarlyLintPass for MiscEarlyLints { check_unneeded_wildcard_pattern(cx, pat); } - fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { let mut registered_names: FxHashMap = FxHashMap::default(); - for arg in &decl.inputs { + for arg in &fn_kind.decl().inputs { if let PatKind::Ident(_, ident, None) = arg.pat.kind { let arg_name = ident.to_string(); @@ -506,7 +516,11 @@ impl MiscEarlyLints { ); } - if lit_snip.starts_with("0x") && maybe_last_sep_idx >= 3 { + 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 { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index dc5a58abe56d..a22d8b4cf9eb 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,13 +1,14 @@ -use crate::utils::{has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use rustc::lint::in_external_macro; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Constness, FnDecl, HirId}; +use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; 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; +use std::matches; declare_clippy_lint! { /// **What it does:** @@ -87,11 +88,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { return; } + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + if fn_has_unsatisfiable_preds(cx, 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(_, _, header, ..) => { - if already_const(header) { + 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; } }, diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index e502db59e66b..e03f9e36095b 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -8,12 +8,12 @@ use crate::utils::span_lint; use if_chain::if_chain; use rustc::ty; +use rustc_ast::ast::{self, MetaItem, MetaItemKind}; +use rustc_ast::attr; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use syntax::ast::{self, MetaItem, MetaItemKind}; -use syntax::attr; declare_clippy_lint! { /// **What it does:** Warns if there is missing doc for any documentable item @@ -125,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.attrs, krate.span, "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<'_>) { @@ -135,7 +135,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym!(main) { - let def_id = cx.tcx.hir().local_def_id(it.hir_id); + 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; @@ -164,7 +164,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { 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::Method(..) => "a trait method", + hir::TraitItemKind::Fn(..) => "a trait method", hir::TraitItemKind::Type(..) => "an associated type", }; @@ -185,7 +185,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { let desc = match impl_item.kind { hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Method(..) => "a method", + hir::ImplItemKind::Fn(..) => "a method", hir::ImplItemKind::TyAlias(_) => "an associated type", hir::ImplItemKind::OpaqueTy(_) => "an existential type", }; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 407321effb80..22b56c28b6bc 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,9 +1,9 @@ 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; -use syntax::ast; declare_clippy_lint! { /// **What it does:** it lints if an exported function, method, trait method with default impl, @@ -69,7 +69,7 @@ fn check_missing_inline_attrs(cx: &LateContext<'_, '_>, attrs: &[ast::Attribute] } fn is_executable(cx: &LateContext<'_, '_>) -> bool { - use rustc::session::config::CrateType; + use rustc_session::config::CrateType; cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| match t { CrateType::Executable => true, @@ -100,7 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline { let tit_ = cx.tcx.hir().trait_item(tit.id); match tit_.kind { hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, - hir::TraitItemKind::Method(..) => { + hir::TraitItemKind::Fn(..) => { if tit.defaultness.has_value() { // trait method with default body needs inline in case // an impl is not provided @@ -141,7 +141,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline { } let desc = match impl_item.kind { - hir::ImplItemKind::Method(..) => "a method", + hir::ImplItemKind::Fn(..) => "a method", hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => return, }; diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 238863e8c67b..7446b732c4b2 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -2,7 +2,7 @@ use crate::consts::{constant, Constant}; use crate::utils::{sext, span_lint_and_then}; use if_chain::if_chain; use rustc::ty::{self}; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; diff --git a/clippy_lints/src/mul_add.rs b/clippy_lints/src/mul_add.rs deleted file mode 100644 index 5de936634991..000000000000 --- a/clippy_lints/src/mul_add.rs +++ /dev/null @@ -1,111 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::*; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::*; - -declare_clippy_lint! { - /// **What it does:** Checks for expressions of the form `a * b + c` - /// or `c + a * b` where `a`, `b`, `c` are floats and suggests using - /// `a.mul_add(b, c)` instead. - /// - /// **Why is this bad?** Calculating `a * b + c` may lead to slight - /// numerical inaccuracies as `a * b` is rounded before being added to - /// `c`. Depending on the target architecture, `mul_add()` may be more - /// performant. - /// - /// **Known problems:** This lint can emit semantic incorrect suggestions. - /// For example, for `a * b * c + d` the suggestion `a * b.mul_add(c, d)` - /// is emitted, which is equivalent to `a * (b * c + d)`. (#4735) - /// - /// **Example:** - /// - /// ```rust - /// # let a = 0_f32; - /// # let b = 0_f32; - /// # let c = 0_f32; - /// let foo = (a * b) + c; - /// ``` - /// - /// can be written as - /// - /// ```rust - /// # let a = 0_f32; - /// # let b = 0_f32; - /// # let c = 0_f32; - /// let foo = a.mul_add(b, c); - /// ``` - pub MANUAL_MUL_ADD, - nursery, - "Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`" -} - -declare_lint_pass!(MulAddCheck => [MANUAL_MUL_ADD]); - -fn is_float<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool { - cx.tables.expr_ty(expr).is_floating_point() -} - -// Checks whether expression is multiplication of two floats -fn is_float_mult_expr<'a, 'tcx, 'b>( - cx: &LateContext<'a, 'tcx>, - expr: &'b Expr<'b>, -) -> Option<(&'b Expr<'b>, &'b Expr<'b>)> { - if let ExprKind::Binary(op, lhs, rhs) = &expr.kind { - if let BinOpKind::Mul = op.node { - if is_float(cx, &lhs) && is_float(cx, &rhs) { - return Some((&lhs, &rhs)); - } - } - } - - None -} - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MulAddCheck { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(op, lhs, rhs) = &expr.kind { - if let BinOpKind::Add = op.node { - //Converts mult_lhs * mult_rhs + rhs to mult_lhs.mult_add(mult_rhs, rhs) - if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, lhs) { - if is_float(cx, rhs) { - span_lint_and_sugg( - cx, - MANUAL_MUL_ADD, - expr.span, - "consider using `mul_add()` for better numerical precision", - "try", - format!( - "{}.mul_add({}, {})", - snippet(cx, mult_lhs.span, "_"), - snippet(cx, mult_rhs.span, "_"), - snippet(cx, rhs.span, "_"), - ), - Applicability::MaybeIncorrect, - ); - } - } - //Converts lhs + mult_lhs * mult_rhs to mult_lhs.mult_add(mult_rhs, lhs) - if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, rhs) { - if is_float(cx, lhs) { - span_lint_and_sugg( - cx, - MANUAL_MUL_ADD, - expr.span, - "consider using `mul_add()` for better numerical precision", - "try", - format!( - "{}.mul_add({}, {})", - snippet(cx, mult_lhs.span, "_"), - snippet(cx, mult_rhs.span, "_"), - snippet(cx, lhs.span, "_"), - ), - Applicability::MaybeIncorrect, - ); - } - } - } - } - } -} diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index d07b21bfa991..88605c52f2e2 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,10 +1,10 @@ //! lint on multiple versions of a crate being used use crate::utils::span_lint; +use rustc_ast::ast::Crate; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; -use syntax::ast::*; use itertools::Itertools; diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index d48c4c419aef..adaf82c70748 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType { } fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'tcx>) { - if let hir::ImplItemKind::Method(ref sig, ..) = item.kind { + 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); } @@ -67,7 +67,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType { } fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'tcx>) { - if let hir::TraitItemKind::Method(ref sig, ..) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { check_sig(cx, item.hir_id, &sig.decl); } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 988484591660..70061f2aa83b 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -108,7 +108,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { intravisit::walk_ty(self, ty); } - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 2d70b65bb1ee..aeffcd34110e 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -1,7 +1,7 @@ use crate::utils::span_lint; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty}; -use rustc_hir::*; +use rustc_hir::{print, BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index f0c5c95b1d49..75225fa8c42b 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { walk_expr(self, expr) } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 98cb9126f8ba..bbd2e4d83faa 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -4,10 +4,10 @@ use crate::utils::{match_type, paths, span_lint}; use rustc::ty::{self, Ty}; +use rustc_ast::ast; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast; declare_clippy_lint! { /// **What it does:** Checks for usages of `Mutex` where an atomic will do. diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index db4012146dff..f2c58f582722 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,13 +3,14 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{higher, parent_node_is_if_expr, span_lint, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; +use crate::utils::{higher, parent_node_is_if_expr, span_lint, span_lint_and_help, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for expressions of the form `if c { true } else { @@ -51,7 +52,13 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust,ignore - /// if x == true {} // could be `if x { }` + /// if x == true {} + /// if y == false {} + /// ``` + /// use `x` directly: + /// ```rust,ignore + /// if x {} + /// if !y {} /// ``` pub BOOL_COMPARISON, complexity, @@ -62,7 +69,7 @@ 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::*; + 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; @@ -182,6 +189,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison { } } +fn is_unary_not<'tcx>(e: &'tcx Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Unary(unop, _) = e.kind; + if let UnOp::UnNot = unop; + then { + return true; + } + }; + false +} + +fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> bool { + is_unary_not(left_side) ^ is_unary_not(right_side) +} + fn check_comparison<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>, @@ -191,11 +213,19 @@ fn check_comparison<'a, 'tcx>( 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::*; + use self::Expression::{Bool, Other}; - if let ExprKind::Binary(_, ref left_side, ref right_side) = e.kind { + 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() { + if_chain! { + if let BinOpKind::Eq = op.node; + if one_side_is_unary_not(&left_side, &right_side); + then { + span_lint_and_help(cx, BOOL_COMPARISON, e.span, "Here comes", "the suggestion"); + } + }; + let mut applicability = Applicability::MachineApplicable; match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { (Bool(true), Other) => left_true.map_or((), |(h, m)| { diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index ac14b113cc58..6be4b1effeae 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -33,13 +33,13 @@ //! ``` //! //! 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 std::borrow::Cow; -use syntax::ast; +use rustc_span::Span; -use crate::utils::{snippet, snippet_block, span_lint_and_help, trim_multiline}; +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 @@ -119,9 +119,9 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); impl EarlyLintPass for NeedlessContinue { - fn check_expr(&mut self, ctx: &EarlyContext<'_>, expr: &ast::Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { if !expr.span.from_expansion() { - check_and_warn(ctx, expr); + check_and_warn(cx, expr); } } } @@ -273,93 +273,96 @@ struct LintData<'a> { block_stmts: &'a [ast::Stmt], } -const MSG_REDUNDANT_ELSE_BLOCK: &str = "This `else` block is redundant.\n"; +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\n"; +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, like so:\n"; +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, and moving out the code in the `else` \ - block, like so:\n"; +const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; -fn emit_warning<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { +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(ctx, data, header), + suggestion_snippet_for_continue_inside_else(cx, data), MSG_REDUNDANT_ELSE_BLOCK, data.else_expr, ), LintType::ContinueInsideThenBlock => ( - suggestion_snippet_for_continue_inside_if(ctx, data, header), + suggestion_snippet_for_continue_inside_if(cx, data), MSG_ELSE_BLOCK_NOT_NEEDED, data.if_expr, ), }; - span_lint_and_help(ctx, NEEDLESS_CONTINUE, expr.span, message, &snip); + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + expr.span, + message, + &format!("{}\n{}", header, snip), + ); } -fn suggestion_snippet_for_continue_inside_if<'a>( - ctx: &EarlyContext<'_>, - data: &'a LintData<'_>, - header: &str, -) -> String { - let cond_code = snippet(ctx, data.if_cond.span, ".."); - - let if_code = format!("if {} {{\n continue;\n}}\n", cond_code); - /* ^^^^--- Four spaces of indentation. */ - // region B - let else_code = snippet(ctx, data.else_expr.span, "..").into_owned(); - let else_code = erode_block(&else_code); - let else_code = trim_multiline(Cow::from(else_code), false); - - let mut ret = String::from(header); - ret.push_str(&if_code); - ret.push_str(&else_code); - ret.push_str("\n..."); - ret +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>( - ctx: &EarlyContext<'_>, - data: &'a LintData<'_>, - header: &str, -) -> String { - let cond_code = snippet(ctx, data.if_cond.span, ".."); - let mut if_code = format!("if {} {{\n", cond_code); +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 = &snippet(ctx, data.if_block.span, "..").into_owned(); - let block_code = erode_block(block_code); - let block_code = trim_multiline(Cow::from(block_code), false); - - if_code.push_str(&block_code); + 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| snippet_block(ctx, span, "..").into_owned()) + .map(|span| { + let snip = snippet_block(cx, span, "..", None).into_owned(); + snip.lines() + .map(|line| format!("{}{}", " ".repeat(indent), line)) + .collect::>() + .join("\n") + }) .collect::>() .join("\n"); - let mut ret = String::from(header); - - ret.push_str(&if_code); - ret.push_str("\n// Merged code follows..."); - ret.push_str(&to_annex); - ret.push_str("\n}\n"); - ret + 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>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) { +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| { @@ -373,22 +376,22 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) { }; if needless_continue_in_else(else_expr, label) { emit_warning( - ctx, + cx, data, DROP_ELSE_BLOCK_AND_MERGE_MSG, LintType::ContinueInsideElseBlock, ); } else if is_first_block_stmt_continue(then_block, label) { - emit_warning(ctx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + 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 +/// 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 /// { @@ -402,12 +405,9 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) { /// { /// let x = 5; /// ``` -/// -/// NOTE: when there is no closing brace in `s`, `s` is _not_ preserved, i.e., -/// an empty string will be returned in that case. #[must_use] -pub fn erode_from_back(s: &str) -> String { - let mut ret = String::from(s); +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() { @@ -415,41 +415,48 @@ pub fn erode_from_back(s: &str) -> String { break; } } - ret + if ret.is_empty() { + s.to_string() + } else { + ret + } } -/// Eats at `s` from the front by first skipping all leading whitespace. Then, -/// any number of opening braces are eaten, followed by any number of newlines. -/// e.g., the string -/// -/// ```ignore -/// { -/// something(); -/// inside_a_block(); -/// } -/// ``` -/// -/// is transformed to -/// -/// ```ignore -/// something(); -/// inside_a_block(); -/// } -/// ``` -#[must_use] -pub fn erode_from_front(s: &str) -> String { - s.chars() - .skip_while(|c| c.is_whitespace()) - .skip_while(|c| *c == '{') - .skip_while(|c| *c == '\n') - .collect::() +fn span_of_first_expr_in_block(block: &ast::Block) -> Option { + block.stmts.iter().next().map(|stmt| stmt.span) } -/// If `s` contains the code for a block, delimited by braces, this function -/// tries to get the contents of the block. If there is no closing brace -/// present, -/// an empty string is returned. -#[must_use] -pub fn erode_block(s: &str) -> String { - erode_from_back(&erode_from_front(s)) +#[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); + } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index b23e26b75586..d2d2b48c9cf2 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -5,20 +5,21 @@ use crate::utils::{ }; use if_chain::if_chain; use matches::matches; -use rustc::traits; -use rustc::traits::misc::can_type_implement_copy; -use rustc::ty::{self, RegionKind, TypeFoldable}; +use rustc::ty::{self, TypeFoldable}; +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::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{Span, Symbol}; 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; -use syntax::ast::Attribute; declare_clippy_lint! { /// **What it does:** Checks for functions taking arguments by value, but not @@ -170,8 +171,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { ( preds.iter().any(|t| t.def_id() == borrow_trait), - !preds.is_empty() - && preds.iter().all(|t| { + !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 @@ -180,8 +182,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { .skip(1) .cloned() .collect::>(); - implements_trait(cx, cx.tcx.mk_imm_ref(&RegionKind::ReEmpty, ty), t.def_id(), ty_params) - }), + implements_trait(cx, ty_empty_region, t.def_id(), ty_params) + }) + }, ) }; diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index efe5f26c4239..2c4c29913cff 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,6 +1,6 @@ use if_chain::if_chain; use rustc::lint::in_external_macro; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -67,7 +67,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoNegCompOpForPartialOrd { }; let implements_partial_ord = { - if let Some(id) = utils::get_trait_def_id(cx, &paths::PARTIAL_ORD) { + if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { utils::implements_trait(cx, ty, id, &[]) } else { return; diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index bd4ada23762b..4681e990df88 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,5 +1,5 @@ use if_chain::if_chain; -use rustc_hir::*; +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; diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 303ba1e68087..f69b145fa39f 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -105,7 +105,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { if in_external_macro(cx.sess(), impl_item.span) { return; } - if let hir::ImplItemKind::Method(ref sig, _) = impl_item.kind { + 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 { @@ -149,9 +149,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { 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 self_def.did.is_local(); + 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.did.to_local()); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id); if impling_types.contains(&self_id) { return; } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 6383d3ca2454..b8bfa676a160 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -69,7 +69,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { 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(..), _) => { + 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, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 48ffc113546f..43d76d0cbbdd 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -7,7 +7,7 @@ use std::ptr; use rustc::ty::adjustment::Adjust; use rustc::ty::{Ty, TypeFlags}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; @@ -128,7 +128,7 @@ fn verify_ty_bound<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, source: S db.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, Source::Assoc { ty: ty_span, .. } => { - if ty.flags.contains(TypeFlags::HAS_FREE_LOCAL_NAMES) { + if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { db.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); } }, @@ -191,7 +191,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst { // Make sure it is a const item. match qpath_res(cx, qpath, expr.hir_id) { - Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {}, + Res::Def(DefKind::Const | DefKind::AssocConst, _) => {}, _ => return, }; diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index b9df82d65e3b..98446eef9d73 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,12 +1,14 @@ 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; -use syntax::ast::*; -use syntax::attr; -use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor}; declare_clippy_lint! { /// **What it does:** Checks for names that are very similar and thus confusing. @@ -143,7 +145,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { _ => walk_pat(self, pat), } } - fn visit_mac(&mut self, _mac: &Mac) { + fn visit_mac(&mut self, _mac: &MacCall) { // do not check macs } } @@ -345,20 +347,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> { fn visit_item(&mut self, _: &Item) { // do not recurse into inner items } - fn visit_mac(&mut self, _mac: &Mac) { + 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, _, ref blk) = item.kind { + 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 { + if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind { do_check(self, cx, &item.attrs, &sig.decl, blk); } } diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index ff0cb53509d9..9d3b67988dbb 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -1,9 +1,9 @@ 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}; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for duplicate open options as well as combinations diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs new file mode 100644 index 000000000000..96ab4238008d --- /dev/null +++ b/clippy_lints/src/option_env_unwrap.rs @@ -0,0 +1,54 @@ +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", + "consider using the `env!` macro instead" + ); + } + } + } +} diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index df3f588f38a1..b90fdc232e72 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -1,6 +1,6 @@ use crate::utils::{span_lint, SpanlessEq}; use if_chain::if_chain; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index b26ad1a5b45d..2cd9200ddb25 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,10 +1,10 @@ use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, paths, span_lint}; use if_chain::if_chain; -use rustc_hir::*; +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; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for missing parameters in `panic!`. diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index dfd25f1c9db6..1445df41c452 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,6 +1,6 @@ use crate::utils::{is_automatically_derived, span_lint_hir}; use if_chain::if_chain; -use rustc_hir::*; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index adaf8bb77a20..bdbaf2695c8e 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -1,11 +1,11 @@ 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::*; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::path::{Component, Path}; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 27e67bbe718c..8b6d0336b207 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,9 +1,9 @@ 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; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for operations where precedence may be unclear @@ -123,7 +123,7 @@ fn is_arith_expr(expr: &Expr) -> bool { #[must_use] fn is_bit_op(op: BinOpKind) -> bool { - use syntax::ast::BinOpKind::*; + use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; match op { BitXor | BitAnd | BitOr | Shl | Shr => true, _ => false, @@ -132,7 +132,7 @@ fn is_bit_op(op: BinOpKind) -> bool { #[must_use] fn is_arith_op(op: BinOpKind) -> bool { - use syntax::ast::BinOpKind::*; + use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; match op { Add | Sub | Mul | Div | Rem => true, _ => false, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 445f065aaced..3d420cf8cecf 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -8,8 +8,10 @@ use crate::utils::{ use if_chain::if_chain; use rustc::ty; use rustc_errors::Applicability; -use rustc_hir::QPath; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -107,7 +109,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ptr { } fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Method(ref sig, body_id) = item.kind { + 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 { @@ -119,8 +121,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ptr { } fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Method(ref sig, ref trait_method) = item.kind { - let body_id = if let TraitMethod::Provided(b) = *trait_method { + 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 @@ -253,7 +255,7 @@ fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_ } } - if let FunctionRetTy::Return(ref ty) = decl.output { + 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 diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 93bd3b57ad7e..ffc59d43750e 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -use crate::utils; +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}; @@ -59,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrOffsetWithCast { 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) { - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, PTR_OFFSET_WITH_CAST, expr.span, @@ -69,7 +69,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrOffsetWithCast { Applicability::MachineApplicable, ); } else { - utils::span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg); + span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg); } } } @@ -119,8 +119,8 @@ fn build_suggestion<'a, 'tcx>( receiver_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>, ) -> Option { - let receiver = utils::snippet_opt(cx, receiver_expr.span)?; - let cast_lhs = utils::snippet_opt(cx, cast_lhs_expr.span)?; + 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)) } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 46c4746d2f56..f054c6ef67d3 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -1,13 +1,15 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; +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::paths::*; +use crate::utils::paths::{OPTION, OPTION_NONE}; use crate::utils::sugg::Sugg; -use crate::utils::{higher, match_def_path, match_type, span_lint_and_then, SpanlessEq}; +use crate::utils::{ + higher, match_def_path, match_qpath, match_type, 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. @@ -55,7 +57,8 @@ impl QuestionMark { if Self::is_option(cx, subject); then { - let receiver_str = &Sugg::hir(cx, subject, ".."); + let mut applicability = Applicability::MachineApplicable; + let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); let mut replacement: Option = None; if let Some(else_) = else_ { if_chain! { @@ -74,25 +77,61 @@ impl QuestionMark { } if let Some(replacement_str) = replacement { - span_lint_and_then( + span_lint_and_sugg( cx, QUESTION_MARK, expr.span, "this block may be rewritten with the `?` operator", - |db| { - db.span_suggestion( - expr.span, - "replace_it_with", - replacement_str, - Applicability::MaybeIncorrect, // snippet - ); - } + "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); @@ -158,5 +197,6 @@ impl QuestionMark { 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); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1be597877213..c9de88d9c83d 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,10 +1,10 @@ use if_chain::if_chain; +use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::*; +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 syntax::ast::RangeLimits; use crate::utils::sugg::Sugg; use crate::utils::{higher, SpanlessEq}; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 66faecbef2ad..b63bb371c4f3 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,12 +1,12 @@ use crate::utils::{ - has_drop, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_hir, span_lint_hir_and_then, - walk_ptrs_ty_depth, + fn_has_unsatisfiable_preds, has_drop, is_copy, 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 matches::matches; use rustc::mir::{ self, traversal, - visit::{MutatingUseContext, PlaceContext, Visitor as _}, + visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, }; use rustc::ty::{self, fold::TypeVisitor, Ty}; use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; @@ -15,9 +15,8 @@ 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_mir::dataflow::{ - do_dataflow, BitDenotation, BottomValue, DataflowResults, DataflowResultsCursor, DebugFormatted, GenKillSet, -}; +use rustc_mir::dataflow::generic::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_mir::dataflow::BottomValue; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use std::convert::TryFrom; @@ -80,19 +79,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { _: 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) { + return; + } + let mir = cx.tcx.optimized_mir(def_id); let mir_read_only = mir.unwrap_read_only(); - let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len()); - let maybe_storage_live_result = do_dataflow( - cx.tcx, - mir, - def_id, - &[], - &dead_unwinds, - MaybeStorageLive::new(mir), - |bd, p| DebugFormatted::new(&bd.body.local_decls[p]), - ); + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir, def_id) + .iterate_to_fixpoint() + .into_results_cursor(mir); let mut possible_borrower = { let mut vis = PossibleBorrowerVisitor::new(cx, mir); vis.visit_body(mir_read_only); @@ -111,7 +110,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { continue; } - let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); + 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) @@ -133,8 +133,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { statement_index: bbdata.statements.len(), }; - // Cloned local - let local = if from_borrow { + // `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. @@ -142,7 +142,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { continue; } - cloned + (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. @@ -154,15 +154,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { let pred_terminator = mir[ps[0]].terminator(); // receiver of the `deref()` call - let pred_arg = if_chain! { - if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) = + 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.local == cloned; + 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 + (pred_arg, res) } else { continue; } @@ -189,25 +189,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { continue; } - local + (local, deref_clone_ret) }; - // `local` cannot be moved out if it is used later - let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| { - // Give up on loops - if tdata.terminator().successors().any(|s| *s == bb) { - return true; - } + let is_temp = mir_read_only.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 { - local, - used_other_than_drop: false, - }; - vis.visit_basic_block_data(tbb, tdata); - vis.used_other_than_drop - }); + 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_later { + if !used || !consumed_or_mutated { let span = terminator.source_info.span; let scope = terminator.source_info.scope; let node = mir.source_scopes[scope] @@ -241,10 +251,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone { String::new(), app, ); - db.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); + if used { + db.span_note( + span, + "cloned value is neither consumed nor mutated", + ); + } else { + db.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"); @@ -260,7 +277,7 @@ 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>, Option<&'tcx mir::Place<'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; @@ -269,7 +286,7 @@ fn is_call_with_ref_arg<'tcx>( 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))) + Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?)) } else { None } @@ -338,8 +355,8 @@ fn base_local_and_movability<'tcx>( } struct LocalUseVisitor { - local: mir::Local, - used_other_than_drop: bool, + used: (mir::Local, bool), + consumed_or_mutated: (mir::Local, bool), } impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { @@ -347,11 +364,6 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { let statements = &data.statements; for (statement_index, statement) in statements.iter().enumerate() { self.visit_statement(statement, mir::Location { block, statement_index }); - - // Once flagged, skip remaining statements - if self.used_other_than_drop { - return; - } } self.visit_terminator( @@ -363,48 +375,48 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { ); } - fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext, _: mir::Location) { - match ctx { - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return, - _ => {}, + 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.local { - self.used_other_than_drop = 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<'a, 'tcx> { - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> MaybeStorageLive<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - MaybeStorageLive { body } - } -} +struct MaybeStorageLive; -impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { type Idx = mir::Local; - fn name() -> &'static str { - "maybe_storage_live" - } - fn bits_per_block(&self) -> usize { - self.body.local_decls.len() + const NAME: &'static str = "maybe_storage_live"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + body.local_decls.len() } - fn start_block_effect(&self, on_entry: &mut BitSet) { - for arg in self.body.args_iter() { - on_entry.insert(arg); + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { + for arg in body.args_iter() { + state.insert(arg); } } +} - fn statement_effect(&self, trans: &mut GenKillSet, loc: mir::Location) { - let stmt = &self.body[loc.block].statements[loc.statement_index]; - +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { match stmt.kind { mir::StatementKind::StorageLive(l) => trans.gen(l), mir::StatementKind::StorageDead(l) => trans.kill(l), @@ -412,20 +424,27 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> { } } - fn terminator_effect(&self, _trans: &mut GenKillSet, _loc: mir::Location) {} + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } - fn propagate_call_return( + fn call_return_effect( &self, - _in_out: &mut BitSet, - _call_bb: mir::BasicBlock, - _dest_bb: mir::BasicBlock, - _dest_place: &mir::Place<'tcx>, + _in_out: &mut impl GenKill, + _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<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> { +impl BottomValue for MaybeStorageLive { /// bottom = dead const BOTTOM_VALUE: bool = false; } @@ -451,8 +470,8 @@ impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { fn into_map( self, cx: &LateContext<'a, 'tcx>, - maybe_live: DataflowResults<'tcx, MaybeStorageLive<'a, 'tcx>>, - ) -> PossibleBorrower<'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) { @@ -475,9 +494,9 @@ impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { } let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrower { + PossibleBorrowerMap { map, - maybe_live: DataflowResultsCursor::new(maybe_live, self.body), + maybe_live, bitset: (bs.clone(), bs), } } @@ -538,7 +557,7 @@ impl TypeVisitor<'_> for ContainsRegion { } fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc::mir::Rvalue::*; + use rustc::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), @@ -557,18 +576,18 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } /// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrower<'a, 'tcx> { +struct PossibleBorrowerMap<'a, 'tcx> { /// Mapping `Local -> its possible borrowers` map: FxHashMap>, - maybe_live: DataflowResultsCursor<'a, 'tcx, MaybeStorageLive<'a, 'tcx>>, + maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, // Caches to avoid allocation of `BitSet` on every query bitset: (BitSet, BitSet), } -impl PossibleBorrower<'_, '_> { +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(at); + self.maybe_live.seek_after(at); self.bitset.0.clear(); let maybe_live = &mut self.maybe_live; diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 57349715c784..b12c3c344ef4 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,8 +1,8 @@ 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}; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs index f0acd993480b..a0b584b57ef3 100644 --- a/clippy_lints/src/redundant_pattern_matching.rs +++ b/clippy_lints/src/redundant_pattern_matching.rs @@ -1,9 +1,9 @@ use crate::utils::{match_qpath, paths, snippet, span_lint_and_then}; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Lint for redundant pattern matching over `Result` or diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 9cebe310d5fe..4a7a15aba5be 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,8 +1,8 @@ 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}; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. @@ -79,7 +79,7 @@ impl RedundantStaticLifetimes { 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 { + 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) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index c575ef67f2a9..d5797468e9d5 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,9 +1,9 @@ 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}; -use syntax::ast::{Expr, ExprKind, UnOp}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 5de0e441367a..4bcb9198792d 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,13 +1,13 @@ 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::*; +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; -use syntax::ast::{LitKind, StrStyle}; declare_clippy_lint! { /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation @@ -145,8 +145,8 @@ fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option< } fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { - use regex_syntax::hir::Anchor::*; - use regex_syntax::hir::HirKind::*; + 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() { diff --git a/clippy_lints/src/replace_consts.rs b/clippy_lints/src/replace_consts.rs index 66e1df72b25f..aca1ebbd5080 100644 --- a/clippy_lints/src/replace_consts.rs +++ b/clippy_lints/src/replace_consts.rs @@ -2,7 +2,7 @@ use crate::utils::{match_def_path, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index c903df0f2119..ef1bbfd8fa92 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,12 +1,12 @@ use if_chain::if_chain; use rustc::lint::in_external_macro; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use syntax::ast; -use syntax::visit::FnKind; use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_then}; @@ -235,13 +235,14 @@ impl Return { } impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, decl: &ast::FnDecl, span: Span, _: ast::NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { match kind { - FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block), - FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), + 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::FunctionRetTy::Ty(ref ty) = decl.output; + 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 { diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 63fdd599b51e..6820d1620bd1 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use crate::utils::{get_trait_def_id, paths, span_lint}; -use rustc_hir::*; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 28e3acdf6bea..0ada65ec7855 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,9 +1,12 @@ -use crate::reexport::*; +use crate::reexport::Name; use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; use rustc::lint::in_external_macro; use rustc::ty; use rustc_hir::intravisit::FnKind; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -101,7 +104,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Shadow { } fn check_fn<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) { - let mut bindings = Vec::new(); + 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)) @@ -151,10 +154,14 @@ fn check_local<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>, bin } fn is_binding(cx: &LateContext<'_, '_>, pat_id: HirId) -> bool { - let var_ty = cx.tables.node_type(pat_id); - match var_ty.kind { - ty::Adt(..) => false, - _ => true, + 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 } } diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index eb3261bebe31..8d767a7fec88 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,10 +1,10 @@ -use crate::utils::span_lint_and_sugg; +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; -use syntax::ast::{Item, ItemKind, UseTreeKind}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -39,6 +39,7 @@ 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; diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 48d0e53d163e..61dc7d1c9c5a 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,13 +2,13 @@ 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::hir::map::Map; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks slow zero-filled vector initialization @@ -318,7 +318,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ffe7c13ec575..e777adb22cfb 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use rustc::lint::in_external_macro; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; @@ -153,7 +153,7 @@ 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 syntax::ast::LitKind; + use rustc_ast::ast::LitKind; if_chain! { if let ExprKind::MethodCall(path, _, args) = &e.kind; diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 40e1b4abc796..e39a7b5e2ab6 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 7e699844ef23..d532efbec749 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -7,7 +7,7 @@ use if_chain::if_chain; use matches::matches; use rustc::ty; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Symbol; @@ -54,7 +54,7 @@ declare_clippy_lint! { /// a = b; /// b = a; /// ``` - /// Could be written as: + /// If swapping is intended, use `swap()` instead: /// ```rust /// # let mut a = 1; /// # let mut b = 2; @@ -99,7 +99,7 @@ fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) { then { if let ExprKind::Field(ref lhs1, _) = lhs1.kind { if let ExprKind::Field(ref lhs2, _) = lhs2.kind { - if lhs1.hir_id.owner_def_id() == lhs2.hir_id.owner_def_id() { + if lhs1.hir_id.owner == lhs2.hir_id.owner { return; } } diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 41e3f9918cc9..7b673e15b764 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -1,10 +1,10 @@ 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; -use syntax::ast; declare_clippy_lint! { /// **What it does:** Checks doc comments for usage of tab characters. diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index fec9ad256336..bbb883aaf328 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -1,5 +1,4 @@ -use crate::utils::is_adjusted; -use crate::utils::span_lint; +use crate::utils::{is_adjusted, span_lint}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 858b505712d3..d343ec4a8483 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,6 +1,6 @@ use crate::utils::{in_macro, snippet, span_lint_and_help, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::*; +use rustc_hir::{GenericBound, Generics, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index e6a9aa3c9a63..4d1996ffcefb 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -3,12 +3,12 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::ty::{self, Ty}; +use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::borrow::Cow; -use syntax::ast; declare_clippy_lint! { /// **What it does:** Checks for transmutes that can't ever be correct on any diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index dcab84122505..494dd4486891 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -2,10 +2,10 @@ use crate::consts::{constant_context, Constant}; use crate::utils::{match_qpath, paths, span_lint}; use if_chain::if_chain; use rustc::lint::in_external_macro; +use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::LitKind; declare_clippy_lint! { /// **What it does:** Checks for transmute calls which would receive a null pointer. diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 1bacb4683c38..194e11ac9bdf 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -3,13 +3,13 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; use matches::matches; -use rustc::session::config::Config as SessionConfig; use rustc::ty; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::*; +use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; +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; @@ -132,7 +132,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef { return; } - if let hir::TraitItemKind::Method(method_sig, _) = &item.kind { + if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); } } diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 15f77d62201d..a6624408ce05 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -3,7 +3,7 @@ use if_chain::if_chain; use rustc::lint::in_external_macro; use rustc::ty::Ty; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index e47f2480a541..d466924dc689 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -9,10 +9,15 @@ use rustc::hir::map::Map; use rustc::lint::in_external_macro; use rustc::ty::layout::LayoutOf; use rustc::ty::{self, InferTy, Ty, TyCtxt, TypeckTables}; +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::*; +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_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -20,15 +25,14 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; use rustc_typeck::hir_ty_to_ty; -use syntax::ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, last_path_segment, match_def_path, - match_path, method_chain_args, multispan_sugg, 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, + 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! { @@ -200,7 +204,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types { 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::Method(ref sig, _) => self.check_fn_decl(cx, &sig.decl), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), _ => (), } } @@ -242,7 +246,7 @@ impl Types { self.check_ty(cx, input, false); } - if let FunctionRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ref ty) = decl.output { self.check_ty(cx, ty, false); } } @@ -1206,22 +1210,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts { 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 let LitKind::Int(n, _) = lit.node { - if cast_to.is_floating_point() { - 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 { - 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; - } + 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 { @@ -1450,7 +1457,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexity { 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::Method(FnSig { ref decl, .. }, TraitMethod::Required(_)) => self.check_fndecl(cx, decl), + TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), // methods with default impl are covered by check_fn _ => (), } @@ -1476,7 +1483,7 @@ impl<'a, 'tcx> TypeComplexity { for arg in decl.inputs { self.check_type(cx, arg); } - if let FunctionRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ref ty) = decl.output { self.check_type(cx, ty); } } @@ -1547,7 +1554,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { walk_ty(self, ty); self.nest -= sub_nest; } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -1635,7 +1642,7 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust - /// let vec: Vec = vec![]; + /// let vec: Vec = Vec::new(); /// if vec.len() <= 0 {} /// if 100 > std::i32::MAX {} /// ``` @@ -1679,9 +1686,9 @@ fn detect_absurd_comparison<'a, 'tcx>( lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, ) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { - use crate::types::AbsurdComparisonResult::*; - use crate::types::ExtremeType::*; - use crate::utils::comparisons::*; + 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 @@ -1694,12 +1701,7 @@ fn detect_absurd_comparison<'a, 'tcx>( return None; } - let normalized = normalize_comparison(op, lhs, rhs); - let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { - val - } else { - 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); @@ -1726,7 +1728,7 @@ fn detect_absurd_comparison<'a, 'tcx>( } fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option> { - use crate::types::ExtremeType::*; + use crate::types::ExtremeType::{Maximum, Minimum}; let ty = cx.tables.expr_ty(expr); @@ -1755,8 +1757,8 @@ fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx 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::*; - use crate::types::ExtremeType::*; + 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) { @@ -1863,7 +1865,7 @@ impl Ord for FullInt { } fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { - use std::*; + use std::{i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize}; if let ExprKind::Cast(ref cast_exp, _) = expr.kind { let pre_cast_ty = cx.tables.expr_ty(cast_exp); @@ -1963,7 +1965,7 @@ fn upcast_comparison_bounds_err<'a, 'tcx>( rhs: &'tcx Expr<'_>, invert: bool, ) { - use crate::utils::comparisons::*; + use crate::utils::comparisons::Rel; if let Some((lb, ub)) = lhs_bounds { if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { @@ -2307,7 +2309,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { walk_ty(self, t); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -2384,8 +2386,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't walk_expr(self, e); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index eaf9f2e85cb6..d073c197656c 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -1,10 +1,10 @@ use crate::utils::{is_allowed, snippet, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::*; +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 syntax::ast::LitKind; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 023096398756..86c469a4dccf 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -1,9 +1,9 @@ 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; -use syntax::ast::*; declare_clippy_lint! { /// **What it does:** Checks for imports that remove "unsafe" from an item's diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 9f1a7aaf977c..a45b4a6869c5 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedSelf { kind: AssocItemKind::Method { has_self: true }, .. } = impl_item_ref; - if let ImplItemKind::Method(_, body_id) = &impl_item.kind; + if let ImplItemKind::Fn(_, body_id) = &impl_item.kind; let body = cx.tcx.hir().body(*body_id); if !body.params.is_empty(); then { @@ -107,7 +107,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> { walk_path(self, path); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 4fd421e4af94..767b48d4a096 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -2,8 +2,8 @@ use crate::utils::{higher::if_block, match_type, paths, span_lint_and_then, usag use if_chain::if_chain; use rustc::hir::map::Map; use rustc::lint::in_external_macro; -use rustc_hir::intravisit::*; -use rustc_hir::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -185,8 +185,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 4160370dd15a..73a986b79011 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -7,7 +7,10 @@ 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::*; +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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; @@ -110,7 +113,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TraitImplTyVisitor<'a, 'tcx> { walk_ty(self, t) } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -125,12 +128,7 @@ fn check_trait_method_impl_decl<'a, 'tcx>( let trait_method = cx .tcx .associated_items(impl_trait_ref.def_id) - .find(|assoc_item| { - assoc_item.kind == ty::AssocKind::Method - && cx - .tcx - .hygienic_eq(impl_item.ident, assoc_item.ident, impl_trait_ref.def_id) - }) + .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Method, impl_trait_ref.def_id) .expect("impl method matches a trait method"); let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); @@ -140,7 +138,7 @@ fn check_trait_method_impl_decl<'a, 'tcx>( let impl_method_sig = cx.tcx.fn_sig(impl_method_def_id); let impl_method_sig = cx.tcx.erase_late_bound_regions(&impl_method_sig); - let output_ty = if let FunctionRetTy::Return(ty) = &impl_decl.output { + let output_ty = if let FnRetTy::Return(ty) = &impl_decl.output { Some(&**ty) } else { None @@ -197,7 +195,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf { 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::Method(FnSig{ decl: impl_decl, .. }, impl_body_id) + if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id) = &impl_item.kind { let item_type = cx.tcx.type_of(impl_def_id); check_trait_method_impl_decl(cx, item_type, impl_item, impl_decl, &impl_trait_ref); @@ -277,7 +275,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::All(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) } } diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 8f886d174f1e..e27e1b7f2e6c 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -1,7 +1,7 @@ -use rustc::session::Session; +use rustc_ast::ast; use rustc_errors::Applicability; +use rustc_session::Session; use std::str::FromStr; -use syntax::ast; /// Deprecation status of attributes known by Clippy. #[allow(dead_code)] diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 52ccdf95d57b..4fcaf4264d77 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -3,15 +3,15 @@ use crate::utils::{get_attr, higher}; use rustc::hir::map::Map; -use rustc::session::Session; +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_session::Session; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::{Attribute, LitFloatType, LitKind}; -use syntax::walk_list; declare_clippy_lint! { /// **What it does:** Generates clippy code that detects the offending pattern @@ -157,7 +157,7 @@ impl PrintVisitor { } fn next(&mut self, s: &'static str) -> String { - use std::collections::hash_map::Entry::*; + use std::collections::hash_map::Entry::{Occupied, Vacant}; match self.ids.entry(s) { // already there: start numbering from `1` Occupied(mut occ) => { @@ -689,7 +689,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 584cde2678f3..a1d7bebc7b56 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -3,12 +3,12 @@ #![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}; -use syntax::ast::{LitKind, MetaItemKind, NestedMetaItem}; /// Gets the configuration file from arguments. pub fn file_from_args(args: &[NestedMetaItem]) -> Result, (&'static str, Span)> { diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 9739b118f749..cc519d525521 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -6,27 +6,16 @@ use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; use std::env; -/// Wrapper around `DiagnosticBuilder` that adds a link to Clippy documentation for the emitted lint -struct DiagnosticWrapper<'a>(DiagnosticBuilder<'a>); - -impl<'a> Drop for DiagnosticWrapper<'a> { - fn drop(&mut self) { - self.0.emit(); - } -} - -impl<'a> DiagnosticWrapper<'a> { - fn docs_link(&mut self, lint: &'static Lint) { - if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { - self.0.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) - )); - } +fn docs_link(db: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { + if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { + db.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) + )); } } @@ -48,7 +37,11 @@ impl<'a> DiagnosticWrapper<'a> { /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: &str) { - DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).docs_link(lint); + cx.struct_span_lint(lint, sp, |ldb| { + let mut db = ldb.build(msg); + docs_link(&mut db, lint); + db.emit(); + }); } /// Same as `span_lint` but with an extra `help` message. @@ -70,9 +63,12 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into(cx: &'a T, lint: &'static Lint, span: Span, msg: &str, help: &str) { - let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg)); - db.0.help(help); - db.docs_link(lint); + cx.struct_span_lint(lint, span, |ldb| { + let mut db = ldb.build(msg); + db.help(help); + docs_link(&mut db, lint); + db.emit(); + }); } /// Like `span_lint` but with a `note` section instead of a `help` message. @@ -104,26 +100,36 @@ pub fn span_lint_and_note<'a, T: LintContext>( note_span: Span, note: &str, ) { - let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg)); - if note_span == span { - db.0.note(note); - } else { - db.0.span_note(note_span, note); - } - db.docs_link(lint); + cx.struct_span_lint(lint, span, |ldb| { + let mut db = ldb.build(msg); + if note_span == span { + db.note(note); + } else { + db.span_note(note_span, note); + } + docs_link(&mut db, lint); + db.emit(); + }); } 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>), { - let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)); - f(&mut db.0); - db.docs_link(lint); + cx.struct_span_lint(lint, sp, |ldb| { + let mut db = ldb.build(msg); + f(&mut db); + docs_link(&mut db, lint); + db.emit(); + }); } pub fn span_lint_hir(cx: &LateContext<'_, '_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { - DiagnosticWrapper(cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg)).docs_link(lint); + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |ldb| { + let mut db = ldb.build(msg); + docs_link(&mut db, lint); + db.emit(); + }); } pub fn span_lint_hir_and_then( @@ -134,9 +140,12 @@ pub fn span_lint_hir_and_then( msg: &str, f: impl FnOnce(&mut DiagnosticBuilder<'_>), ) { - let mut db = DiagnosticWrapper(cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg)); - f(&mut db.0); - db.docs_link(lint); + cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |ldb| { + let mut db = ldb.build(msg); + f(&mut db); + docs_link(&mut db, lint); + db.emit(); + }); } /// Add a span lint with a suggestion on how to fix it. diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index b046d7dc08f1..f27cc57fb545 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -6,9 +6,9 @@ use crate::utils::{is_expn_of, match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc::ty; +use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::LateContext; -use syntax::ast; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c209ebaaf137..dcdec2e7eb6a 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -2,11 +2,15 @@ use crate::consts::{constant_context, constant_simple}; use crate::utils::differing_macro_contexts; use rustc::ich::StableHashingContextProvider; use rustc::ty::TypeckTables; +use rustc_ast::ast::Name; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::*; +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 std::hash::Hash; -use syntax::ast::Name; /// Type used to check whether two ast are the same. This is different from the /// operator @@ -633,10 +637,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(&arg); } match bfn.decl.output { - FunctionRetTy::DefaultReturn(_) => { + FnRetTy::DefaultReturn(_) => { ().hash(&mut self.s); }, - FunctionRetTy::Return(ref ty) => { + FnRetTy::Return(ref ty) => { self.hash_ty(ty); }, } diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index aa76ada7b484..c6a47f05e248 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -1,12 +1,12 @@ //! checks for attributes use crate::utils::get_attr; -use rustc::session::Session; +use rustc_ast::ast::Attribute; use rustc_hir as hir; use rustc_hir::print; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::Session; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::Attribute; declare_clippy_lint! { /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]` @@ -62,7 +62,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DeepCodeInspector { println!("associated constant"); print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); }, - hir::ImplItemKind::Method(..) => println!("method"), + hir::ImplItemKind::Fn(..) => println!("method"), hir::ImplItemKind::TyAlias(_) => println!("associated type"), hir::ImplItemKind::OpaqueTy(_) => println!("existential type"), } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index af07cf666583..7dbb0d5a9ab8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -4,20 +4,18 @@ use crate::utils::{ }; use if_chain::if_chain; use rustc::hir::map::Map; +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::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_session::declare_tool_lint; -use rustc_session::{declare_lint_pass, impl_lint_pass}; +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 syntax::ast; -use syntax::ast::{Crate as AstCrate, ItemKind, LitKind, Name}; -use syntax::visit::FnKind; declare_clippy_lint! { /// **What it does:** Checks for various things we like to keep tidy in clippy. @@ -301,8 +299,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { self.output.insert(path.segments[0].ident.name); } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { - NestedVisitorMap::All(&self.cx.tcx.hir()) + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) } } @@ -380,18 +378,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: &ast::FnDecl, _: Span, _: ast::NodeId) { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { if is_trigger_fn(fn_kind) { - panic!("Testing the ICE message"); + panic!("Would you like some help with that?"); } } } fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) => { - ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy" - }, + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", FnKind::Closure(..) => false, } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4b7e21bc3bad..c1c5bcc1dd1b 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -12,6 +12,7 @@ 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; @@ -27,13 +28,13 @@ use if_chain::if_chain; use matches::matches; use rustc::hir::map::Map; use rustc::traits; -use rustc::traits::predicate_for_trait_def; use rustc::ty::{ self, layout::{self, IntegerExt}, subst::GenericArg, - Binder, Ty, TyCtxt, + Binder, Ty, TyCtxt, TypeFoldable, }; +use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_hir as hir; @@ -41,16 +42,23 @@ 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::*; +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_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_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 syntax::ast::{self, Attribute, LitKind}; use crate::consts::{constant, Constant}; -use crate::reexport::*; +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). @@ -93,7 +101,7 @@ pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool { .. }) | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Method(ref sig, _), + kind: ImplItemKind::Fn(ref sig, _), .. }) => sig.header.constness == Constness::Const, _ => false, @@ -289,8 +297,8 @@ pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath<'_>, id: hir::HirI match qpath { hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) => { - if cx.tcx.has_typeck_tables(id.owner_def_id()) { - cx.tcx.typeck_tables_of(id.owner_def_id()).qpath_res(qpath, id) + if cx.tcx.has_typeck_tables(id.owner.to_def_id()) { + cx.tcx.typeck_tables_of(id.owner.to_def_id()).qpath_res(qpath, id) } else { Res::Err } @@ -307,7 +315,7 @@ pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option Some(trait_id), + Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path), _ => None, } @@ -440,10 +448,11 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); match cx.tcx.hir().find(parent_id) { - Some(Node::Item(&Item { ref ident, .. })) => Some(ident.name), - Some(Node::TraitItem(&TraitItem { ident, .. })) | Some(Node::ImplItem(&ImplItem { ident, .. })) => { - Some(ident.name) - }, + Some( + Node::Item(Item { ident, .. }) + | Node::TraitItem(TraitItem { ident, .. }) + | Node::ImplItem(ImplItem { ident, .. }), + ) => Some(ident.name), _ => None, } } @@ -471,7 +480,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName { self.result = true; } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } @@ -533,19 +542,49 @@ pub fn snippet_opt(cx: &T, span: Span) -> Option { 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 +/// 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, expr.span, "..") +/// snippet_block(cx, block.span, "..", None) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } /// ``` -pub fn snippet_block<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { +/// +/// ```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, +) -> Cow<'a, str> { let snip = snippet(cx, span, default); - trim_multiline(snip, true) + 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 @@ -554,27 +593,73 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, default: &'a str, + indent_relative_to: Option, applicability: &mut Applicability, ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); - trim_multiline(snip, true) + 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(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(cx: &T, span: Span) -> Option { + 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 a new Span that covers the full last line of the given Span -pub fn last_line_of_span(cx: &T, span: Span) -> Span { +/// 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(cx: &T, span: Span) -> Option { + 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(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]; - let span = Span::new(*line_start, span.hi(), span.ctxt()); - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(first_ch_pos) = snip.find(|c: char| !c.is_whitespace()); - then { - span.with_lo(span.lo() + BytePos::from_usize(first_ch_pos)) - } else { - span - } - } + 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`. @@ -584,8 +669,9 @@ pub fn expr_block<'a, T: LintContext>( expr: &Expr<'_>, option: Option, default: &'a str, + indent_relative_to: Option, ) -> Cow<'a, str> { - let code = snippet_block(cx, expr.span, default); + 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))) @@ -600,14 +686,14 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, '\t'); - trim_multiline_inner(s_tab, ignore_first, ' ') +fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> 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, ch: char) -> Cow<'_, str> { - let x = s +fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { + let mut x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -620,6 +706,9 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, ch: char) -> Cow<'_ }) .min() .unwrap_or(0); + if let Some(indent) = indent { + x = x.saturating_sub(indent); + } if x > 0 { Cow::Owned( s.lines() @@ -669,7 +758,7 @@ pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, hir_id: HirId) .. }) | Node::ImplItem(&ImplItem { - kind: ImplItemKind::Method(_, eid), + kind: ImplItemKind::Fn(_, eid), .. }) => match cx.tcx.hir().body(eid).value.kind { ExprKind::Block(ref block, _) => Some(block), @@ -837,7 +926,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp 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, ..) | Res::Def(DefKind::Ctor(..), _) => true, + def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; @@ -1141,87 +1230,6 @@ pub fn is_normalizable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, param_env: ty::Para }) } -#[cfg(test)] -mod test { - use super::{trim_multiline, without_block_comments}; - - #[test] - fn test_trim_multiline_single_line() { - assert_eq!("", trim_multiline("".into(), false)); - assert_eq!("...", trim_multiline("...".into(), false)); - assert_eq!("...", trim_multiline(" ...".into(), false)); - assert_eq!("...", trim_multiline("\t...".into(), false)); - assert_eq!("...", trim_multiline("\t\t...".into(), false)); - } - - #[test] - #[rustfmt::skip] - fn test_trim_multiline_block() { - assert_eq!("\ - if x { - y - } else { - z - }", trim_multiline(" if x { - y - } else { - z - }".into(), false)); - assert_eq!("\ - if x { - \ty - } else { - \tz - }", trim_multiline(" if x { - \ty - } else { - \tz - }".into(), false)); - } - - #[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)); - } - - #[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"]); - } -} - pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, did: DefId, syms: &[&str]) -> bool { let path = cx.get_def_path(did); path.len() == syms.len() && path.into_iter().zip(syms.iter()).all(|(a, &b)| a.as_str() == b) @@ -1288,17 +1296,19 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - use ty::TyKind::*; match ty.kind { - Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), - Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), - Slice(ref ty) | Array(ref ty, _) | RawPtr(ty::TypeAndMut { ref ty, .. }) | Ref(_, ref ty, _) => { + 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) }, - Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), - Opaque(ref def_id, _) => { + 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() { @@ -1308,7 +1318,7 @@ pub fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> boo } false }, - Dynamic(binder, _) => { + 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() { @@ -1346,7 +1356,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool } pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { - krate.attrs.iter().any(|attr| { + krate.item.attrs.iter().any(|attr| { if let ast::AttrKind::Normal(ref attr) = attr.kind { attr.path == symbol::sym::no_std } else { @@ -1369,3 +1379,105 @@ pub fn is_trait_impl_item(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool { 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 }) + .collect(); + !traits::normalize_and_test_predicates(cx.tcx, traits::elaborate_predicates(cx.tcx, predicates).collect()) +} + +#[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"]); + } +} diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs new file mode 100644 index 000000000000..8b3492724e10 --- /dev/null +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -0,0 +1,227 @@ +use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; + +#[derive(Debug, PartialEq)] +pub(crate) 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(crate) 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::from_lit_kind(src, &lit.kind) + } + + pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { + 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 { + 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) +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 0af7f946fa9d..6cb1f694fd5e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -24,12 +24,14 @@ pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "defa 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"]; @@ -68,7 +70,6 @@ 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 PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"]; 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"]; diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_lints/src/utils/ptr.rs index 8a59bcc98549..238c2277a932 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_lints/src/utils/ptr.rs @@ -1,11 +1,11 @@ use crate::utils::{get_pat_name, match_var, snippet}; use rustc::hir::map::Map; +use rustc_ast::ast::Name; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; use rustc_span::source_map::Span; use std::borrow::Cow; -use syntax::ast::Name; pub fn get_spans( cx: &LateContext<'_, '_>, @@ -78,7 +78,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 3e694f40d193..b2c4027b9f11 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -3,6 +3,8 @@ use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; use matches::matches; +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; @@ -12,11 +14,6 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use syntax::ast; -use syntax::token; -use syntax::util::parser::AssocOp; - -pub use crate::literal_representation::format_numeric_literal; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -133,7 +130,7 @@ impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { - use syntax::ast::RangeLimits; + use rustc_ast::ast::RangeLimits; let snippet = snippet(cx, expr.span, default); @@ -157,7 +154,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::InlineAsm(..) | ast::ExprKind::Lit(..) | ast::ExprKind::Loop(..) - | ast::ExprKind::Mac(..) + | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Paren(..) | ast::ExprKind::Path(..) @@ -427,7 +424,10 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: &AssocOp) -> Associativity { - use syntax::util::parser::AssocOp::*; + 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, @@ -440,7 +440,7 @@ fn associativity(op: &AssocOp) -> Associativity { /// Converts a `hir::BinOp` to the corresponding assigning binary operator. fn hirbinop2assignop(op: hir::BinOp) -> AssocOp { - use syntax::token::BinOpToken::*; + use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star}; AssocOp::AssignOp(match op.node { hir::BinOpKind::Add => Plus, @@ -467,8 +467,10 @@ fn hirbinop2assignop(op: hir::BinOp) -> AssocOp { /// Converts an `ast::BinOp` to the corresponding assigning binary operator. fn astbinop2assignop(op: ast::BinOp) -> AssocOp { - use syntax::ast::BinOpKind::*; - use syntax::token::BinOpToken; + 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, diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 7939d7d0863c..a08251c1a4eb 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,14 +1,15 @@ use crate::utils::match_var; use rustc::hir::map::Map; use rustc::ty; +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::*; +use rustc_hir::{Expr, HirId, Path}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_typeck::expr_use_visitor::*; -use syntax::ast; +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> { @@ -16,7 +17,7 @@ pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, used_mutably: FxHashSet::default(), skip: false, }; - let def_id = def_id::DefId::local(expr.hir_id.owner); + let def_id = expr.hir_id.owner.to_def_id(); cx.tcx.infer_ctxt().enter(|infcx| { ExprUseVisitor::new(&mut delegate, &infcx, def_id, cx.param_env, cx.tables).walk_expr(expr); }); @@ -92,7 +93,7 @@ impl<'tcx> Visitor<'tcx> for UsedVisitor { } } - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index bc2c827b0afd..726a34856aeb 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -3,7 +3,7 @@ use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_su use if_chain::if_chain; use rustc::ty::{self, Ty}; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs new file mode 100644 index 000000000000..37885317c58e --- /dev/null +++ b/clippy_lints/src/verbose_file_reads.rs @@ -0,0 +1,83 @@ +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, + complexity, + "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`", + "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`", + "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 +} diff --git a/clippy_lints/src/wildcard_dependencies.rs b/clippy_lints/src/wildcard_dependencies.rs index 2aa68842da41..035a10b1a247 100644 --- a/clippy_lints/src/wildcard_dependencies.rs +++ b/clippy_lints/src/wildcard_dependencies.rs @@ -1,8 +1,8 @@ use crate::utils::span_lint; +use rustc_ast::ast::Crate; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; -use syntax::ast::*; use if_chain::if_chain; diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs new file mode 100644 index 000000000000..348d13ef41f1 --- /dev/null +++ b/clippy_lints/src/wildcard_imports.rs @@ -0,0 +1,156 @@ +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.to_def_id()); + if !used_imports.is_empty(); // Already handled by `unused_imports` + then { + let mut applicability = Applicability::MachineApplicable; + let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); + let (span, braced_glob) = if import_source_snippet.is_empty() { + // This is a `_::{_, *}` import + // In this case `use_path.span` is empty and ends directly in front of the `*`, + // so we need to extend it by one byte. + ( + use_path.span.with_hi(use_path.span.hi() + BytePos(1)), + true, + ) + } else { + // In this case, the `use_path.span` ends right before the `::*`, so we need to + // extend it up to the `*`. Since it is hard to find the `*` in weird + // formattings like `use _ :: *;`, we extend it up to, but not including the + // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we + // can just use the end of the item span + let mut span = use_path.span.with_hi(item.span.hi()); + if snippet(cx, span, "").ends_with(';') { + span = use_path.span.with_hi(item.span.hi() - BytePos(1)); + } + ( + span, + false, + ) + }; + + let imports_string = if used_imports.len() == 1 { + used_imports.iter().next().unwrap().to_string() + } else { + let mut imports = used_imports + .iter() + .map(ToString::to_string) + .collect::>(); + 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, + ); + } + } + } +} diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0be52c2aa3d6..5ad43ad55a36 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,16 +2,16 @@ 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_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span}; -use syntax::ast::*; -use syntax::token; -use syntax::tokenstream::TokenStream; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -175,7 +175,12 @@ declare_clippy_lint! { "writing a literal with a format string" } -declare_lint_pass!(Write => [ +#[derive(Default)] +pub struct Write { + in_debug_impl: bool, +} + +impl_lint_pass!(Write => [ PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, @@ -187,10 +192,34 @@ declare_lint_pass!(Write => [ ]); impl EarlyLintPass for Write { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) { + 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), _) = check_tts(cx, &mac.args.inner_tokens(), false) { + 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, @@ -205,7 +234,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -226,7 +255,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -247,7 +276,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = check_tts(cx, &mac.args.inner_tokens(), true) { + 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( @@ -296,127 +325,136 @@ fn newline_span(fmtstr: &StrLit) -> Span { sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) } -/// 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>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option, Option) { - use fmt_macros::*; - let tts = tts.clone(); - - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None); - let mut expr: Option = 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), +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, Option) { + use fmt_macros::{ + AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, Parser, Piece, }; - // might be `writeln!(foo)` - if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() { - return (None, expr); - } - } + let tts = tts.clone(); - 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 arg.format.ty == "?" { - // FIXME: modify rustc's fmt string parser to give us the current span - span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting"); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); + let mut expr: Option = 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); } - 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, + + let fmtstr = match parser.parse_str_lit() { + Ok(fmtstr) => fmtstr, + Err(_) => return (None, expr), }; - if !parser.eat(&token::Comma) { - return (Some(fmtstr), 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 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(_) => {}, + 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, 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"); } - } - if all_simple && seen { - span_lint(cx, lint, rhs.span, "literal with an empty format string"); } } - } - }, - _ => idx += 1, + }, + _ => idx += 1, + } } } } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index f36da58843f3..42cb9a77db02 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,7 +1,7 @@ use crate::consts::{constant_simple, Constant}; use crate::utils::span_lint_and_help; use if_chain::if_chain; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 99178c2d75b9..1d78a27820a6 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -1,4 +1,4 @@ -## Adding a new lint +# 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 @@ -25,14 +25,14 @@ because that's clearly a non-descriptive name. - [PR Checklist](#pr-checklist) - [Cheatsheet](#cheatsheet) -### Setup +## 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 +## 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 @@ -45,7 +45,7 @@ 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 +## Testing Let's write some tests first that we can execute while we iterate on our lint. @@ -88,12 +88,10 @@ fn main() { let a = A; a.foo(); } - ``` -Now we can run the test with `TESTNAME=foo_functions cargo uitest`. -Currently this test will fail. If you go through the output you will see that we -are told that `clippy::foo_functions` is an unknown lint, which is expected. +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. @@ -105,24 +103,26 @@ 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. -### Rustfix tests +## 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](https://github.com/rust-lang-nursery/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. +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. -### Edition 2018 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 `// compile-flags: --edition 2018` at the top of the test file. +add `// edition:2018` at the top of the test file (note that it's space-sensitive). -### Testing manually +## 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 @@ -131,16 +131,16 @@ 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 +## 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::{LintArray, LintPass, EarlyLintPass, EarlyContext}; +use rustc_lint::{EarlyLintPass, EarlyContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use syntax::ast::*; +use rustc_ast::ast::*; ``` The next step is to update the lint declaration. Lints are declared using the @@ -167,12 +167,12 @@ declare_clippy_lint! { ``` * The section of lines prefixed with `///` constitutes the lint documentation -section. This is the default documentation style and will be displayed at -https://rust-lang.github.io/rust-clippy/master/index.html. -* `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`. + 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 @@ -191,22 +191,23 @@ declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]); impl EarlyLintPass for FooFunctions {} ``` -Don't worry about the `name` method here. As long as it includes the name of the -lint pass it should be fine. - -The new lint automation runs `update_lints`, which automates some things, but 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`: +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 -reg.register_early_lint_pass(box foo_functions::FooFunctions); +store.register_early_pass(|| box foo_functions::FooFunctions); ``` -This should fix the `unknown clippy lint: clippy::foo_functions` error that we -saw when we executed our tests the first time. The next decision we have to make -is which lint pass our lint is going to need. +[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 +## 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. @@ -224,7 +225,10 @@ 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. -### Emitting a lint +[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. @@ -233,7 +237,7 @@ 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<'_>, _: &FnDecl, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { // TODO: Emit lint here } } @@ -255,7 +259,7 @@ automatically. This is how it looks: ```rust impl EarlyLintPass for FooFunctions { - fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, _: &FnDecl, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { span_lint_and_help( cx, FOO_FUNCTIONS, @@ -269,20 +273,23 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. -### Adding the lint logic +[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 two relevant variants for us `FnKind::ItemFn` and `FnKind::Method`. -Both provide access to the name of the function/method via an [`Ident`][ident]. +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<'_>, _: &FnDecl, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { if is_foo_fn(fn_kind) { span_lint_and_help( cx, @@ -307,9 +314,11 @@ In our example, `is_foo_fn` looks like: fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) => { - ident.name == "foo" - }, + FnKind::Fn(_, ident, ..) => { + // check if `fn` name is `foo` + ident.name.as_str() == "foo" + } + // ignore closures FnKind::Closure(..) => false } } @@ -325,13 +334,18 @@ implementation is not violating any Clippy lints itself. That should be it for the lint implementation. Running `cargo test` should now pass. -### Author lint +[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][Play]. +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. @@ -341,7 +355,9 @@ see the generated code in the output below. If the command was executed successfully, you can copy the code over to where you are implementing your lint. -### Documentation +[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. @@ -374,11 +390,13 @@ declare_clippy_lint! { Once your lint is merged, this documentation will show up in the [lint list][lint_list]. -### Running rustfmt +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html + +## Running rustfmt -[Rustfmt](https://github.com/rust-lang/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. +[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`: @@ -389,13 +407,17 @@ 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. -### Debugging +[Rustfmt]: https://github.com/rust-lang/rustfmt -If you want to debug parts of your lint implementation, you can use the `dbg!` +## 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. -### PR Checklist +[`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: @@ -408,7 +430,7 @@ Before submitting your PR make sure you followed all of the basic requirements: - [ ] Added lint documentation - [ ] Run `cargo dev fmt` -### Cheatsheet +## Cheatsheet Here are some pointers to things you are likely going to need for every lint: @@ -419,46 +441,33 @@ Here are some pointers to things you are likely going to need for every lint: * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] -* [The rustc guide][rustc_guide] explains a lot of internal compiler concepts +* [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] -* [`syntax::ast`][ast] +* [`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, IRC or in the issue/PR. +don't hesitate to ask on [Discord] or in the issue/PR. -[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html -[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/bd23cb89ec0ea63403a17d3fc5e50c88e38dd54f/clippy_lints/src/lib.rs#L43 -[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/a71acac1da7eaf667ab90a1d65d10e5cc4b80191/clippy_lints/src/lib.rs#L39 -[compilation_stages]: https://rust-lang.github.io/rustc-guide/high-level-overview.html#the-main-stages-of-compilation -[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/trait.EarlyLintPass.html#method.check_fn -[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 -[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/visit/enum.FnKind.html -[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs -[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map/symbol/struct.Ident.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 [if_chain]: https://docs.rs/if_chain/*/if_chain/ -[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/sty/index.html -[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/index.html [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/lint/fn.in_external_macro.html -[play]: https://play.rust-lang.org -[author_example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f093b986e80ad62f3b67a1f24f5e66e2 -[rustc_guide]: https://rust-lang.github.io/rustc-guide/ +[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/ +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/sty/index.html +[Discord]: https://discord.gg/rust-lang diff --git a/doc/changelog_update.md b/doc/changelog_update.md index cabf25135dac..5c7898fc465f 100644 --- a/doc/changelog_update.md +++ b/doc/changelog_update.md @@ -55,10 +55,13 @@ try to keep it somewhat coherent. The order should roughly be: 1. New lints -2. Changes that expand what code existing lints cover -3. ICE fixes +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. diff --git a/etc/relicense/RELICENSE_DOCUMENTATION.md b/etc/relicense/RELICENSE_DOCUMENTATION.md index 847e66e65e35..fcd7abbf3f16 100644 --- a/etc/relicense/RELICENSE_DOCUMENTATION.md +++ b/etc/relicense/RELICENSE_DOCUMENTATION.md @@ -1,36 +1,69 @@ -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) +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: +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)) +- #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) +- @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. +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)) +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)) diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 18341ca2f280..6027538dc4ab 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -53,7 +53,7 @@ This gives the following output in clippy: ## License -Copyright 2014-2019 The Rust Project Developers +Copyright 2014-2020 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/driver.rs b/src/driver.rs index 27ff7fa9116e..7865f6bb9dd0 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -15,7 +15,7 @@ extern crate rustc_interface; use rustc::ty::TyCtxt; use rustc_interface::interface; -use rustc_tools_util::*; +use rustc_tools_util::VersionInfo; use lazy_static::lazy_static; use std::borrow::Cow; @@ -63,10 +63,10 @@ fn test_arg_value() { assert_eq!(arg_value(args, "--foo", |_| true), None); } -#[allow(clippy::too_many_lines)] +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(); @@ -93,7 +93,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { #[allow(clippy::find_map, clippy::filter_map)] fn describe_lints() { - use lintlist::*; + use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS}; use std::collections::HashSet; println!( @@ -281,6 +281,17 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { } } +fn toolchain_path(home: Option, toolchain: Option) -> Option { + 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); @@ -301,22 +312,21 @@ pub fn main() { // - 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 = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - home.and_then(|home| { - toolchain.map(|toolchain| { - let mut path = PathBuf::from(home); - path.push("toolchains"); - path.push(toolchain); - path - }) - }) + 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") @@ -328,6 +338,15 @@ pub fn main() { .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"); @@ -387,7 +406,7 @@ pub fn main() { } } let mut clippy = ClippyCallbacks; - let mut default = rustc_driver::DefaultCallbacks; + 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) diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f725ce2563fa..fd948953ea23 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 354] = [ +pub const ALL_LINTS: [Lint; 361] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -191,7 +191,7 @@ pub const ALL_LINTS: [Lint; 354] = [ }, Lint { name: "chars_next_cmp", - group: "complexity", + group: "style", desc: "using `.chars().next()` to check if a string starts with a char", deprecation: None, module: "methods", @@ -460,7 +460,7 @@ pub const ALL_LINTS: [Lint; 354] = [ group: "pedantic", desc: "use items that import all variants of an enum", deprecation: None, - module: "enum_glob_use", + module: "wildcard_imports", }, Lint { name: "enum_variant_names", @@ -495,7 +495,7 @@ pub const ALL_LINTS: [Lint; 354] = [ group: "style", desc: "excessive precision for float literal", deprecation: None, - module: "excessive_precision", + module: "float_literal", }, Lint { name: "exit", @@ -749,6 +749,13 @@ pub const ALL_LINTS: [Lint; 354] = [ deprecation: None, module: "implicit_return", }, + 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", @@ -1001,6 +1008,20 @@ pub const ALL_LINTS: [Lint; 354] = [ 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", @@ -1015,13 +1036,6 @@ pub const ALL_LINTS: [Lint; 354] = [ deprecation: None, module: "loops", }, - Lint { - name: "manual_mul_add", - group: "nursery", - desc: "Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`", - deprecation: None, - module: "mul_add", - }, Lint { name: "manual_saturating_arithmetic", group: "style", @@ -1498,6 +1512,13 @@ pub const ALL_LINTS: [Lint; 354] = [ 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", @@ -1771,6 +1792,13 @@ pub const ALL_LINTS: [Lint; 354] = [ deprecation: None, module: "replace_consts", }, + 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", @@ -1946,6 +1974,13 @@ pub const ALL_LINTS: [Lint; 354] = [ 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", @@ -2221,7 +2256,7 @@ pub const ALL_LINTS: [Lint; 354] = [ }, Lint { name: "unneeded_field_pattern", - group: "style", + group: "restriction", desc: "struct fields bound to a wildcard instead of using `..`", deprecation: None, module: "misc_early", @@ -2366,6 +2401,13 @@ pub const ALL_LINTS: [Lint; 354] = [ deprecation: None, module: "bit_mask", }, + Lint { + name: "verbose_file_reads", + group: "complexity", + desc: "use of `File::read_to_end` or `File::read_to_string`", + deprecation: None, + module: "verbose_file_reads", + }, Lint { name: "while_immutable_condition", group: "correctness", @@ -2401,6 +2443,13 @@ pub const ALL_LINTS: [Lint; 354] = [ 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", diff --git a/src/main.rs b/src/main.rs index 8f9afb95337d..93e6996be069 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use rustc_tools_util::*; +use rustc_tools_util::VersionInfo; const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code. diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index 5b2de05cb174..3c385343f701 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -1,75 +1,29 @@ -use cargo_metadata::{Message::CompilerArtifact, MetadataCommand}; +use lazy_static::lazy_static; use std::env; -use std::ffi::OsStr; -use std::mem; use std::path::PathBuf; -use std::process::Command; -pub struct BuildInfo { - cargo_target_dir: PathBuf, -} - -impl BuildInfo { - pub fn new() -> Self { - let data = MetadataCommand::new().exec().unwrap(); - Self { - cargo_target_dir: data.target_directory, +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 fn host_lib(&self) -> PathBuf { - if let Some(path) = option_env!("HOST_LIBS") { - PathBuf::from(path) - } else { - self.cargo_target_dir.join(env!("PROFILE")) - } - } - - pub fn target_lib(&self) -> PathBuf { + }; + pub static ref TARGET_LIB: PathBuf = { if let Some(path) = option_env!("TARGET_LIBS") { path.into() } else { - let mut dir = self.cargo_target_dir.clone(); + 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 } - } - - pub fn clippy_driver_path(&self) -> PathBuf { - if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") { - PathBuf::from(path) - } else { - self.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. - pub fn third_party_crates() -> Vec<(&'static str, PathBuf)> { - const THIRD_PARTY_CRATES: [&str; 4] = ["serde", "serde_derive", "regex", "clippy_lints"]; - let cargo = env::var_os("CARGO"); - let cargo = cargo.as_deref().unwrap_or_else(|| OsStr::new("cargo")); - let output = Command::new(cargo) - .arg("build") - .arg("--test=compile-test") - .arg("--message-format=json") - .output() - .unwrap(); + }; +} - let mut crates = Vec::with_capacity(THIRD_PARTY_CRATES.len()); - for message in cargo_metadata::parse_messages(output.stdout.as_slice()) { - if let CompilerArtifact(mut artifact) = message.unwrap() { - if let Some(&krate) = THIRD_PARTY_CRATES.iter().find(|&&krate| krate == artifact.target.name) { - crates.push((krate, mem::take(&mut artifact.filenames[0]))); - } - } - } - crates - } +#[must_use] +pub fn is_rustc_test_suite() -> bool { + option_env!("RUSTC_TEST_SUITE").is_some() } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 7cfbfcf16a99..de2cf6d7873f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,4 @@ -#![feature(test)] +#![feature(test)] // compiletest_rs requires this attribute use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; @@ -11,51 +11,87 @@ use std::path::{Path, PathBuf}; mod cargo; -#[must_use] -fn rustc_test_suite() -> Option { - option_env!("RUSTC_TEST_SUITE").map(PathBuf::from) +fn host_lib() -> PathBuf { + if let Some(path) = option_env!("HOST_LIBS") { + PathBuf::from(path) + } else { + cargo::CARGO_TARGET_DIR.join(env!("PROFILE")) + } } -#[must_use] -fn rustc_lib_path() -> PathBuf { - option_env!("RUSTC_LIB_PATH").unwrap().into() +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 build_info = cargo::BuildInfo::new(); let mut config = compiletest::Config::default(); if let Ok(name) = env::var("TESTNAME") { config.filter = Some(name); } - if rustc_test_suite().is_some() { - let path = rustc_lib_path(); + if let Some(path) = option_env!("RUSTC_LIB_PATH") { + let path = PathBuf::from(path); config.run_lib_path = path.clone(); config.compile_lib_path = path; } - let disambiguated: Vec<_> = cargo::BuildInfo::third_party_crates() - .iter() - .map(|(krate, path)| format!("--extern {}={}", krate, path.display())) - .collect(); - config.target_rustcflags = Some(format!( "-L {0} -L {1} -Dwarnings -Zui-testing {2}", - build_info.host_lib().join("deps").display(), - build_info.target_lib().join("deps").display(), - disambiguated.join(" ") + host_lib().join("deps").display(), + cargo::TARGET_LIB.join("deps").display(), + third_party_crates(), )); - config.build_base = if rustc_test_suite().is_some() { - // we don't need access to the stderr files on travis + 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 { - build_info.host_lib().join("test_build_base") + host_lib().join("test_build_base") }; - config.rustc_path = build_info.clippy_driver_path(); + config.rustc_path = clippy_driver_path(); config } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 5458143ab1cc..81af3d3033b2 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,21 +1,20 @@ +// Dogfood cannot run on Windows +#![cfg(not(windows))] + use lazy_static::lazy_static; use std::path::PathBuf; use std::process::Command; -#[allow(dead_code)] mod cargo; lazy_static! { - static ref CLIPPY_PATH: PathBuf = { - let build_info = cargo::BuildInfo::new(); - build_info.target_lib().join("cargo-clippy") - }; + 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 option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) { + if cargo::is_rustc_test_suite() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -44,7 +43,7 @@ fn dogfood_clippy() { #[test] fn dogfood_subprojects() { // run clippy on remaining subprojects and fail the test if lint warnings are reported - if option_env!("RUSTC_TEST_SUITE").is_some() || cfg!(windows) { + if cargo::is_rustc_test_suite() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); diff --git a/tests/fmt.rs b/tests/fmt.rs index 2e33c3ed5e5b..3aff8741f605 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -3,7 +3,7 @@ use std::process::Command; #[test] fn fmt() { - if option_env!("RUSTC_TEST_SUITE").is_some() { + if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() { return; } diff --git a/tests/integration.rs b/tests/integration.rs index d14ced8ad4e7..4f373e034bc2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,8 +1,7 @@ #![cfg(feature = "integration")] -use git2::Repository; - use std::env; +use std::ffi::OsStr; use std::process::Command; #[cfg_attr(feature = "integration", test)] @@ -14,12 +13,19 @@ fn integration_test() { .nth(1) .expect("repo name should have format `/`"); - let repo_dir = tempfile::tempdir() - .expect("couldn't create temp dir") - .path() - .join(crate_name); + let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path(); + repo_dir.push(crate_name); - Repository::clone(&repo_url, &repo_dir).expect("clone of repo failed"); + 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"); @@ -59,16 +65,15 @@ fn integration_test() { panic!("query stack during panic in the output"); } else if stderr.contains("E0463") { 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(code) => { - if code == 0 { - println!("Compilation successful"); - } else { - eprintln!("Compilation failed. Exit code: {}", code); - } - }, + Some(0) => println!("Compilation successful"), + Some(code) => eprintln!("Compilation failed. Exit code: {}", code), None => panic!("Process terminated by signal"), } } diff --git a/tests/needless_continue_helpers.rs b/tests/needless_continue_helpers.rs deleted file mode 100644 index 255653b4737d..000000000000 --- a/tests/needless_continue_helpers.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Tests for the various helper functions used by the needless_continue -// lint that don't belong in utils. - -use clippy_lints::needless_continue::{erode_block, erode_from_back, erode_from_front}; - -#[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 = ""; - let got = erode_from_back(input); - assert_eq!(expected, got); -} - -#[test] -#[rustfmt::skip] -fn test_erode_from_front() { - let input = " - { - something(); - inside_a_block(); - } - "; - let expected = -" something(); - inside_a_block(); - } - "; - let got = erode_from_front(input); - println!("input: {}\nexpected:\n{}\ngot:\n{}", input, expected, got); - assert_eq!(expected, got); -} - -#[test] -#[rustfmt::skip] -fn test_erode_from_front_no_brace() { - let input = " - something(); - inside_a_block(); - "; - let expected = -"something(); - inside_a_block(); - "; - let got = erode_from_front(input); - println!("input: {}\nexpected:\n{}\ngot:\n{}", input, expected, got); - assert_eq!(expected, got); -} - -#[test] -#[rustfmt::skip] -fn test_erode_block() { - - let input = " - { - something(); - inside_a_block(); - } - "; - let expected = -" something(); - inside_a_block();"; - let got = erode_block(input); - println!("input: {}\nexpected:\n{}\ngot:\n{}", input, expected, got); - assert_eq!(expected, got); -} diff --git a/tests/ui/approx_const.rs b/tests/ui/approx_const.rs index 8c295d1438a7..e1c150fdefd4 100644 --- a/tests/ui/approx_const.rs +++ b/tests/ui/approx_const.rs @@ -45,6 +45,12 @@ fn main() { 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; diff --git a/tests/ui/approx_const.stderr b/tests/ui/approx_const.stderr index 71c1c360e74a..98b85443f0b7 100644 --- a/tests/ui/approx_const.stderr +++ b/tests/ui/approx_const.stderr @@ -96,23 +96,35 @@ error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it 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:48:17 + --> $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:49:21 + --> $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:52:18 + --> $DIR/approx_const.rs:58:18 | LL | let my_sq2 = 1.4142; | ^^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/arithmetic.stderr b/tests/ui/arithmetic.stderr deleted file mode 100644 index d999b69d7cbb..000000000000 --- a/tests/ui/arithmetic.stderr +++ /dev/null @@ -1,127 +0,0 @@ -error: integer arithmetic detected - --> $DIR/arithmetic.rs:13:5 - | -LL | 1 + i; - | ^^^^^ - | - = note: `-D clippy::integer-arithmetic` implied by `-D warnings` - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:14:5 - | -LL | i * 2; - | ^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:15:5 - | -LL | / 1 % -LL | | i / 2; // no error, this is part of the expression in the preceding line - | |_________^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:17:5 - | -LL | i - 2 + 2 - i; - | ^^^^^^^^^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:18:5 - | -LL | -i; - | ^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:30:5 - | -LL | i += 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:31:5 - | -LL | i -= 1; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:32:5 - | -LL | i *= 2; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:33:5 - | -LL | i /= 2; - | ^^^^^^ - -error: integer arithmetic detected - --> $DIR/arithmetic.rs:34:5 - | -LL | i %= 2; - | ^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:45:5 - | -LL | f * 2.0; - | ^^^^^^^ - | - = note: `-D clippy::float-arithmetic` implied by `-D warnings` - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:47:5 - | -LL | 1.0 + f; - | ^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:48:5 - | -LL | f * 2.0; - | ^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:49:5 - | -LL | f / 2.0; - | ^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:50:5 - | -LL | f - 2.0 * 4.2; - | ^^^^^^^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:51:5 - | -LL | -f; - | ^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:53:5 - | -LL | f += 1.0; - | ^^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:54:5 - | -LL | f -= 1.0; - | ^^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:55:5 - | -LL | f *= 2.0; - | ^^^^^^^^ - -error: floating-point arithmetic detected - --> $DIR/arithmetic.rs:56:5 - | -LL | f /= 2.0; - | ^^^^^^^^ - -error: aborting due to 20 previous errors - diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 0ff0f51ce8c2..8f09c8ce9d52 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -6,6 +6,7 @@ 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 @@ -14,6 +15,7 @@ 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 @@ -22,6 +24,7 @@ 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 @@ -30,6 +33,7 @@ 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 @@ -38,6 +42,7 @@ 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 @@ -46,6 +51,7 @@ 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 @@ -54,6 +60,7 @@ 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 @@ -62,6 +69,7 @@ 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 @@ -70,7 +78,7 @@ LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 diff --git a/tests/ui/author/blocks.rs b/tests/ui/author/blocks.rs index 6034762d130a..a8068436b70b 100644 --- a/tests/ui/author/blocks.rs +++ b/tests/ui/author/blocks.rs @@ -1,5 +1,5 @@ #![feature(stmt_expr_attributes)] -#![allow(redundant_semicolon, clippy::no_effect)] +#![allow(redundant_semicolons, clippy::no_effect)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index c7127db904a3..c8ef75e48fc2 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,10 +1,7 @@ if_chain! { if let ExprKind::Block(ref block) = expr.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::Tup(ref elements) = e.kind; - if elements.len() == 0; + if block.stmts.len() == 0; then { // report your lint here } diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index eafc68bd6bcf..0bbb9534928e 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -46,3 +46,13 @@ macro_rules! take_external { 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) + }; +} diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs new file mode 100644 index 000000000000..414477aedd78 --- /dev/null +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -0,0 +1,21 @@ +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, + } +} diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed new file mode 100644 index 000000000000..955801e40f9b --- /dev/null +++ b/tests/ui/block_in_if_condition.fixed @@ -0,0 +1,75 @@ +// 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() {} diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs index 50f238814a31..a6ea01d5fc5f 100644 --- a/tests/ui/block_in_if_condition.rs +++ b/tests/ui/block_in_if_condition.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] #![allow(unused, clippy::let_and_return)] @@ -41,37 +42,6 @@ fn condition_has_block_with_single_expression() -> i32 { } } -fn predicate 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 condition_is_normal() -> i32 { let x = 3; if true && x == 3 { @@ -81,10 +51,6 @@ fn condition_is_normal() -> i32 { } } -fn closure_without_block() { - if predicate(|x| x == 3, 6) {} -} - fn condition_is_unsafe_block() { let a: i32 = 1; @@ -94,16 +60,6 @@ fn condition_is_unsafe_block() { } } -fn main() {} - -fn macro_in_closure() { - let option = Some(true); - - if option.unwrap_or_else(|| unimplemented!()) { - unimplemented!() - } -} - fn block_in_assert() { let opt = Some(42); assert!(opt @@ -115,3 +71,5 @@ fn block_in_assert() { }) .is_some()); } + +fn main() {} diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr index d75f3c02f197..b0a0a276c890 100644 --- a/tests/ui/block_in_if_condition.stderr +++ b/tests/ui/block_in_if_condition.stderr @@ -1,62 +1,36 @@ 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:26:8 + --> $DIR/block_in_if_condition.rs:27:5 | -LL | if { - | ________^ +LL | / if { LL | | let x = 3; LL | | x == 3 LL | | } { | |_____^ | = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` - = help: try - let res = { - let x = 3; - x == 3 - }; - if res { - 6 - } ... +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:37:8 + --> $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` - = help: try - if true { - 6 - } ... - -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:58:17 - | -LL | |x| { - | _________________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_____________^ - -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:67:13 - | -LL | |x| { - | _____________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:77:8 + --> $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 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs new file mode 100644 index 000000000000..bac3eda5e7f3 --- /dev/null +++ b/tests/ui/block_in_if_condition_closure.rs @@ -0,0 +1,48 @@ +#![warn(clippy::block_in_if_condition_expr)] +#![warn(clippy::block_in_if_condition_stmt)] +#![allow(unused, clippy::let_and_return)] + +fn predicate 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() {} diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr new file mode 100644 index 000000000000..86cd24fe7632 --- /dev/null +++ b/tests/ui/block_in_if_condition_closure.stderr @@ -0,0 +1,24 @@ +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 + diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 0bd73ec2c104..ccbbf950e830 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -111,3 +111,10 @@ fn issue3703() { if Foo < false {} if false < Foo {} } + +fn issue4983() { + let a = true; + let b = false; + + //if a == !b {}; +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 74f504edfd06..c13575eae717 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -111,3 +111,13 @@ fn issue3703() { if Foo < false {} if false < Foo {} } + +fn issue4983() { + let a = true; + let b = false; + + if a == !b {}; + if !a == b {}; + if a == b {}; + if !a == !b {}; +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index 2aa070a00f30..cec7b196a23f 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -84,5 +84,21 @@ error: order comparisons between booleans can be simplified LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` -error: aborting due to 14 previous errors +error: Here comes + --> $DIR/bool_comparison.rs:119:8 + | +LL | if a == !b {}; + | ^^^^^^^ + | + = help: the suggestion + +error: Here comes + --> $DIR/bool_comparison.rs:120:8 + | +LL | if !a == b {}; + | ^^^^^^^ + | + = help: the suggestion + +error: aborting due to 16 previous errors diff --git a/tests/ui/bool_comparison.stdout b/tests/ui/bool_comparison.stdout new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index fbeee1b572a6..c986c992a07a 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -51,15 +51,4 @@ fn test_complex_conditions() { } } -fn test_nested() { - fn nested() { - let x = Some(()); - if x.is_some() { - x.unwrap(); // unnecessary - } else { - x.unwrap(); // will panic - } - } -} - fn main() {} diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index 4e53bd769e27..dc666bab4603 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -188,22 +188,5 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.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:58:13 - | -LL | if x.is_some() { - | ----------- the check is happening here -LL | x.unwrap(); // unnecessary - | ^^^^^^^^^^ - -error: This call to `unwrap()` will always panic. - --> $DIR/complex_conditionals.rs:60:13 - | -LL | if x.is_some() { - | ----------- because of this check -... -LL | x.unwrap(); // will panic - | ^^^^^^^^^^ - -error: aborting due to 22 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs new file mode 100644 index 000000000000..2307996a48ff --- /dev/null +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -0,0 +1,15 @@ +#![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() {} diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr new file mode 100644 index 000000000000..e4d085470c3b --- /dev/null +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -0,0 +1,31 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $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 + diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index b226bd726403..e40542e2e4f9 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -54,6 +54,8 @@ 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 diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index a31f748670ce..c4149ad19c1e 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -10,57 +10,57 @@ fn main() { if x == "hello" { print!("Hello "); } else if y == "world" { - println!("world!") -} + println!("world!") + } if x == "hello" { print!("Hello "); } else if let Some(42) = Some(42) { - println!("world!") -} + println!("world!") + } if x == "hello" { print!("Hello "); } else if y == "world" { - println!("world") -} -else { - println!("!") -} + println!("world") + } + else { + println!("!") + } if x == "hello" { print!("Hello "); } else if let Some(42) = Some(42) { - println!("world") -} -else { - println!("!") -} + println!("world") + } + else { + println!("!") + } if let Some(42) = Some(42) { print!("Hello "); } else if let Some(42) = Some(42) { - println!("world") -} -else { - println!("!") -} + println!("world") + } + else { + println!("!") + } if let Some(42) = Some(42) { print!("Hello "); } else if x == "hello" { - println!("world") -} -else { - println!("!") -} + println!("world") + } + else { + println!("!") + } if let Some(42) = Some(42) { print!("Hello "); } else if let Some(42) = Some(42) { - println!("world") -} -else { - println!("!") -} + println!("world") + } + else { + println!("!") + } } diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 26e3635c2d1c..28048999e8ec 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -13,8 +13,8 @@ LL | | } help: try | LL | } else if y == "world" { -LL | println!("world!") -LL | } +LL | println!("world!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -31,8 +31,8 @@ LL | | } help: try | LL | } else if let Some(42) = Some(42) { -LL | println!("world!") -LL | } +LL | println!("world!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -51,11 +51,11 @@ LL | | } help: try | LL | } else if y == "world" { -LL | println!("world") -LL | } -LL | else { -LL | println!("!") -LL | } +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -74,11 +74,11 @@ LL | | } help: try | LL | } else if let Some(42) = Some(42) { -LL | println!("world") -LL | } -LL | else { -LL | println!("!") -LL | } +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -97,11 +97,11 @@ LL | | } help: try | LL | } else if let Some(42) = Some(42) { -LL | println!("world") -LL | } -LL | else { -LL | println!("!") -LL | } +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -120,11 +120,11 @@ LL | | } help: try | LL | } else if x == "hello" { -LL | println!("world") -LL | } -LL | else { -LL | println!("!") -LL | } +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } | error: this `else { if .. }` block can be collapsed @@ -143,11 +143,11 @@ LL | | } help: try | LL | } else if let Some(42) = Some(42) { -LL | println!("world") -LL | } -LL | else { -LL | println!("!") -LL | } +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } | error: aborting due to 7 previous errors diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index e8f90b3164a9..076771f5c57e 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -7,28 +7,28 @@ fn main() { let x = "hello"; let y = "world"; if x == "hello" && y == "world" { - println!("Hello world!"); -} + println!("Hello world!"); + } if (x == "hello" || x == "world") && (y == "world" || y == "hello") { - println!("Hello world!"); -} + println!("Hello world!"); + } if x == "hello" && x == "world" && (y == "world" || y == "hello") { - println!("Hello world!"); -} + println!("Hello world!"); + } if (x == "hello" || x == "world") && y == "world" && y == "hello" { - println!("Hello world!"); -} + println!("Hello world!"); + } if x == "hello" && x == "world" && y == "world" && y == "hello" { - println!("Hello world!"); -} + println!("Hello world!"); + } if 42 == 1337 && 'a' != 'A' { - println!("world!") -} + println!("world!") + } // Works because any if with an else statement cannot be collapsed. if x == "hello" { @@ -81,8 +81,8 @@ fn main() { } if x == "hello" && y == "world" { // Collapsible - println!("Hello world!"); -} + println!("Hello world!"); + } if x == "hello" { print!("Hello "); diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index b123bc1c7bd7..6440ff41be81 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -12,8 +12,8 @@ LL | | } help: try | LL | if x == "hello" && y == "world" { -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: this `if` statement can be collapsed @@ -29,8 +29,8 @@ LL | | } help: try | LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: this `if` statement can be collapsed @@ -46,8 +46,8 @@ LL | | } help: try | LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: this `if` statement can be collapsed @@ -63,8 +63,8 @@ LL | | } help: try | LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: this `if` statement can be collapsed @@ -80,8 +80,8 @@ LL | | } help: try | LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: this `if` statement can be collapsed @@ -97,8 +97,8 @@ LL | | } help: try | LL | if 42 == 1337 && 'a' != 'A' { -LL | println!("world!") -LL | } +LL | println!("world!") +LL | } | error: this `if` statement can be collapsed @@ -114,8 +114,8 @@ LL | | } help: try | LL | if x == "hello" && y == "world" { // Collapsible -LL | println!("Hello world!"); -LL | } +LL | println!("Hello world!"); +LL | } | error: aborting due to 7 previous errors diff --git a/tests/ui/crashes/ice-2636.stderr b/tests/ui/crashes/ice-2636.stderr index aba8be4adcc1..53799b4fbf1d 100644 --- a/tests/ui/crashes/ice-2636.stderr +++ b/tests/ui/crashes/ice-2636.stderr @@ -11,6 +11,7 @@ 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 diff --git a/tests/ui/crashes/ice-5207.rs b/tests/ui/crashes/ice-5207.rs new file mode 100644 index 000000000000..1b20c9defac8 --- /dev/null +++ b/tests/ui/crashes/ice-5207.rs @@ -0,0 +1,7 @@ +// edition:2018 + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207 + +pub async fn bar<'a, T: 'a>(_: T) {} + +fn main() {} diff --git a/tests/ui/crashes/ice-5223.rs b/tests/ui/crashes/ice-5223.rs new file mode 100644 index 000000000000..9bb2e227fc12 --- /dev/null +++ b/tests/ui/crashes/ice-5223.rs @@ -0,0 +1,18 @@ +// Regression test for #5233 + +#![feature(const_generics)] +#![allow(incomplete_features)] +#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)] + +pub struct KotomineArray { + arr: [T; N], +} + +impl KotomineArray { + pub fn ice(self) { + let _ = self.arr[..]; + let _ = self.arr.iter().cloned().collect::>(); + } +} + +fn main() {} diff --git a/tests/ui/crashes/ice-5238.rs b/tests/ui/crashes/ice-5238.rs new file mode 100644 index 000000000000..989eb6d44855 --- /dev/null +++ b/tests/ui/crashes/ice-5238.rs @@ -0,0 +1,9 @@ +// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562 + +#![feature(generators, generator_trait)] + +fn main() { + let _ = || { + yield; + }; +} diff --git a/tests/ui/crashes/shadow.rs b/tests/ui/crashes/shadow.rs new file mode 100644 index 000000000000..843e8ef64dcd --- /dev/null +++ b/tests/ui/crashes/shadow.rs @@ -0,0 +1,6 @@ +fn main() { + let x: [i32; { + let u = 2; + 4 + }] = [2; { 4 }]; +} diff --git a/tests/ui/crashes/trivial_bounds.rs b/tests/ui/crashes/trivial_bounds.rs new file mode 100644 index 000000000000..2bb95c18a391 --- /dev/null +++ b/tests/ui/crashes/trivial_bounds.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(trivial_bounds)] +#![allow(unused, trivial_bounds)] + +fn test_trivial_bounds() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr index 36f243ebc5fb..a9a65a38c109 100644 --- a/tests/ui/custom_ice_message.stderr +++ b/tests/ui/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Testing the ICE message', clippy_lints/src/utils/internal_lints.rs +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 diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index ca7831618eda..6a9a57361f9f 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -32,6 +32,8 @@ 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 @@ -71,6 +73,8 @@ 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 diff --git a/tests/ui/default_lint.rs b/tests/ui/default_lint.rs index 988a7b866dea..0c6f29a43c6d 100644 --- a/tests/ui/default_lint.rs +++ b/tests/ui/default_lint.rs @@ -6,7 +6,6 @@ extern crate rustc; #[macro_use] extern crate rustc_session; extern crate rustc_lint; -use rustc_lint::{LintArray, LintPass}; declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui/default_lint.stderr b/tests/ui/default_lint.stderr index a37759db24b2..5c5836a7d297 100644 --- a/tests/ui/default_lint.stderr +++ b/tests/ui/default_lint.stderr @@ -1,5 +1,5 @@ error: the lint `TEST_LINT_DEFAULT` has the default lint description - --> $DIR/default_lint.rs:18:1 + --> $DIR/default_lint.rs:17:1 | LL | / declare_tool_lint! { LL | | pub clippy::TEST_LINT_DEFAULT, @@ -15,7 +15,7 @@ note: the lint level is defined here LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr index 10c38bb42e39..2287a548fe46 100644 --- a/tests/ui/derive_hash_xor_eq.stderr +++ b/tests/ui/derive_hash_xor_eq.stderr @@ -14,6 +14,7 @@ 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 @@ -30,6 +31,7 @@ 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 @@ -44,6 +46,7 @@ note: `PartialEq` implemented here | 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 @@ -58,6 +61,7 @@ note: `PartialEq` implemented here | 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 diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 776a65275e9b..445fc8d31d77 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,3 +1,4 @@ +// edition:2018 #![warn(clippy::missing_errors_doc)] use std::io; @@ -6,22 +7,42 @@ 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 { @@ -30,16 +51,32 @@ impl Struct1 { 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 { diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index f1d321cf909c..f44d6693d303 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:5:1 + --> $DIR/doc_errors.rs:6:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -11,13 +11,29 @@ LL | | } 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:29:5 + --> $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!(); @@ -25,10 +41,18 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:47:5 + --> $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 4 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index 9c8666419ff0..c784d41ba172 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -40,6 +40,8 @@ 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 diff --git a/tests/ui/enum_glob_use.fixed b/tests/ui/enum_glob_use.fixed new file mode 100644 index 000000000000..a98216758bb9 --- /dev/null +++ b/tests/ui/enum_glob_use.fixed @@ -0,0 +1,30 @@ +// 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; +} diff --git a/tests/ui/enum_glob_use.rs b/tests/ui/enum_glob_use.rs index e7b2526ca50b..5d929c9731d3 100644 --- a/tests/ui/enum_glob_use.rs +++ b/tests/ui/enum_glob_use.rs @@ -1,29 +1,30 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(unused_imports, dead_code, clippy::missing_docs_in_private_items)] +// run-rustfix + +#![warn(clippy::enum_glob_use)] +#![allow(unused)] +#![warn(unused_imports)] use std::cmp::Ordering::*; enum Enum { - _Foo, + Foo, } use self::Enum::*; -fn blarg() { - use self::Enum::*; // ok, just for a function +mod in_fn_test { + fn blarg() { + use crate::Enum::*; + + let _ = Foo; + } } mod blurg { pub use std::cmp::Ordering::*; // ok, re-export } -mod tests { - use super::*; +fn main() { + let _ = Foo; + let _ = Less; } - -#[allow(non_snake_case)] -mod CamelCaseName {} - -use CamelCaseName::*; - -fn main() {} diff --git a/tests/ui/enum_glob_use.stderr b/tests/ui/enum_glob_use.stderr index a301703c2988..69531aed39bd 100644 --- a/tests/ui/enum_glob_use.stderr +++ b/tests/ui/enum_glob_use.stderr @@ -1,16 +1,22 @@ -error: don't use glob imports for enum variants - --> $DIR/enum_glob_use.rs:4:1 +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: don't use glob imports for enum variants - --> $DIR/enum_glob_use.rs:10:1 +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: aborting due to 2 previous errors +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 diff --git a/tests/ui/excessive_precision.fixed b/tests/ui/excessive_precision.fixed index 1646dff9064d..bf0325fec792 100644 --- a/tests/ui/excessive_precision.fixed +++ b/tests/ui/excessive_precision.fixed @@ -12,12 +12,12 @@ fn main() { 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; + 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; + 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; @@ -34,11 +34,11 @@ fn main() { let good64_inf = 0.123_456_789_012; let bad32: f32 = 1.123_456_8; - let bad32_suf: f32 = 1.123_456_8; - let bad32_inf = 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; + let bad64_suf: f64 = 0.123_456_789_012_345_66_f64; let bad64_inf = 0.123_456_789_012_345_66; // Vectors diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index 12f8a61b75c5..599773f2f70c 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -2,7 +2,7 @@ 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` + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32` | = note: `-D clippy::excessive-precision` implied by `-D warnings` @@ -28,7 +28,7 @@ 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` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 @@ -58,13 +58,13 @@ 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` + | ^^^^^^^^^^^^^^^^^ 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` + | ^^^^^^^^^^^^^^^^^ 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 @@ -76,7 +76,7 @@ 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` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 4142f7ede116..ab976b947b35 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -38,7 +38,7 @@ note: potential failure(s) | LL | panic!(); | ^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -65,7 +65,7 @@ LL | } else if s.parse::().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -87,7 +87,7 @@ LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 diff --git a/tests/ui/float_arithmetic.rs b/tests/ui/float_arithmetic.rs new file mode 100644 index 000000000000..5ad320c62095 --- /dev/null +++ b/tests/ui/float_arithmetic.rs @@ -0,0 +1,53 @@ +#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::trivially_copy_pass_by_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) +} diff --git a/tests/ui/float_arithmetic.stderr b/tests/ui/float_arithmetic.stderr new file mode 100644 index 000000000000..809392529fd9 --- /dev/null +++ b/tests/ui/float_arithmetic.stderr @@ -0,0 +1,106 @@ +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:16:5 + | +LL | f * 2.0; + | ^^^^^^^ + | + = note: `-D clippy::float-arithmetic` implied by `-D warnings` + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:18:5 + | +LL | 1.0 + f; + | ^^^^^^^ + +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; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:21:5 + | +LL | f - 2.0 * 4.2; + | ^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:22:5 + | +LL | -f; + | ^^ + +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 -= 1.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:27:5 + | +LL | f /= 2.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:33:5 + | +LL | 3.1_f32 + &1.2_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:34:5 + | +LL | &3.4_f32 + 1.5_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:35:5 + | +LL | &3.5_f32 + &1.3_f32; + | ^^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:40:5 + | +LL | a + f + | ^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:44:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:48:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:52:5 + | +LL | (&f1 + &f2) + | ^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui/floating_point_abs.fixed b/tests/ui/floating_point_abs.fixed new file mode 100644 index 000000000000..b623e4988e7d --- /dev/null +++ b/tests/ui/floating_point_abs.fixed @@ -0,0 +1,98 @@ +// 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 }); +} diff --git a/tests/ui/floating_point_abs.rs b/tests/ui/floating_point_abs.rs new file mode 100644 index 000000000000..cbf9c94e41e6 --- /dev/null +++ b/tests/ui/floating_point_abs.rs @@ -0,0 +1,126 @@ +// 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 }); +} diff --git a/tests/ui/floating_point_abs.stderr b/tests/ui/floating_point_abs.stderr new file mode 100644 index 000000000000..74a71f2ca7c5 --- /dev/null +++ b/tests/ui/floating_point_abs.stderr @@ -0,0 +1,80 @@ +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 + diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed new file mode 100644 index 000000000000..ae7805fdf018 --- /dev/null +++ b/tests/ui/floating_point_exp.fixed @@ -0,0 +1,18 @@ +// 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; +} diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs new file mode 100644 index 000000000000..27e0b9bcbc93 --- /dev/null +++ b/tests/ui/floating_point_exp.rs @@ -0,0 +1,18 @@ +// 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; +} diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr new file mode 100644 index 000000000000..5cd999ad47cd --- /dev/null +++ b/tests/ui/floating_point_exp.stderr @@ -0,0 +1,28 @@ +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 + diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed new file mode 100644 index 000000000000..42c5e5d2bae2 --- /dev/null +++ b/tests/ui/floating_point_log.fixed @@ -0,0 +1,58 @@ +// 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() {} diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs new file mode 100644 index 000000000000..8be0d9ad56fc --- /dev/null +++ b/tests/ui/floating_point_log.rs @@ -0,0 +1,58 @@ +// 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() {} diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr new file mode 100644 index 000000000000..943fbdb0b832 --- /dev/null +++ b/tests/ui/floating_point_log.stderr @@ -0,0 +1,174 @@ +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 + diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed new file mode 100644 index 000000000000..e343c37740da --- /dev/null +++ b/tests/ui/floating_point_mul_add.fixed @@ -0,0 +1,21 @@ +// 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); +} diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs new file mode 100644 index 000000000000..810f929c8568 --- /dev/null +++ b/tests/ui/floating_point_mul_add.rs @@ -0,0 +1,21 @@ +// 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; +} diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr new file mode 100644 index 000000000000..2dfbf562d15f --- /dev/null +++ b/tests/ui/floating_point_mul_add.stderr @@ -0,0 +1,58 @@ +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 + diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed new file mode 100644 index 000000000000..78a9d44829bb --- /dev/null +++ b/tests/ui/floating_point_powf.fixed @@ -0,0 +1,42 @@ +// 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); +} diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs new file mode 100644 index 000000000000..dbc1cac5cb43 --- /dev/null +++ b/tests/ui/floating_point_powf.rs @@ -0,0 +1,42 @@ +// 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); +} diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr new file mode 100644 index 000000000000..ad5163f0079b --- /dev/null +++ b/tests/ui/floating_point_powf.stderr @@ -0,0 +1,150 @@ +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 + diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 6e100230a3ad..306514769990 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal)] +#![allow(clippy::print_literal, clippy::redundant_clone)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index 1fae6603ac09..b604d79cca37 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal)] +#![allow(clippy::print_literal, clippy::redundant_clone)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index b73d9716adc7..252e9eb5dd8c 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -105,6 +105,7 @@ LL | impl Foo for HashMap { 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 Foo for HashMap { @@ -123,6 +124,7 @@ LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) 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(_map: &mut HashMap, _set: &mut HashSet) {} @@ -137,6 +139,7 @@ LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) 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(_map: &mut HashMap, _set: &mut HashSet) {} diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index b4fac2a0fe05..ac5f0d0a39e8 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,22 +1,22 @@ -error: index out of bounds: the len is 4 but the index is 4 +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(const_err)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: 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: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: index out of bounds: the len is 4 but the index is 15 +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 diff --git a/tests/ui/inefficient_to_string.stderr b/tests/ui/inefficient_to_string.stderr index 1c0490ffa44c..4be46161e8b7 100644 --- a/tests/ui/inefficient_to_string.stderr +++ b/tests/ui/inefficient_to_string.stderr @@ -35,21 +35,21 @@ LL | let _: String = 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>` +error: calling `to_string` on `&&std::borrow::Cow` --> $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` + = help: `&std::borrow::Cow` implements `ToString` through a slower blanket impl, but `std::borrow::Cow` has a fast specialization of `ToString` -error: calling `to_string` on `&&&std::borrow::Cow<'_, str>` +error: calling `to_string` on `&&&std::borrow::Cow` --> $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` + = help: `&&std::borrow::Cow` implements `ToString` through a slower blanket impl, but `std::borrow::Cow` has a fast specialization of `ToString` error: aborting due to 6 previous errors diff --git a/tests/ui/arithmetic.rs b/tests/ui/integer_arithmetic.rs similarity index 75% rename from tests/ui/arithmetic.rs rename to tests/ui/integer_arithmetic.rs index dc11cbd98deb..31a07e7c35b0 100644 --- a/tests/ui/arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -4,7 +4,9 @@ clippy::shadow_reuse, clippy::shadow_unrelated, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::op_ref, + clippy::trivially_copy_pass_by_ref )] #[rustfmt::skip] @@ -40,25 +42,6 @@ fn main() { i &= 1; i ^= i; - 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; - - // no error, overflows are checked by `overflowing_literals` - -1.; - -(-1.); - // No errors for the following items because they are constant expressions enum Foo { Bar = -2, @@ -90,4 +73,30 @@ fn main() { 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) } diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr new file mode 100644 index 000000000000..0b8d0b767bf8 --- /dev/null +++ b/tests/ui/integer_arithmetic.stderr @@ -0,0 +1,107 @@ +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:15:5 + | +LL | 1 + i; + | ^^^^^ + | + = note: `-D clippy::integer-arithmetic` implied by `-D warnings` + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:16:5 + | +LL | i * 2; + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:17: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:19:5 + | +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:20:5 + | +LL | -i; + | ^^ + +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 -= 1; + | ^^^^^^ + +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 %= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:82:5 + | +LL | 3 + &1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:83:5 + | +LL | &3 + 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:84:5 + | +LL | &3 + &1; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:89:5 + | +LL | a + x + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:93:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:97:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:101:5 + | +LL | (&x + &y) + | ^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index 1538abb1d776..8a9d5a3d1d56 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,4 +1,4 @@ -// compile-flags: --edition 2018 +// edition:2018 #![allow(dead_code)] async fn sink1<'a>(_: &'a str) {} // lint diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 2ebe85bd6b0e..0426508e622f 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -2,17 +2,15 @@ error: explicit lifetimes given in parameter types where they could be elided (o --> $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 { -LL | | s -LL | | } - | |_^ +LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/lint_without_lint_pass.rs b/tests/ui/lint_without_lint_pass.rs index 5b0b4d12d134..8eb1f7b64610 100644 --- a/tests/ui/lint_without_lint_pass.rs +++ b/tests/ui/lint_without_lint_pass.rs @@ -6,7 +6,7 @@ extern crate rustc; #[macro_use] extern crate rustc_session; extern crate rustc_lint; -use rustc_lint::{LintArray, LintPass}; +use rustc_lint::LintPass; declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui/lint_without_lint_pass.stderr b/tests/ui/lint_without_lint_pass.stderr index ee7593d1b95f..1257dae96d71 100644 --- a/tests/ui/lint_without_lint_pass.stderr +++ b/tests/ui/lint_without_lint_pass.stderr @@ -15,7 +15,7 @@ note: the lint level is defined here LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 20bd872c638c..c299b16c8ce8 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -28,6 +28,7 @@ fn main() { 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; diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 9a6388394cd0..0b3af2d8bc35 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -50,7 +50,7 @@ LL | let fail8 = 0o123; | ^^^^^ error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:32:18 + --> $DIR/literals.rs:33:18 | LL | let fail19 = 12_3456_21; | ^^^^^^^^^^ help: consider: `12_345_621` @@ -58,13 +58,13 @@ LL | let fail19 = 12_3456_21; = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:33:18 + --> $DIR/literals.rs:34:18 | LL | let fail22 = 3__4___23; | ^^^^^^^^^ help: consider: `3_423` error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:34:18 + --> $DIR/literals.rs:35:18 | LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` diff --git a/tests/ui/lossy_float_literal.fixed b/tests/ui/lossy_float_literal.fixed new file mode 100644 index 000000000000..24e372354fc0 --- /dev/null +++ b/tests/ui/lossy_float_literal.fixed @@ -0,0 +1,35 @@ +// 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; +} diff --git a/tests/ui/lossy_float_literal.rs b/tests/ui/lossy_float_literal.rs new file mode 100644 index 000000000000..3dcf98fa0bdd --- /dev/null +++ b/tests/ui/lossy_float_literal.rs @@ -0,0 +1,35 @@ +// 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; +} diff --git a/tests/ui/lossy_float_literal.stderr b/tests/ui/lossy_float_literal.stderr new file mode 100644 index 000000000000..d2193c0c8195 --- /dev/null +++ b/tests/ui/lossy_float_literal.stderr @@ -0,0 +1,70 @@ +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 + diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs new file mode 100644 index 000000000000..60c64ee8146e --- /dev/null +++ b/tests/ui/macro_use_imports.rs @@ -0,0 +1,11 @@ +// edition:2018 +#![warn(clippy::macro_use_imports)] + +use std::collections::HashMap; +#[macro_use] +use std::prelude; + +fn main() { + let _ = HashMap::::new(); + println!(); +} diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr new file mode 100644 index 000000000000..b5e3dbec5727 --- /dev/null +++ b/tests/ui/macro_use_imports.stderr @@ -0,0 +1,10 @@ +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::` + | + = note: `-D clippy::macro-use-imports` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/match_bool.stderr b/tests/ui/match_bool.stderr index 42f20862939b..d0c20eb2696b 100644 --- a/tests/ui/match_bool.stderr +++ b/tests/ui/match_bool.stderr @@ -40,8 +40,8 @@ LL | | }; help: consider using an `if`/`else` expression | LL | if !test { -LL | println!("Noooo!"); -LL | }; +LL | println!("Noooo!"); +LL | }; | error: you seem to be trying to match on a boolean expression @@ -58,8 +58,8 @@ LL | | }; help: consider using an `if`/`else` expression | LL | if !test { -LL | println!("Noooo!"); -LL | }; +LL | println!("Noooo!"); +LL | }; | error: you seem to be trying to match on a boolean expression @@ -76,8 +76,8 @@ LL | | }; help: consider using an `if`/`else` expression | LL | if !(test && test) { -LL | println!("Noooo!"); -LL | }; +LL | println!("Noooo!"); +LL | }; | error: equal expressions as operands to `&&` @@ -103,10 +103,10 @@ LL | | }; help: consider using an `if`/`else` expression | LL | if test { -LL | println!("Yes!"); -LL | } else { -LL | println!("Noooo!"); -LL | }; +LL | println!("Yes!"); +LL | } else { +LL | println!("Noooo!"); +LL | }; | error: aborting due to 8 previous errors diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 28abb2fc9bf2..26c65f32ad78 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -121,7 +121,7 @@ help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -139,7 +139,7 @@ help: consider refactoring into `Ok(3) | Ok(_)` | LL | Ok(3) => println!("ok"), | ^^^^^ - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 8fb8dc323e43..90f00aa78712 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,25 +1,29 @@ // run-rustfix #![warn(clippy::match_single_binding)] -#![allow(clippy::many_single_char_names, clippy::toplevel_ref_arg)] +#![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 } +} + fn main() { let a = 1; let b = 2; let c = 3; // Lint let (x, y, z) = (a, b, c); -{ - println!("{} {} {}", x, y, z); -} + { + println!("{} {} {}", x, y, z); + } // Lint let (x, y, z) = (a, b, c); -println!("{} {} {}", x, y, z); + println!("{} {} {}", x, y, z); // Ok match a { 2 => println!("2"), @@ -35,29 +39,42 @@ println!("{} {} {}", x, y, z); println!("whatever"); // Lint { - let x = 29; - println!("x has a value of {}", x); -} + let x = 29; + println!("x has a value of {}", x); + } // Lint { - let e = 5 * a; - if e >= 5 { - println!("e is superior to 5"); + 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); + println!("Coords: ({}, {})", x, y); // Lint let Point { x: x1, y: y1 } = p; -println!("Coords: ({}, {})", x1, y1); + println!("Coords: ({}, {})", x1, y1); // Lint let x = 5; let ref r = x; -println!("Got a reference to {}", r); + println!("Got a reference to {}", r); // Lint let mut x = 5; let ref mut mr = x; -println!("Got a mutable reference to {}", mr); + 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::>(); } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 55b0b09a0088..4a4b290036c7 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,13 +1,17 @@ // run-rustfix #![warn(clippy::match_single_binding)] -#![allow(clippy::many_single_char_names, clippy::toplevel_ref_arg)] +#![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 } +} + fn main() { let a = 1; let b = 2; @@ -72,4 +76,17 @@ fn main() { 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::>(); } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index d76e229adff0..cf741a989c2d 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:16:5 + --> $DIR/match_single_binding.rs:20:5 | LL | / match (a, b, c) { LL | | (x, y, z) => { @@ -12,13 +12,13 @@ LL | | } help: consider using `let` statement | LL | let (x, y, z) = (a, b, c); -LL | { -LL | println!("{} {} {}", x, y, z); -LL | } +LL | { +LL | println!("{} {} {}", x, y, z); +LL | } | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:22:5 + --> $DIR/match_single_binding.rs:26:5 | LL | / match (a, b, c) { LL | | (x, y, z) => println!("{} {} {}", x, y, z), @@ -28,11 +28,11 @@ LL | | } help: consider using `let` statement | LL | let (x, y, z) = (a, b, c); -LL | println!("{} {} {}", x, y, z); +LL | println!("{} {} {}", x, y, z); | error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:37:5 + --> $DIR/match_single_binding.rs:41:5 | LL | / match a { LL | | _ => println!("whatever"), @@ -40,7 +40,7 @@ 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:41:5 + --> $DIR/match_single_binding.rs:45:5 | LL | / match a { LL | | _ => { @@ -53,13 +53,13 @@ LL | | } help: consider using the match body instead | LL | { -LL | let x = 29; -LL | println!("x has a value of {}", x); -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:48:5 + --> $DIR/match_single_binding.rs:52:5 | LL | / match a { LL | | _ => { @@ -73,15 +73,15 @@ 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 | let e = 5 * a; +LL | if e >= 5 { +LL | println!("e is superior to 5"); +LL | } LL | } -LL | } | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:58:5 + --> $DIR/match_single_binding.rs:62:5 | LL | / match p { LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), @@ -91,11 +91,11 @@ LL | | } help: consider using `let` statement | LL | let Point { x, y } = p; -LL | println!("Coords: ({}, {})", x, y); +LL | println!("Coords: ({}, {})", x, y); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:62:5 + --> $DIR/match_single_binding.rs:66:5 | LL | / match p { LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), @@ -105,11 +105,11 @@ LL | | } help: consider using `let` statement | LL | let Point { x: x1, y: y1 } = p; -LL | println!("Coords: ({}, {})", x1, y1); +LL | println!("Coords: ({}, {})", x1, y1); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:67:5 + --> $DIR/match_single_binding.rs:71:5 | LL | / match x { LL | | ref r => println!("Got a reference to {}", r), @@ -119,11 +119,11 @@ LL | | } help: consider using `let` statement | LL | let ref r = x; -LL | println!("Got a reference to {}", r); +LL | println!("Got a reference to {}", r); | error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:72:5 + --> $DIR/match_single_binding.rs:76:5 | LL | / match x { LL | | ref mut mr => println!("Got a mutable reference to {}", mr), @@ -133,8 +133,39 @@ LL | | } help: consider using `let` statement | LL | let ref mut mr = x; -LL | println!("Got a mutable reference to {}", mr); +LL | println!("Got a mutable reference to {}", mr); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:80: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:88: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 9 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/mem_discriminant.stderr b/tests/ui/mem_discriminant.stderr index 1dd6610d5a30..8d9810970ade 100644 --- a/tests/ui/mem_discriminant.stderr +++ b/tests/ui/mem_discriminant.stderr @@ -71,6 +71,8 @@ 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` --> $DIR/mem_discriminant.rs:34:5 diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 58657b934fbf..54e962e7116e 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,12 +1,3 @@ -// Copyright 2014-2019 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - // run-rustfix #![allow(unused_imports)] #![warn( diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index eac0ce586a08..60f527810716 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,12 +1,3 @@ -// Copyright 2014-2019 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - // run-rustfix #![allow(unused_imports)] #![warn( diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index d5dc66873b2f..245d33aa4f26 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:23:13 + --> $DIR/mem_replace.rs:14:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:25:13 + --> $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:30:13 + --> $DIR/mem_replace.rs:21:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,13 +21,13 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = 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:32:13 + --> $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:33:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` diff --git a/tests/ui/mem_replace_macro.stderr b/tests/ui/mem_replace_macro.stderr index a0872b1a6bf4..4971a91050bf 100644 --- a/tests/ui/mem_replace_macro.stderr +++ b/tests/ui/mem_replace_macro.stderr @@ -8,6 +8,7 @@ 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 diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index bc4484358297..2af33b263405 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -1,5 +1,5 @@ // aux-build:option_helpers.rs -// compile-flags: --edition 2018 +// edition:2018 #![warn(clippy::all, clippy::pedantic)] #![allow( diff --git a/tests/ui/missing-doc-impl.rs b/tests/ui/missing-doc-impl.rs index 1317521f7366..57af84dcdf4d 100644 --- a/tests/ui/missing-doc-impl.rs +++ b/tests/ui/missing-doc-impl.rs @@ -46,7 +46,7 @@ pub trait D { } /// dox -pub trait E { +pub trait E: Sized { type AssociatedType; type AssociatedTypeDef = Self; diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index f367279906fd..ba352ef9ee93 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -3,7 +3,8 @@ //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. #![warn(clippy::missing_const_for_fn)] -#![feature(start)] +#![allow(incomplete_features)] +#![feature(start, const_generics)] struct Game; @@ -90,3 +91,13 @@ mod with_drop { } } } + +fn const_generic_params(t: &[T; N]) -> &[T; N] { + t +} + +fn const_generic_return(t: &[T]) -> &[T; N] { + let p = t.as_ptr() as *const [T; N]; + + unsafe { &*p } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 9a73dcbe99f1..c6f44b7daa34 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,6 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(clippy::let_and_return)] +#![allow(incomplete_features, clippy::let_and_return)] +#![feature(const_generics)] use std::mem::transmute; @@ -12,6 +13,10 @@ impl Game { 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 diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 165b8a030771..8dde56cd79f4 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:12:5 + --> $DIR/could_be_const.rs:13:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,15 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:18:1 + --> $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 @@ -17,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:28:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -26,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:29:1 + --> $DIR/could_be_const.rs:34:1 | LL | / fn string() -> String { LL | | String::new() @@ -34,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:39:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -42,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:44:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -50,7 +58,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:43:1 + --> $DIR/could_be_const.rs:48:1 | LL | / fn sub(x: u32) -> usize { LL | | unsafe { transmute(&x) } @@ -58,12 +66,12 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:62:9 + --> $DIR/could_be_const.rs:67:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B LL | | } | |_________^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/mul_add.rs b/tests/ui/mul_add.rs deleted file mode 100644 index 1322e002c641..000000000000 --- a/tests/ui/mul_add.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::manual_mul_add)] -#![allow(unused_variables)] - -fn mul_add_test() { - let a: f64 = 1234.567; - let b: f64 = 45.67834; - let c: f64 = 0.0004; - - // Examples of not auto-fixable expressions - let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c); - let test2 = 1234.567 * 45.67834 + 0.0004; -} - -fn main() { - mul_add_test(); -} diff --git a/tests/ui/mul_add.stderr b/tests/ui/mul_add.stderr deleted file mode 100644 index 3b21646f7c30..000000000000 --- a/tests/ui/mul_add.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add.rs:10:17 - | -LL | let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(a * b + c).mul_add((c + a * b), (c + (a * b) + c))` - | - = note: `-D clippy::manual-mul-add` implied by `-D warnings` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add.rs:10:17 - | -LL | let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c); - | ^^^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add.rs:10:31 - | -LL | let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c); - | ^^^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add.rs:10:46 - | -LL | let test1 = (a * b + c) * (c + a * b) + (c + (a * b) + c); - | ^^^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add.rs:11:17 - | -LL | let test2 = 1234.567 * 45.67834 + 0.0004; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1234.567.mul_add(45.67834, 0.0004)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/mul_add_fixable.fixed b/tests/ui/mul_add_fixable.fixed deleted file mode 100644 index 4af7c7e3e1a5..000000000000 --- a/tests/ui/mul_add_fixable.fixed +++ /dev/null @@ -1,24 +0,0 @@ -// run-rustfix - -#![warn(clippy::manual_mul_add)] -#![allow(unused_variables)] - -fn mul_add_test() { - let a: f64 = 1234.567; - let b: f64 = 45.67834; - let c: f64 = 0.0004; - - // Auto-fixable examples - let test1 = a.mul_add(b, c); - let test2 = a.mul_add(b, c); - - let test3 = a.mul_add(b, c); - let test4 = a.mul_add(b, c); - - let test5 = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; - let test6 = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); -} - -fn main() { - mul_add_test(); -} diff --git a/tests/ui/mul_add_fixable.rs b/tests/ui/mul_add_fixable.rs deleted file mode 100644 index 8b42f6f184a4..000000000000 --- a/tests/ui/mul_add_fixable.rs +++ /dev/null @@ -1,24 +0,0 @@ -// run-rustfix - -#![warn(clippy::manual_mul_add)] -#![allow(unused_variables)] - -fn mul_add_test() { - let a: f64 = 1234.567; - let b: f64 = 45.67834; - let c: f64 = 0.0004; - - // Auto-fixable examples - let test1 = a * b + c; - let test2 = c + a * b; - - let test3 = (a * b) + c; - let test4 = c + (a * b); - - let test5 = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; - let test6 = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; -} - -fn main() { - mul_add_test(); -} diff --git a/tests/ui/mul_add_fixable.stderr b/tests/ui/mul_add_fixable.stderr deleted file mode 100644 index 235443f4b02b..000000000000 --- a/tests/ui/mul_add_fixable.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:12:17 - | -LL | let test1 = a * b + c; - | ^^^^^^^^^ help: try: `a.mul_add(b, c)` - | - = note: `-D clippy::manual-mul-add` implied by `-D warnings` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:13:17 - | -LL | let test2 = c + a * b; - | ^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:15:17 - | -LL | let test3 = (a * b) + c; - | ^^^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:16:17 - | -LL | let test4 = c + (a * b); - | ^^^^^^^^^^^ help: try: `a.mul_add(b, c)` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:18:17 - | -LL | let test5 = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` - -error: consider using `mul_add()` for better numerical precision - --> $DIR/mul_add_fixable.rs:19:17 - | -LL | let test6 = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` - -error: aborting due to 6 previous errors - diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 6fa5dbfc29f3..44e814227141 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -20,6 +20,8 @@ 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 diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index b9215885877d..8d6a37df9601 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -1,5 +1,4 @@ -error: This `else` block is redundant. - +error: this `else` block is redundant --> $DIR/needless_continue.rs:28:16 | LL | } else { @@ -9,34 +8,33 @@ 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, like so: - 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 + = 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 { @@ -47,16 +45,16 @@ LL | | println!("Jabber"); LL | | } | |_________^ | - = help: Consider dropping the `else` clause, and moving out the code in the `else` block, like so: - if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { - continue; - } - println!("Blabber"); - println!("Jabber"); - ... - -error: This `else` block is redundant. + = 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 { @@ -65,22 +63,21 @@ 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, like so: - 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 + = 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() { @@ -90,12 +87,13 @@ LL | | println!("bar-5"); LL | | } | |_________________^ | - = help: Consider dropping the `else` clause, and moving out the code in the `else` block, like so: - if condition() { - continue; - } - println!("bar-5"); - ... + = help: consider dropping the `else` clause + if condition() { + continue; // should lint here + } + { + println!("bar-5"); + } error: aborting due to 4 previous errors diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 813d2606153b..682d7b3c4ceb 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -8,7 +8,23 @@ /// unimplemented!(); /// } /// ``` -fn bad_doctest() {} +/// +/// This should, too. +/// +/// ```rust +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This one too. +/// +/// ```no_run +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +fn bad_doctests() {} /// # Examples /// @@ -34,9 +50,25 @@ fn bad_doctest() {} /// 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_doctest(); + bad_doctests(); no_false_positives(); } diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 22d145aad585..65d40ee6832f 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -6,5 +6,17 @@ LL | /// fn main() { | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` -error: aborting due to previous error +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 diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ad55fc5f750d..d3a360ed8b57 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -2,7 +2,7 @@ error: explicit lifetimes given in parameter types where they could be elided (o --> $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` @@ -10,125 +10,97 @@ error: explicit lifetimes given in parameter types where they could be elided (o --> $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 { -LL | | x -LL | | } - | |_^ +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, ()> { -LL | | Ok(x) -LL | | } - | |_^ +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, ()> -LL | | where -LL | | T: Copy, -LL | | { -LL | | Ok(x) -LL | | } - | |_^ +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> -LL | | where -LL | | for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, -LL | | { -LL | | unreachable!() -LL | | } - | |_^ +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 { -LL | | &self.x -LL | | } - | |_____^ +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 { -LL | | unimplemented!() -LL | | } - | |_^ +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 { -LL | | unimplemented!() -LL | | } - | |_^ +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 { -LL | | unimplemented!() -LL | | } - | |_^ +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 { -LL | | unimplemented!() -LL | | } - | |_^ +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) { -LL | | unimplemented!() -LL | | } - | |_^ +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> { -LL | | unimplemented!() -LL | | } - | |_^ +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 diff --git a/tests/ui/option_env_unwrap.rs b/tests/ui/option_env_unwrap.rs new file mode 100644 index 000000000000..642c77460a34 --- /dev/null +++ b/tests/ui/option_env_unwrap.rs @@ -0,0 +1,23 @@ +// 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"); +} diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr new file mode 100644 index 000000000000..8de9c8a9d29e --- /dev/null +++ b/tests/ui/option_env_unwrap.stderr @@ -0,0 +1,61 @@ +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 + diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index ad153e4fc194..9a0da404cb6d 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -13,6 +13,10 @@ fn plus_one(value: usize) -> usize { value + 1 } +fn option() -> Option { + Some(10) +} + struct HasOption { field: Option, } @@ -73,6 +77,8 @@ fn option_map_unit_fn() { if let Some(value) = x.field { plus_one(value + captured); } - if let Some(ref value) = x.field { do_nothing(value + captured) }} + if let Some(ref value) = x.field { do_nothing(value + captured) } + + if let Some(a) = option() { do_nothing(a) }} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 6926498341ac..58041b62df35 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -13,6 +13,10 @@ fn plus_one(value: usize) -> usize { value + 1 } +fn option() -> Option { + Some(10) +} + struct HasOption { field: Option, } @@ -73,6 +77,8 @@ fn option_map_unit_fn() { x.field.map(|value| { { plus_one(value + captured); } }); - x.field.map(|ref value| { do_nothing(value + captured) });} + x.field.map(|ref value| { do_nothing(value + captured) }); + + option().map(do_nothing);} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index f993e1931d59..1312c70b6d59 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ -error: called `map(f)` on an `Option` value where `f` is a unit function - --> $DIR/option_map_unit_fn_fixable.rs:34:5 +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); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -8,133 +8,141 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a unit function - --> $DIR/option_map_unit_fn_fixable.rs:36:5 +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 unit function - --> $DIR/option_map_unit_fn_fixable.rs:38:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:44:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:46:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:49:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:51:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:53:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:55:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:58:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:60:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:62:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:64:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:69:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:71:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:73:5 +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 unit closure - --> $DIR/option_map_unit_fn_fixable.rs:76:5 +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) });} +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: aborting due to 17 previous errors +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 diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index dfe27e193b97..f22388154499 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -17,4 +17,20 @@ fn main() { [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] => (), + } } diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index bd202fc04205..5848ecd38d98 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -17,4 +17,20 @@ fn main() { [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] => (), + } } diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index f25e71e872b2..af067580688b 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -6,5 +6,17 @@ LL | y @ _ => (), | = note: `-D clippy::redundant-pattern` implied by `-D warnings` -error: aborting due to previous error +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 diff --git a/tests/ui/print.stderr b/tests/ui/print.stderr index 12c5a0bdaa02..208d95326285 100644 --- a/tests/ui/print.stderr +++ b/tests/ui/print.stderr @@ -6,12 +6,6 @@ LL | write!(f, "{:?}", 43.1415) | = note: `-D clippy::use-debug` implied by `-D warnings` -error: use of `Debug`-based formatting - --> $DIR/print.rs:18:19 - | -LL | write!(f, "{:?}", 42.718) - | ^^^^^^ - error: use of `println!` --> $DIR/print.rs:23:5 | @@ -56,5 +50,5 @@ error: use of `Debug`-based formatting LL | print!("Hello {:#?}", "#orld"); | ^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed new file mode 100644 index 000000000000..2c3e4989d534 --- /dev/null +++ b/tests/ui/question_mark.fixed @@ -0,0 +1,113 @@ +// run-rustfix +#![allow(unreachable_code)] + +fn some_func(a: Option) -> Option { + a?; + + a +} + +fn some_other_func(a: Option) -> Option { + if a.is_none() { + return None; + } else { + return Some(0); + } + unreachable!() +} + +pub enum SeemsOption { + Some(T), + None, +} + +impl SeemsOption { + pub fn is_none(&self) -> bool { + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } + } +} + +fn returns_something_similar_to_option(a: SeemsOption) -> SeemsOption { + if a.is_none() { + return SeemsOption::None; + } + + a +} + +pub struct CopyStruct { + pub opt: Option, +} + +impl CopyStruct { + #[rustfmt::skip] + pub fn func(&self) -> Option { + (self.opt)?; + + self.opt?; + + let _ = Some(self.opt?); + + let _ = self.opt?; + + self.opt + } +} + +#[derive(Clone)] +pub struct MoveStruct { + pub opt: Option>, +} + +impl MoveStruct { + pub fn ref_func(&self) -> Option> { + self.opt.as_ref()?; + + self.opt.clone() + } + + pub fn mov_func_reuse(self) -> Option> { + self.opt.as_ref()?; + + self.opt + } + + pub fn mov_func_no_use(self) -> Option> { + self.opt.as_ref()?; + Some(Vec::new()) + } + + pub fn if_let_ref_func(self) -> Option> { + let v: &Vec<_> = self.opt.as_ref()?; + + Some(v.clone()) + } + + pub fn if_let_mov_func(self) -> Option> { + let v = self.opt?; + + Some(v) + } +} + +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); +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 57237c52e8c9..24df76344356 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,3 +1,6 @@ +// run-rustfix +#![allow(unreachable_code)] + fn some_func(a: Option) -> Option { if a.is_none() { return None; @@ -58,6 +61,12 @@ impl CopyStruct { self.opt }; + let _ = if let Some(x) = self.opt { + x + } else { + return None; + }; + self.opt } } @@ -90,11 +99,32 @@ impl MoveStruct { } Some(Vec::new()) } + + pub fn if_let_ref_func(self) -> Option> { + 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> { + let v = if let Some(v) = self.opt { + v + } else { + return None; + }; + + Some(v) + } } fn main() { some_func(Some(42)); some_func(None); + some_other_func(Some(42)); let copy_struct = CopyStruct { opt: Some(54) }; copy_struct.func(); diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 522501d58c66..97741069b50a 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,31 +1,31 @@ error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:2:5 + --> $DIR/question_mark.rs:5:5 | LL | / if a.is_none() { LL | | return None; LL | | } - | |_____^ help: replace_it_with: `a?;` + | |_____^ 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:47:9 + --> $DIR/question_mark.rs:50:9 | LL | / if (self.opt).is_none() { LL | | return None; LL | | } - | |_________^ help: replace_it_with: `(self.opt)?;` + | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:51:9 + --> $DIR/question_mark.rs:54:9 | LL | / if self.opt.is_none() { LL | | return None LL | | } - | |_________^ help: replace_it_with: `self.opt?;` + | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:55:17 + --> $DIR/question_mark.rs:58:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -33,31 +33,64 @@ LL | | return None; LL | | } else { LL | | self.opt LL | | }; - | |_________^ help: replace_it_with: `Some(self.opt?)` + | |_________^ 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:72:9 + --> $DIR/question_mark.rs:81:9 | LL | / if self.opt.is_none() { LL | | return None; LL | | } - | |_________^ help: replace_it_with: `self.opt.as_ref()?;` + | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:80:9 + --> $DIR/question_mark.rs:89:9 | LL | / if self.opt.is_none() { LL | | return None; LL | | } - | |_________^ help: replace_it_with: `self.opt.as_ref()?;` + | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:88:9 + --> $DIR/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | return None; LL | | } - | |_________^ help: replace_it_with: `self.opt.as_ref()?;` + | |_________^ 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: aborting due to 7 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 84931f66fa8d..54815603c6de 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -50,6 +50,7 @@ fn main() { cannot_double_move(Alpha); cannot_move_from_type_with_drop(); borrower_propagation(); + not_consumed(); } #[derive(Clone)] @@ -136,3 +137,26 @@ fn borrower_propagation() { 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); + } +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 9ea2de9a3daa..a9b31183adc8 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -50,6 +50,7 @@ fn main() { cannot_double_move(Alpha); cannot_move_from_type_with_drop(); borrower_propagation(); + not_consumed(); } #[derive(Clone)] @@ -136,3 +137,26 @@ fn borrower_propagation() { 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); + } +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 0f185cda0197..9c27812b9cdc 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,52 +108,64 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:59:22 + --> $DIR/redundant_clone.rs:60:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:59:21 + --> $DIR/redundant_clone.rs:60:21 | LL | (a.clone(), a.clone()) | ^ error: redundant clone - --> $DIR/redundant_clone.rs:119:15 + --> $DIR/redundant_clone.rs:120:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:119:14 + --> $DIR/redundant_clone.rs:120:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:120:15 + --> $DIR/redundant_clone.rs:121:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:120:14 + --> $DIR/redundant_clone.rs:121:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:130:19 + --> $DIR/redundant_clone.rs:131:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:130:18 + --> $DIR/redundant_clone.rs:131:18 | LL | let _f = f.clone(); | ^ -error: aborting due to 13 previous errors +error: redundant clone + --> $DIR/redundant_clone.rs:143:14 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^ help: remove this + | +note: cloned value is neither consumed nor mutated + --> $DIR/redundant_clone.rs:143:13 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^^ + +error: aborting due to 14 previous errors diff --git a/tests/ui/rest_pat_in_fully_bound_structs.rs b/tests/ui/rest_pat_in_fully_bound_structs.rs new file mode 100644 index 000000000000..22e4d4edd783 --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -0,0 +1,30 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +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, .. } => {}, + _ => {}, + } +} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.stderr b/tests/ui/rest_pat_in_fully_bound_structs.stderr new file mode 100644 index 000000000000..effa46b4b0fa --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -0,0 +1,27 @@ +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:13: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:14: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:20:9 + | +LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `..` from this binding + +error: aborting due to 3 previous errors + diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 33be39e34f15..467e00263cd3 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a unit function +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); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a unit function +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); @@ -16,7 +16,7 @@ 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 unit function +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); @@ -24,7 +24,7 @@ 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 unit closure +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)); @@ -32,7 +32,7 @@ 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 unit closure +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); }); @@ -40,7 +40,7 @@ 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 unit closure +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)); @@ -48,7 +48,7 @@ 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 unit closure +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) }); @@ -56,7 +56,7 @@ 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 unit closure +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); }); @@ -64,7 +64,7 @@ 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 unit closure +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); } }); @@ -72,7 +72,7 @@ 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 unit closure +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)); @@ -80,7 +80,7 @@ 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 unit closure +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) }); @@ -88,7 +88,7 @@ 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 unit closure +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); }); @@ -96,7 +96,7 @@ 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 unit closure +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); } }); @@ -104,7 +104,7 @@ 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 unit closure +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); }); @@ -112,7 +112,7 @@ 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 unit closure +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); }); @@ -120,7 +120,7 @@ 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 unit closure +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); } }); @@ -128,7 +128,7 @@ 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 unit closure +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) }); diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index 7a882efc4d18..a7a8499b58f0 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -1,5 +1,5 @@ // run-rustfix -// compile-flags: --edition 2018 +// edition:2018 #![warn(clippy::single_component_path_imports)] #![allow(unused_imports)] @@ -7,6 +7,15 @@ 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!(); } diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index d084425cd70f..9a427e90ad3d 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -1,5 +1,5 @@ // run-rustfix -// compile-flags: --edition 2018 +// edition:2018 #![warn(clippy::single_component_path_imports)] #![allow(unused_imports)] @@ -7,6 +7,15 @@ 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!(); } diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 80ddeabdb2fd..f69554d75f9b 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -13,8 +13,8 @@ LL | | }; help: try this | LL | if let Some(y) = x { -LL | println!("{:?}", y); -LL | }; +LL | println!("{:?}", y); +LL | }; | error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 3f29f5aaf6a3..59861d46eb34 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -14,9 +14,9 @@ LL | | } help: try this | LL | if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { -LL | let x = 5; -LL | None -LL | } +LL | let x = 5; +LL | None +LL | } | error: aborting due to previous error diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 82931bf91fdf..0f8f839a0d54 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -5,7 +5,7 @@ clippy::blacklisted_name, clippy::no_effect, clippy::redundant_clone, - redundant_semicolon, + redundant_semicolons, unused_assignments )] diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index 4582db4b40e2..5763d9e82d48 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -5,7 +5,7 @@ clippy::blacklisted_name, clippy::no_effect, clippy::redundant_clone, - redundant_semicolon, + redundant_semicolons, unused_assignments )] diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 37162ecf4756..8582080498f3 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,4 +1,4 @@ -error: transmute from a type (`&'a T`) to itself +error: transmute from a type (`&T`) to itself --> $DIR/transmute.rs:19:20 | LL | let _: &'a T = core::intrinsics::transmute(t); diff --git a/tests/ui/transmute_ptr_to_ptr.stderr b/tests/ui/transmute_ptr_to_ptr.stderr index 4d1b8fcc199e..61fbea1c164e 100644 --- a/tests/ui/transmute_ptr_to_ptr.stderr +++ b/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,3 +1,17 @@ +error: transmute from a type (`&T`) to itself + --> $DIR/transmute_ptr_to_ptr.rs:8:5 + | +LL | std::mem::transmute::<&'a T, &'static T>(t) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` + +error: transmute from a type (`&T`) to itself + --> $DIR/transmute_ptr_to_ptr.rs:13:5 + | +LL | std::mem::transmute::<&'a T, &'b T>(t) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: transmute from a pointer to a pointer --> $DIR/transmute_ptr_to_ptr.rs:29:29 | @@ -36,5 +50,17 @@ error: transmute from a reference to a reference LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` -error: aborting due to 6 previous errors +error: transmute from a type (`&LifetimeParam`) to itself + --> $DIR/transmute_ptr_to_ptr.rs:50:47 + | +LL | let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`&GenericParam<&LifetimeParam>`) to itself + --> $DIR/transmute_ptr_to_ptr.rs:51:62 + | +LL | let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 2d52e421816e..df0598a58cd3 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -42,13 +42,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) 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>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo`) --> $DIR/transmute_ptr_to_ref.rs:32:32 | LL | let _: &Foo = 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>`) +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) }; diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 578a6218ee03..c8c0a85dfc10 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -34,7 +34,7 @@ LL | | } LL | | ); | |______^ | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -48,7 +48,7 @@ LL | | } LL | | ); | |______^ | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -62,7 +62,7 @@ LL | | } LL | | ); | |______^ | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 @@ -76,7 +76,7 @@ LL | | } LL | | ); | |______^ | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + = 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 diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 8c659df831e3..fb89a9fce3d5 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -14,4 +14,10 @@ fn main() { &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; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 7bab5540c42f..4a0c8620dc13 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -14,4 +14,10 @@ fn main() { &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; } diff --git a/tests/ui/unseparated_prefix_literals.stderr b/tests/ui/unseparated_prefix_literals.stderr index d353d34fb357..d7dd526bcb9a 100644 --- a/tests/ui/unseparated_prefix_literals.stderr +++ b/tests/ui/unseparated_prefix_literals.stderr @@ -50,6 +50,8 @@ LL | 42usize ... 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 diff --git a/tests/ui/update-all-references.sh b/tests/ui/update-all-references.sh index 9f970ea26b71..30ba9188db43 100755 --- a/tests/ui/update-all-references.sh +++ b/tests/ui/update-all-references.sh @@ -12,7 +12,10 @@ if [[ "$1" == "--help" || "$1" == "-h" ]]; then echo "usage: $0" fi -BUILD_DIR=$PWD/target/debug/test_build_base +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" {} + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 802de31b783f..ebb3aa28daf3 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,5 +1,5 @@ // run-rustfix -// compile-flags: --edition 2018 +// edition:2018 #![warn(clippy::use_self)] #![allow(dead_code)] diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 605c4f8c41fd..8a182192ab34 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,5 +1,5 @@ // run-rustfix -// compile-flags: --edition 2018 +// edition:2018 #![warn(clippy::use_self)] #![allow(dead_code)] diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 3cdb1cb32495..b33928597c14 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -56,6 +56,8 @@ LL | fn new() -> Foo { ... 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 @@ -65,6 +67,8 @@ LL | Foo {} ... 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 diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index 56811a998728..ff626ab3ddfa 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -20,6 +20,10 @@ extern crate proc_macro_derive; #[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] diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index bbf5e495db4a..822c6b6ea068 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -20,6 +20,10 @@ extern crate proc_macro_derive; #[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] diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 08a211b41a91..57ba976730c1 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ 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:49:5 + --> $DIR/useless_attribute.rs:53:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/tests/ui/verbose_file_reads.rs b/tests/ui/verbose_file_reads.rs new file mode 100644 index 000000000000..e0065e05ade6 --- /dev/null +++ b/tests/ui/verbose_file_reads.rs @@ -0,0 +1,28 @@ +#![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(()) +} diff --git a/tests/ui/verbose_file_reads.stderr b/tests/ui/verbose_file_reads.stderr new file mode 100644 index 000000000000..550b6ab679f1 --- /dev/null +++ b/tests/ui/verbose_file_reads.stderr @@ -0,0 +1,19 @@ +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 + diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed new file mode 100644 index 000000000000..f447a92715dc --- /dev/null +++ b/tests/ui/wildcard_imports.fixed @@ -0,0 +1,156 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![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 { + 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(); +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs new file mode 100644 index 000000000000..3fd66763a9fc --- /dev/null +++ b/tests/ui/wildcard_imports.rs @@ -0,0 +1,157 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![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 { + 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(); +} diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr new file mode 100644 index 000000000000..bebd9c1f8520 --- /dev/null +++ b/tests/ui/wildcard_imports.stderr @@ -0,0 +1,96 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:10: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:11:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:12: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:14:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:18: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:19:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:88:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:94: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:95:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:106:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:106:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:113:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:142: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:151:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:152:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: aborting due to 15 previous errors + diff --git a/util/export.py b/util/export.py index e8fc4d84ea4f..5d1bd60acf3d 100755 --- a/util/export.py +++ b/util/export.py @@ -71,7 +71,9 @@ def main(): outfile = sys.argv[1] if len(sys.argv) > 1 else "util/gh-pages/lints.json" with open(outfile, "w") as fp: - json.dump(list(lints.values()), fp, indent=2) + lints = list(lints.values()) + lints.sort(key=lambda x: x['id']) + json.dump(lints, fp, indent=2) log.info("wrote JSON for great justice") diff --git a/util/gh-pages/versions.html b/util/gh-pages/versions.html index 391e22a4743c..cd3611db3005 100644 --- a/util/gh-pages/versions.html +++ b/util/gh-pages/versions.html @@ -36,7 +36,7 @@

@@ -54,13 +54,17 @@

.controller('docVersions', function ($scope, $http) { $scope.loading = true; - $scope.normalizeVersion = function(v) { + $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 === 'current') { return Number.MAX_VALUE; } + if (v === 'stable') { return Number.MAX_VALUE; } return $scope.normalizeVersion(v) .split('.') diff --git a/util/lintlib.py b/util/lintlib.py index a260e00cde46..16cc6ccfdae3 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -14,7 +14,7 @@ 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+). (.*).*\n\s*\([^,]+,\s+"([^"]+)",\s+([^=\)]+)=>\s+(.*)\),''', re.MULTILINE) + r'''/// Lint: (\w+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE) comment_re = re.compile(r'''\s*/// ?(.*)''') lint_levels = { @@ -93,7 +93,7 @@ def parse_configs(path): match = re.search(conf_re, contents) confvars = re.findall(confvar_re, match.group(1)) - for (lint, doc, name, default, ty) in confvars: + for (lint, doc, name, ty, default) in confvars: configs[lint.lower()] = Config(name.replace("_", "-"), ty, doc, default) return configs diff --git a/util/versions.py b/util/versions.py new file mode 100755 index 000000000000..5798761ad75b --- /dev/null +++ b/util/versions.py @@ -0,0 +1,42 @@ +#!/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 + + 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()