diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config
new file mode 100644
index 00000000000..2bad3b9c57f
--- /dev/null
+++ b/src/tools/clippy/.cargo/config
@@ -0,0 +1,6 @@
+[alias]
+uitest = "test --test compile-test"
+dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
+
+[build]
+rustflags = ["-Zunstable-options"]
diff --git a/src/tools/clippy/.editorconfig b/src/tools/clippy/.editorconfig
new file mode 100644
index 00000000000..a13173544d8
--- /dev/null
+++ b/src/tools/clippy/.editorconfig
@@ -0,0 +1,19 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.yml]
+indent_size = 2
diff --git a/src/tools/clippy/.gitattributes b/src/tools/clippy/.gitattributes
new file mode 100644
index 00000000000..90cf33053c7
--- /dev/null
+++ b/src/tools/clippy/.gitattributes
@@ -0,0 +1,3 @@
+* text=auto eol=lf
+*.rs text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
+*.fixed linguist-language=Rust
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE.md b/src/tools/clippy/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000000..15006a07b44
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,8 @@
+
diff --git a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..97aa220afea
--- /dev/null
+++ b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,31 @@
+Thank you for making Clippy better!
+
+We're collecting our changelog from pull request descriptions.
+If your PR only updates to the latest nightly, you can leave the
+`changelog` entry as `none`. Otherwise, please write a short comment
+explaining your change.
+
+If your PR fixes an issue, you can add "fixes #issue_number" into this
+PR description. This way the issue will be automatically closed when
+your PR is merged.
+
+If you added a new lint, here's a checklist for things that will be
+checked during review or continuous integration.
+
+- [ ] Followed [lint naming conventions][lint_naming]
+- [ ] Added passing UI tests (including committed `.stderr` file)
+- [ ] `cargo test` passes locally
+- [ ] Executed `cargo dev update_lints`
+- [ ] Added lint documentation
+- [ ] Run `cargo dev fmt`
+
+[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
+
+Note that you can skip the above if you are just opening a WIP PR in
+order to get feedback.
+
+Delete this line and everything above before opening your PR.
+
+---
+
+changelog: none
diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh
new file mode 100644
index 00000000000..3f425e5b725
--- /dev/null
+++ b/src/tools/clippy/.github/deploy.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+set -ex
+
+echo "Removing the current docs for master"
+rm -rf out/master/ || exit 0
+
+echo "Making the docs for master"
+mkdir out/master/
+cp util/gh-pages/index.html out/master
+python3 ./util/export.py out/master/lints.json
+
+if [[ -n $TAG_NAME ]]; then
+ echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
+ cp -r out/master "out/$TAG_NAME"
+ rm -f out/stable
+ ln -s "$TAG_NAME" out/stable
+fi
+
+if [[ $BETA = "true" ]]; then
+ echo "Update documentation for the beta release"
+ cp -r out/master out/beta
+fi
+
+# Generate version index that is shown as root index page
+cp util/gh-pages/versions.html out/index.html
+
+echo "Making the versions.json file"
+python3 ./util/versions.py out
+
+cd out
+# Now let's go have some fun with the cloned repo
+git config user.name "GHA CI"
+git config user.email "gha@ci.invalid"
+
+if git diff --exit-code --quiet; then
+ echo "No changes to the output on this push; exiting."
+ exit 0
+fi
+
+if [[ -n $TAG_NAME ]]; then
+ # Add the new dir
+ git add "$TAG_NAME"
+ # Update the symlink
+ git add stable
+ # Update versions file
+ git add versions.json
+ git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
+elif [[ $BETA = "true" ]]; then
+ git add beta
+ git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}"
+else
+ git add .
+ git commit -m "Automatic deploy to GitHub Pages: ${SHA}"
+fi
+
+git push "$SSH_REPO" "$TARGET_BRANCH"
diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh
new file mode 100644
index 00000000000..a2e87f5eb37
--- /dev/null
+++ b/src/tools/clippy/.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/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
new file mode 100644
index 00000000000..8edf0c23860
--- /dev/null
+++ b/src/tools/clippy/.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/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
new file mode 100644
index 00000000000..6675a1029bb
--- /dev/null
+++ b/src/tools/clippy/.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/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
new file mode 100644
index 00000000000..ec3b43c2f43
--- /dev/null
+++ b/src/tools/clippy/.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:
+ - 'CHANGELOG.md'
+ - 'README.md'
+ - '**.stderr'
+ - '**.rs'
+
+env:
+ RUST_BACKTRACE: 1
+
+jobs:
+ clippy_dev:
+ runs-on: ubuntu-latest
+
+ steps:
+ # Setup
+ - name: rust-toolchain
+ uses: actions-rs/toolchain@v1.0.3
+ with:
+ toolchain: nightly
+ target: x86_64-unknown-linux-gnu
+ profile: minimal
+ components: rustfmt
+
+ - name: Checkout
+ uses: actions/checkout@v2.0.0
+
+ # Run
+ - name: Build
+ run: cargo build --features deny-warnings
+ working-directory: clippy_dev
+
+ - name: Test limit_stderr_length
+ run: cargo dev limit_stderr_length
+
+ - name: Test update_lints
+ run: cargo dev update_lints --check
+
+ - name: Test fmt
+ run: cargo dev fmt --check
+
+ # These jobs doesn't actually test anything, but they're only used to tell
+ # bors the build completed, as there is no practical way to detect when a
+ # workflow is successful listening to webhooks only.
+ #
+ # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+
+ end-success:
+ name: bors dev test finished
+ if: github.event.pusher.name == 'bors' && success()
+ runs-on: ubuntu-latest
+ needs: [clippy_dev]
+
+ steps:
+ - name: Mark the job as successful
+ run: exit 0
+
+ end-failure:
+ name: bors dev test finished
+ if: github.event.pusher.name == 'bors' && (failure() || cancelled())
+ runs-on: ubuntu-latest
+ needs: [clippy_dev]
+
+ steps:
+ - name: Mark the job as a failure
+ run: exit 1
diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml
new file mode 100644
index 00000000000..f542f9b02c1
--- /dev/null
+++ b/src/tools/clippy/.github/workflows/deploy.yml
@@ -0,0 +1,51 @@
+name: Deploy
+
+on:
+ push:
+ branches:
+ - master
+ - beta
+ tags:
+ - rust-1.**
+
+env:
+ TARGET_BRANCH: 'gh-pages'
+ SHA: '${{ github.sha }}'
+ SSH_REPO: 'git@github.com:${{ github.repository }}.git'
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ if: github.repository == 'rust-lang/rust-clippy'
+
+ steps:
+ # Setup
+ - name: Checkout
+ uses: actions/checkout@v2.0.0
+
+ - name: Checkout
+ uses: actions/checkout@v2.0.0
+ with:
+ ref: ${{ env.TARGET_BRANCH }}
+ path: 'out'
+
+ # Run
+ - name: Set tag name
+ if: startswith(github.ref, 'refs/tags/')
+ run: |
+ TAG=$(basename ${{ github.ref }})
+ echo "::set-env name=TAG_NAME::$TAG"
+ - name: Set beta to true
+ if: github.ref == 'refs/heads/beta'
+ run: echo "::set-env name=BETA::true"
+
+ - name: Use scripts and templates from master branch
+ run: |
+ git fetch --no-tags --prune --depth=1 origin master
+ git checkout origin/master -- .github/deploy.sh util/gh-pages/ util/*.py
+
+ - name: Deploy
+ run: |
+ eval "$(ssh-agent -s)"
+ ssh-add - <<< "${{ secrets.DEPLOY_KEY }}"
+ bash .github/deploy.sh
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
new file mode 100644
index 00000000000..cc175e8bf24
--- /dev/null
+++ b/src/tools/clippy/.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/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore
new file mode 100644
index 00000000000..adf5e8feddf
--- /dev/null
+++ b/src/tools/clippy/.gitignore
@@ -0,0 +1,37 @@
+# Used by CI to be able to push:
+/.github/deploy_key
+out
+
+# Compiled files
+*.o
+*.d
+*.so
+*.rlib
+*.dll
+*.pyc
+*.rmeta
+
+# Executables
+*.exe
+
+# Generated by Cargo
+*Cargo.lock
+/target
+/clippy_lints/target
+/clippy_workspace_tests/target
+/clippy_dev/target
+/rustc_tools_util/target
+
+# Generated by dogfood
+/target_recur/
+
+# gh pages docs
+util/gh-pages/lints.json
+
+# rustfmt backups
+*.rs.bk
+
+helper.txt
+*.iml
+.vscode
+.idea
diff --git a/src/tools/clippy/.remarkrc b/src/tools/clippy/.remarkrc
new file mode 100644
index 00000000000..0ede7ac75cb
--- /dev/null
+++ b/src/tools/clippy/.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/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
new file mode 100644
index 00000000000..575773580c0
--- /dev/null
+++ b/src/tools/clippy/CHANGELOG.md
@@ -0,0 +1,1657 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Changelog Update](doc/changelog_update.md) if you want to update this
+document.
+
+## Unreleased / In Rust Nightly
+
+[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master)
+
+## Rust 1.44
+
+Current beta, release 2020-06-04
+
+[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
+
+### New lints
+
+* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
+* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
+* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
+* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
+* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
+* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
+* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
+* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
+* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
+* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+
+
+### Moves and Deprecations
+
+* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
+* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
+* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
+* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
+* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
+* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
+* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
+* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
+* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
+
+### Enhancements
+
+* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
+ auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
+* Make [`redundant_clone`] also trigger on cases where the cloned value is not
+ consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
+* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
+* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
+* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
+* [`bool_comparison`] now also checks for inequality comparisons that can be
+ written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
+* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
+* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
+* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
+* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
+* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
+
+### False Positive Fixes
+
+* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
+* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
+* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
+* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
+* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
+* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
+* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
+* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
+* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
+
+
+### Suggestion Improvements
+
+* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
+* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
+* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
+
+### ICE Fixes
+
+* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
+* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
+
+### Documentation
+
+* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
+* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
+* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
+* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
+* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
+ not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
+
+## Rust 1.43
+
+Current stable, released 2020-04-23
+
+[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
+
+### New lints
+
+* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
+* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
+* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
+* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
+* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
+* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
+* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
+
+### Moves and Deprecations
+
+* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
+
+### Enhancements
+
+* Make [`missing_errors_doc`] lint also trigger on `async` functions
+ [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
+* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
+* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
+
+### False Positive Fixes
+
+* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
+* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
+* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
+* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
+
+### Suggestion Improvements
+
+* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
+
+### ICE Fixes
+
+* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
+* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
+* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
+
+### Documentation
+
+* Improve documentation of [`iter_nth_zero`]
+* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
+
+### Others
+
+* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
+
+
+## Rust 1.42
+
+Released 2020-03-12
+
+[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
+
+### New lints
+
+* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
+* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
+* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
+* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
+* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
+* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
+* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
+* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
+* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
+* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
+
+### Moves and Deprecations
+
+* Move [`transmute_float_to_int`] from nursery to complexity group
+ [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
+* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
+* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+
+### Enhancements
+
+* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
+* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
+* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
+* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
+* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
+
+### False Positive Fixes
+
+* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
+* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
+* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
+* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
+* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
+* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Don't trigger [`let_underscore_must_use`] in external macros
+ [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
+* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
+
+### Suggestion Improvements
+
+* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
+* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
+* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
+* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
+* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
+* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
+* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+
+### ICE fixes
+
+* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
+
+### Documentation
+
+* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
+
+
+## Rust 1.41
+
+Released 2020-01-30
+
+[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
+
+* New Lints:
+ * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
+ * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
+ * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
+ * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
+ * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
+ * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
+ * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
+ * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
+ * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
+* Remove plugin interface, see
+ [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
+ details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
+* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
+* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
+ [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
+* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
+* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
+* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
+* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
+* Fix false positive in `print_with_newline` and `write_with_newline`
+ [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
+* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
+* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
+* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
+* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
+* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
+* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
+* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
+* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
+* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
+* Display help when running `clippy-driver` without arguments, instead of ICEing
+ [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
+* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
+* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
+* Improve Documentation by adding positive examples to some lints
+ [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
+
+## Rust 1.40
+
+Released 2019-12-19
+
+[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
+
+* New Lints:
+ * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
+ * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
+ * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
+ * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
+ * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
+ * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
+ * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
+ * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
+ * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
+* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
+* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
+* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
+* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
+* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
+* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
+* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
+* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
+* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
+* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
+* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
+* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
+* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
+* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
+* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
+
+## Rust 1.39
+
+Released 2019-11-07
+
+[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
+
+* New Lints:
+ * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
+ * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
+ * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
+ * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
+ * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
+ * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
+ * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
+* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
+* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
+* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
+* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
+* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
+* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
+* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
+* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
+* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
+* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
+* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
+* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
+* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
+* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
+* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
+* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
+* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
+* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
+* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
+* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
+* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
+* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
+* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
+* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
+* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
+* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
+* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
+* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
+* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
+
+## Rust 1.38
+
+Released 2019-09-26
+
+[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
+
+* New Lints:
+ * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
+ * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
+ * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
+* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
+* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
+* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
+* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
+* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
+* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
+* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
+* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
+* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
+* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
+* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
+* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
+* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
+* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
+
+## Rust 1.37
+
+Released 2019-08-15
+
+[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
+
+* New Lints:
+ * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
+ * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
+ * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
+* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
+ The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
+* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
+* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
+* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
+* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
+* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
+* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
+* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
+* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
+* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
+* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
+* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
+
+## Rust 1.36
+
+Released 2019-07-04
+
+[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
+
+* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
+* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
+* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
+* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
+* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
+* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
+* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
+* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
+* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
+* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
+* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
+* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
+* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
+* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
+* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
+* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
+* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
+* Add macro check for [`unnecessary_cast`] [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
+* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
+* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
+* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
+
+
+## Rust 1.35
+
+Released 2019-05-20
+
+[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
+
+* New lint: [`drop_bounds`] to detect `T: Drop` bounds
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
+* Move [`get_unwrap`] to the restriction category
+* Improve suggestions for [`iter_cloned_collect`]
+* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
+* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
+* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
+* Fix false positive in [`bool_comparison`] pertaining to non-bool types
+* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
+* Fix false positive in [`option_map_unwrap_or`] on non-copy types
+* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
+* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
+* Fix false positive in [`needless_continue`] pertaining to loop labels
+* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
+* Fix false positive for [`use_self`] in nested functions
+* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
+* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
+* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
+* Avoid triggering [`redundant_closure`] in macros
+* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
+
+## Rust 1.34
+
+Released 2019-04-10
+
+[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
+
+* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
+* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
+* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
+* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
+ configured using the `too-many-lines-threshold` configuration.
+* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
+* Expand `redundant_closure` to also work for methods (not only functions)
+* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
+* Fix false positive in `cast_sign_loss`
+* Fix false positive in `integer_arithmetic`
+* Fix false positive in `unit_arg`
+* Fix false positives in `implicit_return`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `cast_lossless`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `needless_bool`
+* Fix incorrect suggestion for `needless_range_loop`
+* Fix incorrect suggestion for `use_self`
+* Fix incorrect suggestion for `while_let_on_iterator`
+* Clippy is now slightly easier to invoke in non-cargo contexts. See
+ [#3665][pull3665] for more details.
+* We now have [improved documentation][adding_lints] on how to add new lints
+
+## Rust 1.33
+
+Released 2019-02-26
+
+[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
+
+* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
+* The `rust-clippy` repository is now part of the `rust-lang` org.
+* Rename `stutter` to `module_name_repetitions`
+* Merge `new_without_default_derive` into `new_without_default` lint
+* Move `large_digit_groups` from `style` group to `pedantic`
+* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
+ comparisons against booleans
+* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
+* Expand `redundant_clone` to work on struct fields
+* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
+* Expand `use_self` to work on tuple structs and also in local macros
+* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
+* Fix false positives in `implicit_return`
+* Fix false positives in `use_self`
+* Fix false negative in `clone_on_copy`
+* Fix false positive in `doc_markdown`
+* Fix false positive in `empty_loop`
+* Fix false positive in `if_same_then_else`
+* Fix false positive in `infinite_iter`
+* Fix false positive in `question_mark`
+* Fix false positive in `useless_asref`
+* Fix false positive in `wildcard_dependencies`
+* Fix false positive in `write_with_newline`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `get_unwrap`
+
+## Rust 1.32
+
+Released 2019-01-17
+
+[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
+
+* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
+ [`redundant_clone`], [`wildcard_dependencies`],
+ [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
+ [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
+* Add support for `u128` and `i128` to integer related lints
+* Add float support to `mistyped_literal_suffixes`
+* Fix false positives in `use_self`
+* Fix false positives in `missing_comma`
+* Fix false positives in `new_ret_no_self`
+* Fix false positives in `possible_missing_comma`
+* Fix false positive in `integer_arithmetic` in constant items
+* Fix false positive in `needless_borrow`
+* Fix false positive in `out_of_bounds_indexing`
+* Fix false positive in `new_without_default_derive`
+* Fix false positive in `string_lit_as_bytes`
+* Fix false negative in `out_of_bounds_indexing`
+* Fix false negative in `use_self`. It will now also check existential types
+* Fix incorrect suggestion for `redundant_closure_call`
+* Fix various suggestions that contained expanded macros
+* Fix `bool_comparison` triggering 3 times on on on the same code
+* Expand `trivially_copy_pass_by_ref` to work on trait methods
+* Improve suggestion for `needless_range_loop`
+* Move `needless_pass_by_value` from `pedantic` group to `style`
+
+## Rust 1.31
+
+Released 2018-12-06
+
+[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
+
+* Clippy has been relicensed under a dual MIT / Apache license.
+ See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
+ information.
+* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
+ installation method is via `rustup component add clippy`.
+* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
+ [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
+* Fix ICE in `if_let_redundant_pattern_matching`
+* Fix ICE in `needless_pass_by_value` when encountering a generic function
+ argument with a lifetime parameter
+* Fix ICE in `needless_range_loop`
+* Fix ICE in `single_char_pattern` when encountering a constant value
+* Fix false positive in `assign_op_pattern`
+* Fix false positive in `boxed_local` on trait implementations
+* Fix false positive in `cmp_owned`
+* Fix false positive in `collapsible_if` when conditionals have comments
+* Fix false positive in `double_parens`
+* Fix false positive in `excessive_precision`
+* Fix false positive in `explicit_counter_loop`
+* Fix false positive in `fn_to_numeric_cast_with_truncation`
+* Fix false positive in `map_clone`
+* Fix false positive in `new_ret_no_self`
+* Fix false positive in `new_without_default` when `new` is unsafe
+* Fix false positive in `type_complexity` when using extern types
+* Fix false positive in `useless_format`
+* Fix false positive in `wrong_self_convention`
+* Fix incorrect suggestion for `excessive_precision`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `get_unwrap`
+* Fix incorrect suggestion for `useless_format`
+* `fn_to_numeric_cast_with_truncation` lint can be disabled again
+* Improve suggestions for `manual_memcpy`
+* Improve help message for `needless_lifetimes`
+
+## Rust 1.30
+
+Released 2018-10-25
+
+[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
+
+* Deprecate `assign_ops` lint
+* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
+ [`needless_collect`], [`copy_iterator`]
+* `cargo clippy -V` now includes the Clippy commit hash of the Rust
+ Clippy component
+* Fix ICE in `implicit_hasher`
+* Fix ICE when encountering `println!("{}" a);`
+* Fix ICE when encountering a macro call in match statements
+* Fix false positive in `default_trait_access`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `similar_names`
+* Fix false positive in `redundant_field_name`
+* Fix false positive in `expect_fun_call`
+* Fix false negative in `identity_conversion`
+* Fix false negative in `explicit_counter_loop`
+* Fix `range_plus_one` suggestion and false negative
+* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
+* Fix `useless_attribute` to also whitelist `unused_extern_crates`
+* Fix incorrect suggestion for `single_char_pattern`
+* Improve suggestion for `identity_conversion` lint
+* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
+* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
+* Move `shadow_unrelated` from `restriction` group to `pedantic`
+* Move `indexing_slicing` from `pedantic` group to `restriction`
+
+## Rust 1.29
+
+Released 2018-09-13
+
+[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
+
+* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
+ :tada:
+ You can now run `rustup component add clippy-preview` and then `cargo
+ clippy` to run Clippy. This should put an end to the continuous nightly
+ upgrades for Clippy users.
+* Clippy now follows the Rust versioning scheme instead of its own
+* Fix ICE when encountering a `while let (..) = x.iter()` construct
+* Fix false positives in `use_self`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `useless_attribute` lint
+* Fix false positive in `print_literal`
+* Fix `use_self` regressions
+* Improve lint message for `neg_cmp_op_on_partial_ord`
+* Improve suggestion highlight for `single_char_pattern`
+* Improve suggestions for various print/write macro lints
+* Improve website header
+
+## 0.0.212 (2018-07-10)
+* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
+
+## 0.0.211
+* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
+
+## 0.0.210
+* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
+
+## 0.0.209
+* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
+
+## 0.0.208
+* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
+
+## 0.0.207
+* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
+
+## 0.0.206
+* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
+
+## 0.0.205
+* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
+* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
+
+## 0.0.204
+* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
+
+## 0.0.203
+* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
+* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
+
+## 0.0.202
+* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
+
+## 0.0.201
+* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
+
+## 0.0.200
+* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
+
+## 0.0.199
+* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
+
+## 0.0.198
+* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
+
+## 0.0.197
+* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
+
+## 0.0.196
+* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
+
+## 0.0.195
+* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
+
+## 0.0.194
+* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
+* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
+
+## 0.0.193
+* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
+
+## 0.0.192
+* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
+* New lint: [`print_literal`]
+
+## 0.0.191
+* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
+* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
+
+## 0.0.190
+* Fix a bunch of intermittent cargo bugs
+
+## 0.0.189
+* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
+
+## 0.0.188
+* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
+* New lint: [`while_immutable_condition`]
+
+## 0.0.187
+* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
+* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
+
+## 0.0.186
+* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
+* Various false positive fixes
+
+## 0.0.185
+* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
+* New lint: [`question_mark`]
+
+## 0.0.184
+* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
+* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
+
+## 0.0.183
+* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
+* New lint: [`misaligned_transmute`]
+
+## 0.0.182
+* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
+* New lint: [`decimal_literal_representation`]
+
+## 0.0.181
+* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
+* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
+* Removed `unit_expr`
+* Various false positive fixes for [`needless_pass_by_value`]
+
+## 0.0.180
+* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
+
+## 0.0.179
+* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
+
+## 0.0.178
+* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
+
+## 0.0.177
+* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
+* New lint: [`match_as_ref`]
+
+## 0.0.176
+* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
+
+## 0.0.175
+* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
+
+## 0.0.174
+* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
+
+## 0.0.173
+* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
+
+## 0.0.172
+* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
+
+## 0.0.171
+* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
+
+## 0.0.170
+* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
+
+## 0.0.169
+* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
+* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`]
+
+## 0.0.168
+* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
+
+## 0.0.167
+* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
+* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
+
+## 0.0.166
+* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
+* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
+ [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
+ [`transmute_int_to_float`]
+
+## 0.0.165
+* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
+* New lint: [`mut_range_bound`]
+
+## 0.0.164
+* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
+* New lint: [`int_plus_one`]
+
+## 0.0.163
+* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
+
+## 0.0.162
+* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
+* New lint: [`chars_last_cmp`]
+* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
+
+## 0.0.161
+* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
+
+## 0.0.160
+* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
+
+## 0.0.159
+* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
+* New lint: [`clone_on_ref_ptr`]
+
+## 0.0.158
+* New lint: [`manual_memcpy`]
+* [`cast_lossless`] no longer has redundant parentheses in its suggestions
+* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
+
+## 0.0.157 - 2017-09-04
+* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
+* New lint: `unit_expr`
+
+## 0.0.156 - 2017-09-03
+* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
+
+## 0.0.155
+* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
+* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
+
+## 0.0.154
+* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
+* Fix [`use_self`] triggering inside derives
+* Add support for linting an entire workspace with `cargo clippy --all`
+* New lint: [`naive_bytecount`]
+
+## 0.0.153
+* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
+* New lint: [`use_self`]
+
+## 0.0.152
+* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
+
+## 0.0.151
+* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
+
+## 0.0.150
+* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
+
+## 0.0.148
+* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
+* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
+
+## 0.0.147
+* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
+
+## 0.0.146
+* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
+* Fixes false positives in `inline_always`
+* Fixes false negatives in `panic_params`
+
+## 0.0.145
+* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
+
+## 0.0.144
+* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
+
+## 0.0.143
+* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
+* Fix `cargo clippy` crashing on `dylib` projects
+* Fix false positives around `nested_while_let` and `never_loop`
+
+## 0.0.142
+* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
+
+## 0.0.141
+* Rewrite of the `doc_markdown` lint.
+* Deprecated [`range_step_by_zero`]
+* New lint: [`iterator_step_by_zero`]
+* New lint: [`needless_borrowed_reference`]
+* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
+
+## 0.0.140 - 2017-06-16
+* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
+
+## 0.0.139 — 2017-06-10
+* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
+* Fix bugs with for loop desugaring
+* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
+
+## 0.0.138 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
+
+## 0.0.137 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
+
+## 0.0.136 — 2017—05—26
+* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
+
+## 0.0.135 — 2017—05—24
+* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
+
+## 0.0.134 — 2017—05—19
+* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
+
+## 0.0.133 — 2017—05—14
+* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
+
+## 0.0.132 — 2017—05—05
+* Fix various bugs and some ices
+
+## 0.0.131 — 2017—05—04
+* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
+
+## 0.0.130 — 2017—05—03
+* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
+
+## 0.0.129 — 2017-05-01
+* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
+
+## 0.0.128 — 2017-04-28
+* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
+
+## 0.0.127 — 2017-04-27
+* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
+* New lint: [`needless_continue`]
+
+## 0.0.126 — 2017-04-24
+* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
+
+## 0.0.125 — 2017-04-19
+* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
+
+## 0.0.124 — 2017-04-16
+* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
+
+## 0.0.123 — 2017-04-07
+* Fix various false positives
+
+## 0.0.122 — 2017-04-07
+* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
+* New lint: [`op_ref`]
+
+## 0.0.121 — 2017-03-21
+* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
+
+## 0.0.120 — 2017-03-17
+* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
+
+## 0.0.119 — 2017-03-13
+* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
+
+## 0.0.118 — 2017-03-05
+* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
+
+## 0.0.117 — 2017-03-01
+* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
+
+## 0.0.116 — 2017-02-28
+* Fix `cargo clippy` on 64 bit windows systems
+
+## 0.0.115 — 2017-02-27
+* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
+* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
+
+## 0.0.114 — 2017-02-08
+* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
+* Tests are now ui tests (testing the exact output of rustc)
+
+## 0.0.113 — 2017-02-04
+* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
+* New lint: [`large_enum_variant`]
+* `explicit_into_iter_loop` provides suggestions
+
+## 0.0.112 — 2017-01-27
+* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
+
+## 0.0.111 — 2017-01-21
+* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
+
+## 0.0.110 — 2017-01-20
+* Add badges and categories to `Cargo.toml`
+
+## 0.0.109 — 2017-01-19
+* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
+
+## 0.0.108 — 2017-01-12
+* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
+
+## 0.0.107 — 2017-01-11
+* Update regex dependency
+* Fix FP when matching `&&mut` by `&ref`
+* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
+* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
+
+## 0.0.106 — 2017-01-04
+* Fix FP introduced by rustup in [`wrong_self_convention`]
+
+## 0.0.105 — 2017-01-04
+* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
+* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
+* Fix suggestion in [`new_without_default`]
+* FP fix in [`absurd_extreme_comparisons`]
+
+## 0.0.104 — 2016-12-15
+* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
+
+## 0.0.103 — 2016-11-25
+* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
+
+## 0.0.102 — 2016-11-24
+* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
+
+## 0.0.101 — 2016-11-23
+* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
+* New lint: [`string_extend_chars`]
+
+## 0.0.100 — 2016-11-20
+* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
+
+## 0.0.99 — 2016-11-18
+* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
+* New lint: [`get_unwrap`]
+
+## 0.0.98 — 2016-11-08
+* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
+
+## 0.0.97 — 2016-11-03
+* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
+ previously added for a short time under the name `clippy` but removed for
+ compatibility.
+* `cargo clippy --help` is more helping (and less helpful :smile:)
+* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
+* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
+
+## 0.0.96 — 2016-10-22
+* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
+* New lint: [`iter_skip_next`]
+
+## 0.0.95 — 2016-10-06
+* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
+
+## 0.0.94 — 2016-10-04
+* Fixes bustage on Windows due to forbidden directory name
+
+## 0.0.93 — 2016-10-03
+* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
+* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now
+ allowed by default.
+* New lint: [`explicit_into_iter_loop`]
+
+## 0.0.92 — 2016-09-30
+* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
+
+## 0.0.91 — 2016-09-28
+* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
+
+## 0.0.90 — 2016-09-09
+* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
+
+## 0.0.89 — 2016-09-06
+* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
+
+## 0.0.88 — 2016-09-04
+* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
+* The following lints are not new but were only usable through the `clippy`
+ lint groups: [`filter_next`], [`for_loop_over_option`],
+ [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be
+ able to `#[allow/deny]` them individually and they are available directly
+ through `cargo clippy`.
+
+## 0.0.87 — 2016-08-31
+* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
+* New lints: [`builtin_type_shadow`]
+* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
+
+## 0.0.86 — 2016-08-28
+* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
+* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
+
+## 0.0.85 — 2016-08-19
+* Fix ICE with [`useless_attribute`]
+* [`useless_attribute`] ignores `unused_imports` on `use` statements
+
+## 0.0.84 — 2016-08-18
+* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
+
+## 0.0.83 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
+* New lints: [`print_with_newline`], [`useless_attribute`]
+
+## 0.0.82 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
+* New lint: [`module_inception`]
+
+## 0.0.81 — 2016-08-14
+* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
+* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
+* False positive fix in [`too_many_arguments`]
+* Addition of functionality to [`needless_borrow`]
+* Suggestions for [`clone_on_copy`]
+* Bug fix in [`wrong_self_convention`]
+* Doc improvements
+
+## 0.0.80 — 2016-07-31
+* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
+* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
+
+## 0.0.79 — 2016-07-10
+* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
+* Major suggestions refactoring
+
+## 0.0.78 — 2016-07-02
+* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
+* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
+* For compatibility, `cargo clippy` does not defines the `clippy` feature
+ introduced in 0.0.76 anymore
+* [`collapsible_if`] now considers `if let`
+
+## 0.0.77 — 2016-06-21
+* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
+* New lints: `stutter` and [`iter_nth`]
+
+## 0.0.76 — 2016-06-10
+* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
+* `cargo clippy` now automatically defines the `clippy` feature
+* New lint: [`not_unsafe_ptr_arg_deref`]
+
+## 0.0.75 — 2016-06-08
+* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
+
+## 0.0.74 — 2016-06-07
+* Fix bug with `cargo-clippy` JSON parsing
+* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
+ “for further information visit *lint-link*” message.
+
+## 0.0.73 — 2016-06-05
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.72 — 2016-06-04
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.71 — 2016-05-31
+* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
+* New lint: [`useless_let_if_seq`]
+
+## 0.0.70 — 2016-05-28
+* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
+* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
+ `RegexBuilder::new` and byte regexes
+
+## 0.0.69 — 2016-05-20
+* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
+* [`used_underscore_binding`] has been made `Allow` temporarily
+
+## 0.0.68 — 2016-05-17
+* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
+* New lint: [`unnecessary_operation`]
+
+## 0.0.67 — 2016-05-12
+* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
+
+## 0.0.66 — 2016-05-11
+* New `cargo clippy` subcommand
+* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
+
+## 0.0.65 — 2016-05-08
+* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
+* New lints: [`float_arithmetic`], [`integer_arithmetic`]
+
+## 0.0.64 — 2016-04-26
+* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
+* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
+
+## 0.0.63 — 2016-04-08
+* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
+
+## 0.0.62 — 2016-04-07
+* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
+
+## 0.0.61 — 2016-04-03
+* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
+* New lint: [`invalid_upcast_comparisons`]
+
+## 0.0.60 — 2016-04-01
+* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
+
+## 0.0.59 — 2016-03-31
+* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
+* New lints: [`logic_bug`], [`nonminimal_bool`]
+* Fixed: [`match_same_arms`] now ignores arms with guards
+* Improved: [`useless_vec`] now warns on `for … in vec![…]`
+
+## 0.0.58 — 2016-03-27
+* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
+* New lint: [`doc_markdown`]
+
+## 0.0.57 — 2016-03-27
+* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
+* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
+* New lint: [`crosspointer_transmute`]
+
+## 0.0.56 — 2016-03-23
+* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
+* New lints: [`many_single_char_names`] and [`similar_names`]
+
+## 0.0.55 — 2016-03-21
+* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
+
+## 0.0.54 — 2016-03-16
+* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
+
+## 0.0.53 — 2016-03-15
+* Add a [configuration file]
+
+## ~~0.0.52~~
+
+## 0.0.51 — 2016-03-13
+* Add `str` to types considered by [`len_zero`]
+* New lints: [`indexing_slicing`]
+
+## 0.0.50 — 2016-03-11
+* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
+
+## 0.0.49 — 2016-03-09
+* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
+* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
+
+## 0.0.48 — 2016-03-07
+* Fixed: ICE in [`needless_range_loop`] with globals
+
+## 0.0.47 — 2016-03-07
+* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
+* New lint: [`redundant_closure_call`]
+
+[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
+[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
+[configuration file]: ./rust-clippy#configuration
+[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+
+
+
+[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
+[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
+[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
+[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
+[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
+[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
+[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
+[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
+[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
+[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
+[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
+[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
+[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
+[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
+[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
+[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
+[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
+[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
+[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
+[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
+[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
+[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
+[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
+[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
+[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
+[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
+[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
+[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
+[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
+[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
+[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
+[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
+[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
+[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
+[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
+[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
+[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
+[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
+[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
+[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
+[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
+[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
+[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
+[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
+[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
+[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
+[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
+[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
+[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
+[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
+[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
+[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
+[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
+[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
+[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
+[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
+[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
+[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
+[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
+[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
+[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
+[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
+[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
+[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
+[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
+[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
+[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
+[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
+[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
+[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
+[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
+[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
+[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
+[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
+[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
+[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
+[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
+[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
+[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
+[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
+[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
+[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
+[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
+[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
+[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
+[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
+[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
+[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
+[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
+[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
+[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
+[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
+[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
+[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
+[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
+[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
+[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
+[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
+[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
+[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
+[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
+[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
+[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
+[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
+[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
+[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
+[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
+[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
+[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
+[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
+[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
+[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
+[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
+[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
+[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
+[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
+[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
+[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
+[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
+[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
+[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
+[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
+[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
+[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
+[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
+[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
+[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
+[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
+[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
+[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
+[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
+[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
+[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
+[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
+[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
+[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
+[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
+[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
+[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
+[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
+[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
+[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
+[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
+[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
+[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
+[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
+[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
+[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
+[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
+[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
+[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
+[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
+[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
+[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
+[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
+[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
+[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
+[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
+[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
+[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
+[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
+[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
+[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
+[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
+[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
+[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
+[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
+[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
+[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
+[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
+[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
+[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
+[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
+[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
+[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
+[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
+[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
+[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
+[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
+[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
+[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
+[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
+[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
+[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
+[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
+[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
+[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
+[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
+[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
+[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
+[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
+[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
+[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
+[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
+[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
+[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
+[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
+[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
+[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
+[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
+[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
+[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
+[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
+[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
+[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
+[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
+[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
+[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
+[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
+[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
+[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
+[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
+[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
+[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
+[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
+[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
+[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
+[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
+[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
+[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
+[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
+[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
+[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
+[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
+[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
+[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
+[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
+[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
+[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
+[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
+[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
+[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
+[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
+[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
+[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
+[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
+[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
+[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
+[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
+[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
+[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
+[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
+[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
+[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
+[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
+[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
+[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
+[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
+[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
+[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
+[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
+[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
+[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
+[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
+[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
+[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
+[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
+[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
+[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
+[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
+[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
+[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
+[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
+[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
+[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
+[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
+[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
+[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
+[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
+[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
+[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
+[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
+[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
+[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
+[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
+[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
+[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
+[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
+[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
+[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
+[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
+[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
+[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
+[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
+[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
+[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
+[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
+[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
+[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
+[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
+[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
+[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
+[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
+[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
+[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
+[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
+[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
+[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
+[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
+[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
+[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
+[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
+[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
+[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
+[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
+[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
+[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
+[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
+[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
+[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
+[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
+[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
+[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
+[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
+[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
+[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
+[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
+[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
+[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
+[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
+[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
+[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
+[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
+[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
+[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
+[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
+[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
+[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
+[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
+[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
+[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
+[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
+[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
+[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
+[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
+[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
+[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
+[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
+[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
+[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
+[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
+[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
+[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
+[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
+[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
+[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
+[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
+[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
+[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
+[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
+[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
+[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
+[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
+
diff --git a/src/tools/clippy/CODE_OF_CONDUCT.md b/src/tools/clippy/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000000..dec13e44a17
--- /dev/null
+++ b/src/tools/clippy/CODE_OF_CONDUCT.md
@@ -0,0 +1,70 @@
+# The Rust Code of Conduct
+
+A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
+
+## Conduct
+
+**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
+
+* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience,
+ gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
+ religion, nationality, or other similar characteristic.
+* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and
+ welcoming environment for all.
+* Please be kind and courteous. There's no need to be mean or rude.
+* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and
+ numerous costs. There is seldom a right answer.
+* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and
+ see how it works.
+* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We
+ interpret the term "harassment" as including the definition in the 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].
+
+1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks,
+ are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
+2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
+3. Moderators will first respond to such remarks with a warning.
+4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
+5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
+6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended
+ party a genuine apology.
+7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a
+ different moderator, **in private**. Complaints about bans in-channel are not allowed.
+8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate
+ situation, they should expect less leeway than others.
+
+In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically
+unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly
+if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can
+drive people away from the community entirely.
+
+And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was
+they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good
+there was something you could've communicated better — remember that it's your responsibility to make your fellow
+Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about
+cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their
+trust.
+
+The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust,
+#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo);
+GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org
+(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the
+maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider
+explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
+
+*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the
+[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
+
+[mod_team]: https://www.rust-lang.org/team.html#Moderation-team
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
new file mode 100644
index 00000000000..50a5ee8bbf3
--- /dev/null
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -0,0 +1,243 @@
+# Contributing to Clippy
+
+Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
+
+**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
+yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
+something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
+
+Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
+explains how you can contribute and how to get started. If you have any questions about contributing or need help with
+anything, feel free to ask questions on issues or visit the `#clippy` on [Discord].
+
+All contributors are expected to follow the [Rust Code of Conduct].
+
+* [Getting started](#getting-started)
+ * [Finding something to fix/improve](#finding-something-to-fiximprove)
+* [Writing code](#writing-code)
+* [How Clippy works](#how-clippy-works)
+* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
+* [Issue and PR Triage](#issue-and-pr-triage)
+* [Bors and Homu](#bors-and-homu)
+* [Contributions](#contributions)
+
+[Discord]: https://discord.gg/rust-lang
+[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
+
+## Getting started
+
+High level approach:
+
+1. Find something to fix/improve
+2. Change code (likely some file in `clippy_lints/src/`)
+3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
+4. Run `cargo test` in the root directory and wiggle code until it passes
+5. Open a PR (also can be done after 2. if you run into problems)
+
+### Finding something to fix/improve
+
+All issues on Clippy are mentored, if you want help with a bug just ask
+@Manishearth, @flip1995, @phansch or @yaahc.
+
+Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
+If you want to work on an issue, please leave a comment so that we can assign it to you!
+
+There are also some abandoned PRs, marked with [`S-inactive-closed`].
+Pretty often these PRs are nearly completed and just need some extra steps
+(formatting, addressing review comments, ...) to be merged. If you want to
+complete such a PR, please leave a comment in the PR and open a new one based
+on it.
+
+Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
+and are generally easier than [`T-middle`] issues, which involve types
+and resolved paths.
+
+[`T-AST`] issues will generally need you to match against a predefined syntax structure.
+To figure out how this syntax structure is encoded in the AST, it is recommended to run
+`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs].
+Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
+But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
+
+[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first.
+They are mostly classified as [`E-medium`], since they might be somewhat involved code wise,
+but not difficult per-se.
+
+[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
+lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
+an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
+
+[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue
+[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
+[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
+[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
+[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
+[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
+[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
+[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43
+[if_chain]: https://docs.rs/if_chain/*/if_chain
+[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150
+
+## Writing code
+
+Have a look at the [docs for writing lints][adding_lints] for more details.
+
+If you want to add a new lint or change existing ones apart from bugfixing, it's
+also a good idea to give the [stability guarantees][rfc_stability] and
+[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
+quick read.
+
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md
+[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
+[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
+
+## How Clippy works
+
+[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
+For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
+
+```rust
+// ./clippy_lints/src/lib.rs
+
+// ...
+pub mod else_if_without_else;
+// ...
+
+pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
+ // ...
+ store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
+ // ...
+
+ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
+ // ...
+ LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+ // ...
+ ]);
+}
+```
+
+The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
+[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
+that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
+every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
+update_lints`. When you are writing your own lint, you can use that script to save you some time.
+
+```rust
+// ./clippy_lints/src/else_if_without_else.rs
+
+use rustc_lint::{EarlyLintPass, EarlyContext};
+
+// ...
+
+pub struct ElseIfWithoutElse;
+
+// ...
+
+impl EarlyLintPass for ElseIfWithoutElse {
+ // ... the functions needed, to make the lint work
+}
+```
+
+The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
+AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
+via the `LateContext` parameter.
+
+That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
+[actual lint logic][else_if_without_else] does not depend on any type information.
+
+[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
+[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
+[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
+[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
+[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
+[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+
+## Fixing build failures caused by Rust
+
+Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of
+the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures
+caused by Rust updates, can be a good way to learn about Rust internals.
+
+In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit
+history][toolstate_commit_history]. You will then have to look for the last commit that contains
+`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component.
+[Here][toolstate_commit] is an example.
+
+The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and
+if they are bigger, they likely include some discussion that may help you to fix Clippy.
+
+To check if Clippy is available for a specific target platform, you can check
+the [rustup component history][rustup_component_history].
+
+If you decide to make Clippy work again with a Rust commit that breaks it,
+you probably want to install the latest Rust from master locally and run Clippy
+using that version of Rust.
+
+You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install
+[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`.
+
+After fixing the build failure on this repository, we can submit a pull request
+to [`rust-lang/rust`] to fix the toolstate.
+
+To submit a pull request, you should follow these steps:
+
+```bash
+# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory
+git submodule update --remote src/tools/clippy
+cargo update -p clippy
+git add -u
+git commit -m "Update Clippy"
+./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway
+# Open a PR in rust-lang/rust
+```
+
+[rustup_component_history]: https://rust-lang.github.io/rustup-components-history
+[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master
+[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727
+[rtim]: https://github.com/kennytm/rustup-toolchain-install-master
+[`rust-lang/rust`]: https://github.com/rust-lang/rust
+
+## Issue and PR triage
+
+Clippy is following the [Rust triage procedure][triage] for issues and pull
+requests.
+
+However, we are a smaller project with all contributors being volunteers
+currently. Between writing new lints, fixing issues, reviewing pull requests and
+responding to issues there may not always be enough time to stay on top of it
+all.
+
+Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't
+want Clippy to crash on your code and we want it to be as reliable as the
+suggestions from Rust compiler errors.
+
+## Bors and Homu
+
+We use a bot powered by [Homu][homu] to help automate testing and landing of pull
+requests in Clippy. The bot's username is @bors.
+
+You can find the Clippy bors queue [here][homu_queue].
+
+If you have @bors permissions, you can find an overview of the available
+commands [here][homu_instructions].
+
+[triage]: https://forge.rust-lang.org/release/triage-procedure.html
+[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A
+[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A
+[homu]: https://github.com/rust-lang/homu
+[homu_instructions]: https://buildbot2.rust-lang.org/homu/
+[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy
+
+## Contributions
+
+Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
+be reviewed by a core contributor (someone with permission to land patches) and either landed in the
+main tree or given feedback for changes that would be required.
+
+All code in this repository is under the [Apache-2.0] or the [MIT] license.
+
+
+
+[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
+[MIT]: https://opensource.org/licenses/MIT
diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT
new file mode 100644
index 00000000000..80d64472c70
--- /dev/null
+++ b/src/tools/clippy/COPYRIGHT
@@ -0,0 +1,7 @@
+Copyright 2014-2020 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0 or the MIT license
+, at your
+option. All files in the project carrying such notice may not be
+copied, modified, or distributed except according to those terms.
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
new file mode 100644
index 00000000000..63ce2cd8cad
--- /dev/null
+++ b/src/tools/clippy/Cargo.toml
@@ -0,0 +1,59 @@
+[package]
+name = "clippy"
+version = "0.0.212"
+authors = [
+ "Manish Goregaokar ",
+ "Andre Bogus ",
+ "Georg Brandl ",
+ "Martin Carton ",
+ "Oliver Schneider "
+]
+description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+categories = ["development-tools", "development-tools::cargo-plugins"]
+build = "build.rs"
+edition = "2018"
+publish = false
+
+[[bin]]
+name = "cargo-clippy"
+test = false
+path = "src/main.rs"
+
+[[bin]]
+name = "clippy-driver"
+path = "src/driver.rs"
+
+[dependencies]
+# begin automatic update
+clippy_lints = { version = "0.0.212", path = "clippy_lints" }
+# end automatic update
+regex = "1"
+semver = "0.9"
+rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
+tempfile = { version = "3.1.0", optional = true }
+lazy_static = "1.0"
+
+[dev-dependencies]
+cargo_metadata = "0.9.0"
+compiletest_rs = { version = "0.5.0", features = ["tmp"] }
+tester = "0.7"
+lazy_static = "1.0"
+clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
+serde = { version = "1.0", features = ["derive"] }
+derive-new = "0.5"
+
+# A noop dependency that changes in the Rust repository, it's a bit of a hack.
+# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
+# for more information.
+rustc-workspace-hack = "1.0.0"
+
+[build-dependencies]
+rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
+
+[features]
+deny-warnings = []
+integration = ["tempfile"]
diff --git a/src/tools/clippy/LICENSE-APACHE b/src/tools/clippy/LICENSE-APACHE
new file mode 100644
index 00000000000..d821a4de2be
--- /dev/null
+++ b/src/tools/clippy/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2014-2020 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/src/tools/clippy/LICENSE-MIT b/src/tools/clippy/LICENSE-MIT
new file mode 100644
index 00000000000..b7c70dd4026
--- /dev/null
+++ b/src/tools/clippy/LICENSE-MIT
@@ -0,0 +1,27 @@
+MIT License
+
+Copyright (c) 2014-2020 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
new file mode 100644
index 00000000000..222b81023a7
--- /dev/null
+++ b/src/tools/clippy/README.md
@@ -0,0 +1,193 @@
+# Clippy
+
+[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
+[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
+
+A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
+
+[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+
+We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
+
+* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`)
+* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default)
+* `clippy::style` (code that should be written in a more idiomatic way)
+* `clippy::complexity` (code that does something simple but in a complex way)
+* `clippy::perf` (code that can be written in a faster way)
+* `clippy::pedantic` (lints which are rather strict, off by default)
+* `clippy::nursery` (new lints that aren't quite ready yet, off by default)
+* `clippy::cargo` (checks against the cargo manifest, off by default)
+
+More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
+
+Only the following of those categories are enabled by default:
+
+* `clippy::style`
+* `clippy::correctness`
+* `clippy::complexity`
+* `clippy::perf`
+
+Other categories need to be enabled in order for their lints to be executed.
+
+The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
+for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
+very selectively, if at all.
+
+Table of contents:
+
+* [Usage instructions](#usage)
+* [Configuration](#configuration)
+* [Contributing](#contributing)
+* [License](#license)
+
+## Usage
+
+Since this is a tool for helping the developer of a library or application
+write better code, it is recommended not to include Clippy as a hard dependency.
+Options include using it as an optional dependency, as a cargo subcommand, or
+as an included feature during build. These options are detailed below.
+
+### As a cargo subcommand (`cargo clippy`)
+
+One way to use Clippy is by installing Clippy through rustup as a cargo
+subcommand.
+
+#### Step 1: Install rustup
+
+You can install [rustup](https://rustup.rs/) on supported platforms. This will help
+us install Clippy and its dependencies.
+
+If you already have rustup installed, update to ensure you have the latest
+rustup and compiler:
+
+```terminal
+rustup update
+```
+
+#### Step 2: Install Clippy
+
+Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
+
+```terminal
+rustup component add clippy
+```
+If it says that it can't find the `clippy` component, please run `rustup self update`.
+
+#### Step 3: Run Clippy
+
+Now you can run Clippy by invoking the following command:
+
+```terminal
+cargo clippy
+```
+
+#### Automatically applying Clippy suggestions
+
+Clippy can automatically apply some lint suggestions.
+Note that this is still experimental and only supported on the nightly channel:
+
+```terminal
+cargo clippy --fix -Z unstable-options
+```
+
+### Running Clippy from the command line without installing it
+
+To have cargo compile your crate with Clippy without Clippy installation
+in your code, you can use:
+
+```terminal
+cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
+```
+
+*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
+
+### Travis CI
+
+You can add Clippy to Travis CI in the same way you use it locally:
+
+```yml
+language: rust
+rust:
+ - stable
+ - beta
+before_script:
+ - rustup component add clippy
+script:
+ - cargo clippy
+ # if you want the build job to fail when encountering warnings, use
+ - cargo clippy -- -D warnings
+ # in order to also check tests and non-default crate features, use
+ - cargo clippy --all-targets --all-features -- -D warnings
+ - cargo test
+ # etc.
+```
+
+If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
+In this case you can try to conditionally install Clippy from the Git repo.
+
+```yaml
+language: rust
+rust:
+ - nightly
+before_script:
+ - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
+ # etc.
+```
+
+Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
+That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
+an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
+line. (You can swap `clippy::all` with the specific lint category you are targeting.)
+
+## Configuration
+
+Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
+value` mapping eg.
+
+```toml
+blacklisted-names = ["toto", "tata", "titi"]
+cognitive-complexity-threshold = 30
+```
+
+See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
+lints can be configured and the meaning of the variables.
+
+To deactivate the “for further information visit *lint-link*” message you can
+define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
+
+### Allowing/denying lints
+
+You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
+
+* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
+
+* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
+ `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
+ lints prone to false positives.
+
+* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
+
+* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
+
+Note: `deny` produces errors instead of warnings.
+
+If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra
+flags to Clippy during the run: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and
+`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you
+can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic`
+If you care only about a single lint, you can allow all others and then explicitly reenable
+the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...`
+
+## Contributing
+
+If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
+
+## License
+
+Copyright 2014-2020 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0 or the MIT license
+, at your
+option. Files in the project may not be
+copied, modified, or distributed except according to those terms.
diff --git a/src/tools/clippy/build.rs b/src/tools/clippy/build.rs
new file mode 100644
index 00000000000..018375dbada
--- /dev/null
+++ b/src/tools/clippy/build.rs
@@ -0,0 +1,19 @@
+fn main() {
+ // Forward the profile to the main compilation
+ println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap());
+ // Don't rebuild even if nothing changed
+ println!("cargo:rerun-if-changed=build.rs");
+ // forward git repo hashes we build at
+ println!(
+ "cargo:rustc-env=GIT_HASH={}",
+ rustc_tools_util::get_commit_hash().unwrap_or_default()
+ );
+ println!(
+ "cargo:rustc-env=COMMIT_DATE={}",
+ rustc_tools_util::get_commit_date().unwrap_or_default()
+ );
+ println!(
+ "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
+ rustc_tools_util::get_channel().unwrap_or_default()
+ );
+}
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
new file mode 100644
index 00000000000..c861efc8afb
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "clippy_dev"
+version = "0.0.1"
+authors = ["Philipp Hansch "]
+edition = "2018"
+
+[dependencies]
+bytecount = "0.6"
+clap = "2.33"
+itertools = "0.9"
+regex = "1"
+lazy_static = "1.0"
+shell-escape = "0.1"
+walkdir = "2"
+
+[features]
+deny-warnings = []
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
new file mode 100644
index 00000000000..6ae3f58c1f2
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -0,0 +1,175 @@
+use crate::clippy_project_root;
+use shell_escape::escape;
+use std::ffi::OsStr;
+use std::io;
+use std::path::Path;
+use std::process::{self, Command};
+use walkdir::WalkDir;
+
+#[derive(Debug)]
+pub enum CliError {
+ CommandFailed(String),
+ IoError(io::Error),
+ RustfmtNotInstalled,
+ WalkDirError(walkdir::Error),
+}
+
+impl From for CliError {
+ fn from(error: io::Error) -> Self {
+ Self::IoError(error)
+ }
+}
+
+impl From for CliError {
+ fn from(error: walkdir::Error) -> Self {
+ Self::WalkDirError(error)
+ }
+}
+
+struct FmtContext {
+ check: bool,
+ verbose: bool,
+}
+
+pub fn run(check: bool, verbose: bool) {
+ fn try_run(context: &FmtContext) -> Result {
+ let mut success = true;
+
+ let project_root = clippy_project_root();
+
+ rustfmt_test(context)?;
+
+ success &= cargo_fmt(context, project_root.as_path())?;
+ success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
+ success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
+
+ for entry in WalkDir::new(project_root.join("tests")) {
+ let entry = entry?;
+ let path = entry.path();
+
+ if path.extension() != Some("rs".as_ref())
+ || entry.file_name() == "ice-3891.rs"
+ // Avoid rustfmt bug rust-lang/rustfmt#1873
+ || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
+ {
+ continue;
+ }
+
+ success &= rustfmt(context, &path)?;
+ }
+
+ Ok(success)
+ }
+
+ fn output_err(err: CliError) {
+ match err {
+ CliError::CommandFailed(command) => {
+ eprintln!("error: A command failed! `{}`", command);
+ },
+ CliError::IoError(err) => {
+ eprintln!("error: {}", err);
+ },
+ CliError::RustfmtNotInstalled => {
+ eprintln!("error: rustfmt nightly is not installed.");
+ },
+ CliError::WalkDirError(err) => {
+ eprintln!("error: {}", err);
+ },
+ }
+ }
+
+ let context = FmtContext { check, verbose };
+ let result = try_run(&context);
+ let code = match result {
+ Ok(true) => 0,
+ Ok(false) => {
+ eprintln!();
+ eprintln!("Formatting check failed.");
+ eprintln!("Run `cargo dev fmt` to update formatting.");
+ 1
+ },
+ Err(err) => {
+ output_err(err);
+ 1
+ },
+ };
+ process::exit(code);
+}
+
+fn format_command(program: impl AsRef, dir: impl AsRef, args: &[impl AsRef]) -> String {
+ let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
+
+ format!(
+ "cd {} && {} {}",
+ escape(dir.as_ref().to_string_lossy()),
+ escape(program.as_ref().to_string_lossy()),
+ arg_display.join(" ")
+ )
+}
+
+fn exec(
+ context: &FmtContext,
+ program: impl AsRef,
+ dir: impl AsRef,
+ args: &[impl AsRef],
+) -> Result {
+ if context.verbose {
+ println!("{}", format_command(&program, &dir, args));
+ }
+
+ let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
+ let code = child.wait()?;
+ let success = code.success();
+
+ if !context.check && !success {
+ return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
+ }
+
+ Ok(success)
+}
+
+fn cargo_fmt(context: &FmtContext, path: &Path) -> Result {
+ let mut args = vec!["+nightly", "fmt", "--all"];
+ if context.check {
+ args.push("--");
+ args.push("--check");
+ }
+ let success = exec(context, "cargo", path, &args)?;
+
+ Ok(success)
+}
+
+fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
+ let program = "rustfmt";
+ let dir = std::env::current_dir()?;
+ let args = &["+nightly", "--version"];
+
+ if context.verbose {
+ println!("{}", format_command(&program, &dir, args));
+ }
+
+ let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
+
+ if output.status.success() {
+ Ok(())
+ } else if std::str::from_utf8(&output.stderr)
+ .unwrap_or("")
+ .starts_with("error: 'rustfmt' is not installed")
+ {
+ Err(CliError::RustfmtNotInstalled)
+ } else {
+ Err(CliError::CommandFailed(format_command(&program, &dir, args)))
+ }
+}
+
+fn rustfmt(context: &FmtContext, path: &Path) -> Result {
+ let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
+ if context.check {
+ args.push("--check".as_ref());
+ }
+ let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
+ if !success {
+ eprintln!("rustfmt failed on {}", path.display());
+ }
+ Ok(success)
+}
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
new file mode 100644
index 00000000000..6fdd282c684
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -0,0 +1,524 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+
+use itertools::Itertools;
+use lazy_static::lazy_static;
+use regex::Regex;
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use walkdir::WalkDir;
+
+pub mod fmt;
+pub mod new_lint;
+pub mod stderr_length_check;
+pub mod update_lints;
+
+lazy_static! {
+ static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new(
+ r#"(?x)
+ declare_clippy_lint!\s*[\{(]
+ (?:\s+///.*)*
+ \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s*
+ (?P[a-z_]+)\s*,\s*
+ "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
+ "#
+ )
+ .unwrap();
+ static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new(
+ r#"(?x)
+ declare_deprecated_lint!\s*[{(]\s*
+ (?:\s+///.*)*
+ \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s*
+ "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
+ "#
+ )
+ .unwrap();
+ static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap();
+}
+
+pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
+
+/// Lint data parsed from the Clippy source code.
+#[derive(Clone, PartialEq, Debug)]
+pub struct Lint {
+ pub name: String,
+ pub group: String,
+ pub desc: String,
+ pub deprecation: Option,
+ pub module: String,
+}
+
+impl Lint {
+ #[must_use]
+ pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
+ Self {
+ name: name.to_lowercase(),
+ group: group.to_string(),
+ desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
+ deprecation: deprecation.map(ToString::to_string),
+ module: module.to_string(),
+ }
+ }
+
+ /// Returns all non-deprecated lints and non-internal lints
+ #[must_use]
+ pub fn usable_lints(lints: &[Self]) -> Vec {
+ lints
+ .iter()
+ .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
+ .cloned()
+ .collect()
+ }
+
+ /// Returns all internal lints (not `internal_warn` lints)
+ #[must_use]
+ pub fn internal_lints(lints: &[Self]) -> Vec {
+ lints.iter().filter(|l| l.group == "internal").cloned().collect()
+ }
+
+ /// Returns all deprecated lints
+ #[must_use]
+ pub fn deprecated_lints(lints: &[Self]) -> Vec {
+ lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
+ }
+
+ /// Returns the lints in a `HashMap`, grouped by the different lint groups
+ #[must_use]
+ pub fn by_lint_group(lints: impl Iterator- ) -> HashMap> {
+ lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
+ }
+}
+
+/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
+#[must_use]
+pub fn gen_lint_group_list<'a>(lints: impl Iterator
- ) -> Vec {
+ lints
+ .map(|l| format!(" LintId::of(&{}::{}),", l.module, l.name.to_uppercase()))
+ .sorted()
+ .collect::>()
+}
+
+/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
+#[must_use]
+pub fn gen_modules_list<'a>(lints: impl Iterator
- ) -> Vec {
+ lints
+ .map(|l| &l.module)
+ .unique()
+ .map(|module| format!("mod {};", module))
+ .sorted()
+ .collect::>()
+}
+
+/// Generates the list of lint links at the bottom of the README
+#[must_use]
+pub fn gen_changelog_lint_list<'a>(lints: impl Iterator
- ) -> Vec {
+ lints
+ .sorted_by_key(|l| &l.name)
+ .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
+ .collect()
+}
+
+/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
+#[must_use]
+pub fn gen_deprecated<'a>(lints: impl Iterator
- ) -> Vec {
+ lints
+ .flat_map(|l| {
+ l.deprecation
+ .clone()
+ .map(|depr_text| {
+ vec![
+ " store.register_removed(".to_string(),
+ format!(" \"clippy::{}\",", l.name),
+ format!(" \"{}\",", depr_text),
+ " );".to_string(),
+ ]
+ })
+ .expect("only deprecated lints should be passed")
+ })
+ .collect::>()
+}
+
+#[must_use]
+pub fn gen_register_lint_list<'a>(lints: impl Iterator
- ) -> Vec {
+ let pre = " store.register_lints(&[".to_string();
+ let post = " ]);".to_string();
+ let mut inner = lints
+ .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
+ .sorted()
+ .collect::>();
+ inner.insert(0, pre);
+ inner.push(post);
+ inner
+}
+
+/// Gathers all files in `src/clippy_lints` and gathers all lints inside
+pub fn gather_all() -> impl Iterator
- {
+ lint_files().flat_map(|f| gather_from_file(&f))
+}
+
+fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator
- {
+ let content = fs::read_to_string(dir_entry.path()).unwrap();
+ let path = dir_entry.path();
+ let filename = path.file_stem().unwrap();
+ let path_buf = path.with_file_name(filename);
+ let mut rel_path = path_buf
+ .strip_prefix(clippy_project_root().join("clippy_lints/src"))
+ .expect("only files in `clippy_lints/src` should be looked at");
+ // If the lints are stored in mod.rs, we get the module name from
+ // the containing directory:
+ if filename == "mod" {
+ rel_path = rel_path.parent().unwrap();
+ }
+
+ let module = rel_path
+ .components()
+ .map(|c| c.as_os_str().to_str().unwrap())
+ .collect::>()
+ .join("::");
+
+ parse_contents(&content, &module)
+}
+
+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, module));
+ let deprecated = DEC_DEPRECATED_LINT_RE
+ .captures_iter(content)
+ .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
+ // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map
+ lints.chain(deprecated).collect::>().into_iter()
+}
+
+/// Collects all .rs files in the `clippy_lints/src` directory
+fn lint_files() -> impl Iterator
- {
+ // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
+ // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
+ let path = clippy_project_root().join("clippy_lints/src");
+ WalkDir::new(path)
+ .into_iter()
+ .filter_map(Result::ok)
+ .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+}
+
+/// Whether a file has had its text changed or not
+#[derive(PartialEq, Debug)]
+pub struct FileChange {
+ pub changed: bool,
+ pub new_lines: String,
+}
+
+/// Replaces a region in a file delimited by two lines matching regexes.
+///
+/// `path` is the relative path to the file on which you want to perform the replacement.
+///
+/// See `replace_region_in_text` for documentation of the other options.
+pub fn replace_region_in_file(
+ path: &Path,
+ start: &str,
+ end: &str,
+ replace_start: bool,
+ write_back: bool,
+ replacements: F,
+) -> FileChange
+where
+ F: FnOnce() -> Vec,
+{
+ let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
+ let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
+
+ if write_back {
+ if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
+ panic!("Cannot write to {}: {}", path.display(), e);
+ }
+ }
+ file_change
+}
+
+/// Replaces a region in a text delimited by two lines matching regexes.
+///
+/// * `text` is the input text on which you want to perform the replacement
+/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
+/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
+/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
+/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
+/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
+/// delimiter line is never replaced.
+/// * `replacements` is a closure that has to return a `Vec` which contains the new text.
+///
+/// If you want to perform the replacement on files instead of already parsed text,
+/// use `replace_region_in_file`.
+///
+/// # Example
+///
+/// ```
+/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
+/// let result =
+/// clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
+/// vec!["a different".to_string(), "text".to_string()]
+/// })
+/// .new_lines;
+/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
+/// ```
+pub fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
+where
+ F: FnOnce() -> Vec,
+{
+ let replace_it = replacements();
+ let mut in_old_region = false;
+ let mut found = false;
+ let mut new_lines = vec![];
+ let start = Regex::new(start).unwrap();
+ let end = Regex::new(end).unwrap();
+
+ for line in text.lines() {
+ if in_old_region {
+ if end.is_match(line) {
+ in_old_region = false;
+ new_lines.extend(replace_it.clone());
+ new_lines.push(line.to_string());
+ }
+ } else if start.is_match(line) {
+ if !replace_start {
+ new_lines.push(line.to_string());
+ }
+ in_old_region = true;
+ found = true;
+ } else {
+ new_lines.push(line.to_string());
+ }
+ }
+
+ if !found {
+ // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
+ // given text or file. Most likely this is an error on the programmer's side and the Regex
+ // is incorrect.
+ eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
+ std::process::exit(1);
+ }
+
+ let mut new_lines = new_lines.join("\n");
+ if text.ends_with('\n') {
+ new_lines.push('\n');
+ }
+ let changed = new_lines != text;
+ FileChange { changed, new_lines }
+}
+
+/// Returns the path to the Clippy project directory
+#[must_use]
+pub fn clippy_project_root() -> PathBuf {
+ let current_dir = std::env::current_dir().unwrap();
+ for path in current_dir.ancestors() {
+ let result = std::fs::read_to_string(path.join("Cargo.toml"));
+ if let Err(err) = &result {
+ if err.kind() == std::io::ErrorKind::NotFound {
+ continue;
+ }
+ }
+
+ let content = result.unwrap();
+ if content.contains("[package]\nname = \"clippy\"") {
+ return path.to_path_buf();
+ }
+ }
+ panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
+}
+
+#[test]
+fn test_parse_contents() {
+ let result: Vec = parse_contents(
+ r#"
+declare_clippy_lint! {
+ pub PTR_ARG,
+ style,
+ "really long \
+ text"
+}
+
+declare_clippy_lint!{
+ pub DOC_MARKDOWN,
+ pedantic,
+ "single line"
+}
+
+/// some doc comment
+declare_deprecated_lint! {
+ pub SHOULD_ASSERT_EQ,
+ "`assert!()` will be more flexible with RFC 2011"
+}
+ "#,
+ "module_name",
+ )
+ .collect();
+
+ let expected = vec![
+ Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
+ Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
+ Lint::new(
+ "should_assert_eq",
+ "Deprecated",
+ "`assert!()` will be more flexible with RFC 2011",
+ Some("`assert!()` will be more flexible with RFC 2011"),
+ "module_name",
+ ),
+ ];
+ assert_eq!(expected, result);
+}
+
+#[test]
+fn test_replace_region() {
+ let text = "\nabc\n123\n789\ndef\nghi";
+ let expected = FileChange {
+ changed: true,
+ new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
+ };
+ let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
+ vec!["hello world".to_string()]
+ });
+ assert_eq!(expected, result);
+}
+
+#[test]
+fn test_replace_region_with_start() {
+ let text = "\nabc\n123\n789\ndef\nghi";
+ let expected = FileChange {
+ changed: true,
+ new_lines: "\nhello world\ndef\nghi".to_string(),
+ };
+ let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
+ vec!["hello world".to_string()]
+ });
+ assert_eq!(expected, result);
+}
+
+#[test]
+fn test_replace_region_no_changes() {
+ let text = "123\n456\n789";
+ let expected = FileChange {
+ changed: false,
+ new_lines: "123\n456\n789".to_string(),
+ };
+ let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]);
+ assert_eq!(expected, result);
+}
+
+#[test]
+fn test_usable_lints() {
+ let lints = vec![
+ Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
+ Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
+ ];
+ let expected = vec![Lint::new(
+ "should_assert_eq2",
+ "Not Deprecated",
+ "abc",
+ None,
+ "module_name",
+ )];
+ assert_eq!(expected, Lint::usable_lints(&lints));
+}
+
+#[test]
+fn test_by_lint_group() {
+ let lints = vec![
+ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
+ Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ ];
+ let mut expected: HashMap> = HashMap::new();
+ expected.insert(
+ "group1".to_string(),
+ vec![
+ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
+ Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ ],
+ );
+ expected.insert(
+ "group2".to_string(),
+ vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
+ );
+ assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
+}
+
+#[test]
+fn test_gen_changelog_lint_list() {
+ let lints = vec![
+ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
+ ];
+ let expected = vec![
+ format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
+ format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
+ ];
+ assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
+}
+
+#[test]
+fn test_gen_deprecated() {
+ let lints = vec![
+ Lint::new(
+ "should_assert_eq",
+ "group1",
+ "abc",
+ Some("has been superseded by should_assert_eq2"),
+ "module_name",
+ ),
+ Lint::new(
+ "another_deprecated",
+ "group2",
+ "abc",
+ Some("will be removed"),
+ "module_name",
+ ),
+ ];
+ let expected: Vec = vec![
+ " store.register_removed(",
+ " \"clippy::should_assert_eq\",",
+ " \"has been superseded by should_assert_eq2\",",
+ " );",
+ " store.register_removed(",
+ " \"clippy::another_deprecated\",",
+ " \"will be removed\",",
+ " );",
+ ]
+ .into_iter()
+ .map(String::from)
+ .collect();
+ assert_eq!(expected, gen_deprecated(lints.iter()));
+}
+
+#[test]
+#[should_panic]
+fn test_gen_deprecated_fail() {
+ let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
+ let _ = gen_deprecated(lints.iter());
+}
+
+#[test]
+fn test_gen_modules_list() {
+ let lints = vec![
+ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
+ Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
+ ];
+ let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
+ assert_eq!(expected, gen_modules_list(lints.iter()));
+}
+
+#[test]
+fn test_gen_lint_group_list() {
+ let lints = vec![
+ Lint::new("abc", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
+ Lint::new("internal", "internal_style", "abc", None, "module_name"),
+ ];
+ let expected = vec![
+ " LintId::of(&module_name::ABC),".to_string(),
+ " LintId::of(&module_name::INTERNAL),".to_string(),
+ " LintId::of(&module_name::SHOULD_ASSERT_EQ),".to_string(),
+ ];
+ assert_eq!(expected, gen_lint_group_list(lints.iter()));
+}
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
new file mode 100644
index 00000000000..d99235f7c07
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -0,0 +1,120 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+
+use clap::{App, Arg, SubCommand};
+use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints};
+
+fn main() {
+ let matches = App::new("Clippy developer tooling")
+ .subcommand(
+ SubCommand::with_name("fmt")
+ .about("Run rustfmt on all projects and tests")
+ .arg(
+ Arg::with_name("check")
+ .long("check")
+ .help("Use the rustfmt --check option"),
+ )
+ .arg(
+ Arg::with_name("verbose")
+ .short("v")
+ .long("verbose")
+ .help("Echo commands run"),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("update_lints")
+ .about("Updates lint registration and information from the source code")
+ .long_about(
+ "Makes sure that:\n \
+ * the lint count in README.md is correct\n \
+ * the changelog contains markdown link references at the bottom\n \
+ * all lint groups include the correct lints\n \
+ * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
+ * all lints are registered in the lint store",
+ )
+ .arg(Arg::with_name("print-only").long("print-only").help(
+ "Print a table of lints to STDOUT. \
+ This does not include deprecated and internal lints. \
+ (Does not modify any files)",
+ ))
+ .arg(
+ Arg::with_name("check")
+ .long("check")
+ .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("new_lint")
+ .about("Create new lint and run `cargo dev update_lints`")
+ .arg(
+ Arg::with_name("pass")
+ .short("p")
+ .long("pass")
+ .help("Specify whether the lint runs during the early or late pass")
+ .takes_value(true)
+ .possible_values(&["early", "late"])
+ .required(true),
+ )
+ .arg(
+ Arg::with_name("name")
+ .short("n")
+ .long("name")
+ .help("Name of the new lint in snake case, ex: fn_too_long")
+ .takes_value(true)
+ .required(true),
+ )
+ .arg(
+ Arg::with_name("category")
+ .short("c")
+ .long("category")
+ .help("What category the lint belongs to")
+ .default_value("nursery")
+ .possible_values(&[
+ "style",
+ "correctness",
+ "complexity",
+ "perf",
+ "pedantic",
+ "restriction",
+ "cargo",
+ "nursery",
+ "internal",
+ "internal_warn",
+ ])
+ .takes_value(true),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("limit_stderr_length")
+ .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
+ )
+ .get_matches();
+
+ match matches.subcommand() {
+ ("fmt", Some(matches)) => {
+ fmt::run(matches.is_present("check"), matches.is_present("verbose"));
+ },
+ ("update_lints", Some(matches)) => {
+ if matches.is_present("print-only") {
+ update_lints::print_lints();
+ } else if matches.is_present("check") {
+ update_lints::run(update_lints::UpdateMode::Check);
+ } else {
+ update_lints::run(update_lints::UpdateMode::Change);
+ }
+ },
+ ("new_lint", Some(matches)) => {
+ match new_lint::create(
+ matches.value_of("pass"),
+ matches.value_of("name"),
+ matches.value_of("category"),
+ ) {
+ Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
+ Err(e) => eprintln!("Unable to create lint: {}", e),
+ }
+ },
+ ("limit_stderr_length", _) => {
+ stderr_length_check::check();
+ },
+ _ => {},
+ }
+}
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
new file mode 100644
index 00000000000..44b2a5383d2
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -0,0 +1,177 @@
+use crate::clippy_project_root;
+use std::fs::{File, OpenOptions};
+use std::io;
+use std::io::prelude::*;
+use std::io::ErrorKind;
+use std::path::Path;
+
+/// Creates files required to implement and test a new lint and runs `update_lints`.
+///
+/// # Errors
+///
+/// This function errors, if the files couldn't be created
+pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
+ let pass = pass.expect("`pass` argument is validated by clap");
+ let lint_name = lint_name.expect("`name` argument is validated by clap");
+ let category = category.expect("`category` argument is validated by clap");
+
+ match open_files(lint_name) {
+ Ok((mut test_file, mut lint_file)) => {
+ let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
+ "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
+ "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
+ _ => {
+ unreachable!("`pass_type` should only ever be `early` or `late`!");
+ },
+ };
+
+ let camel_case_name = to_camel_case(lint_name);
+
+ if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
+ return Err(io::Error::new(
+ ErrorKind::Other,
+ format!("Could not write to test file: {}", e),
+ ));
+ };
+
+ if let Err(e) = lint_file.write_all(
+ get_lint_file_contents(
+ pass_type,
+ pass_lifetimes,
+ lint_name,
+ &camel_case_name,
+ category,
+ pass_import,
+ context_import,
+ )
+ .as_bytes(),
+ ) {
+ return Err(io::Error::new(
+ ErrorKind::Other,
+ format!("Could not write to lint file: {}", e),
+ ));
+ }
+ Ok(())
+ },
+ Err(e) => Err(io::Error::new(
+ ErrorKind::Other,
+ format!("Unable to create lint: {}", e),
+ )),
+ }
+}
+
+fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
+ let project_root = clippy_project_root();
+
+ let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
+ let lint_file_path = project_root
+ .join("clippy_lints")
+ .join("src")
+ .join(format!("{}.rs", lint_name));
+
+ if Path::new(&test_file_path).exists() {
+ return Err(io::Error::new(
+ ErrorKind::AlreadyExists,
+ format!("test file {:?} already exists", test_file_path),
+ ));
+ }
+ if Path::new(&lint_file_path).exists() {
+ return Err(io::Error::new(
+ ErrorKind::AlreadyExists,
+ format!("lint file {:?} already exists", lint_file_path),
+ ));
+ }
+
+ let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
+ let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
+
+ Ok((test_file, lint_file))
+}
+
+fn to_camel_case(name: &str) -> String {
+ name.split('_')
+ .map(|s| {
+ if s.is_empty() {
+ String::from("")
+ } else {
+ [&s[0..1].to_uppercase(), &s[1..]].concat()
+ }
+ })
+ .collect()
+}
+
+fn get_test_file_contents(lint_name: &str) -> String {
+ format!(
+ "#![warn(clippy::{})]
+
+fn main() {{
+ // test code goes here
+}}
+",
+ lint_name
+ )
+}
+
+fn get_lint_file_contents(
+ pass_type: &str,
+ pass_lifetimes: &str,
+ lint_name: &str,
+ camel_case_name: &str,
+ category: &str,
+ pass_import: &str,
+ context_import: &str,
+) -> String {
+ format!(
+ "use rustc_lint::{{{type}, {context_import}}};
+use rustc_session::{{declare_lint_pass, declare_tool_lint}};
+{pass_import}
+
+declare_clippy_lint! {{
+ /// **What it does:**
+ ///
+ /// **Why is this bad?**
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // example code which does not raise clippy warning
+ /// ```
+ pub {name_upper},
+ {category},
+ \"default lint description\"
+}}
+
+declare_lint_pass!({name_camel} => [{name_upper}]);
+
+impl {type}{lifetimes} for {name_camel} {{}}
+",
+ type=pass_type,
+ lifetimes=pass_lifetimes,
+ name_upper=lint_name.to_uppercase(),
+ name_camel=camel_case_name,
+ category=category,
+ pass_import=pass_import,
+ context_import=context_import
+ )
+}
+
+#[test]
+fn test_camel_case() {
+ let s = "a_lint";
+ let s2 = to_camel_case(s);
+ assert_eq!(s2, "ALint");
+
+ let name = "a_really_long_new_lint";
+ let name2 = to_camel_case(name);
+ assert_eq!(name2, "AReallyLongNewLint");
+
+ let name3 = "lint__name";
+ let name4 = to_camel_case(name3);
+ assert_eq!(name4, "LintName");
+}
diff --git a/src/tools/clippy/clippy_dev/src/stderr_length_check.rs b/src/tools/clippy/clippy_dev/src/stderr_length_check.rs
new file mode 100644
index 00000000000..e02b6f7da5f
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/stderr_length_check.rs
@@ -0,0 +1,51 @@
+use crate::clippy_project_root;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use walkdir::WalkDir;
+
+// The maximum length allowed for stderr files.
+//
+// We limit this because small files are easier to deal with than bigger files.
+const LENGTH_LIMIT: usize = 200;
+
+pub fn check() {
+ let exceeding_files: Vec<_> = exceeding_stderr_files();
+
+ if !exceeding_files.is_empty() {
+ eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
+ for (path, count) in exceeding_files {
+ println!("{}: {}", path.display(), count);
+ }
+ std::process::exit(1);
+ }
+}
+
+fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> {
+ // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
+ WalkDir::new(clippy_project_root().join("tests/ui"))
+ .into_iter()
+ .filter_map(Result::ok)
+ .filter(|f| !f.file_type().is_dir())
+ .filter_map(|e| {
+ let p = e.into_path();
+ let count = count_linenumbers(&p);
+ if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT {
+ Some((p, count))
+ } else {
+ None
+ }
+ })
+ .collect()
+}
+
+#[must_use]
+fn count_linenumbers(filepath: &Path) -> usize {
+ match fs::read(filepath) {
+ Ok(content) => bytecount::count(&content, b'\n'),
+ Err(e) => {
+ eprintln!("Failed to read file: {}", e);
+ 0
+ },
+ }
+}
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
new file mode 100644
index 00000000000..a9a70929942
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -0,0 +1,162 @@
+use crate::{
+ gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list,
+ replace_region_in_file, Lint, DOCS_LINK,
+};
+use std::path::Path;
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum UpdateMode {
+ Check,
+ Change,
+}
+
+#[allow(clippy::too_many_lines)]
+pub fn run(update_mode: UpdateMode) {
+ let lint_list: Vec = gather_all().collect();
+
+ let internal_lints = Lint::internal_lints(&lint_list);
+ let deprecated_lints = Lint::deprecated_lints(&lint_list);
+ let usable_lints = Lint::usable_lints(&lint_list);
+ let mut sorted_usable_lints = usable_lints.clone();
+ sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
+
+ let usable_lint_count = round_to_fifty(usable_lints.len());
+
+ let mut file_change = replace_region_in_file(
+ Path::new("src/lintlist/mod.rs"),
+ "begin lint list",
+ "end lint list",
+ false,
+ update_mode == UpdateMode::Change,
+ || {
+ format!("pub static ref ALL_LINTS: Vec = vec!{:#?};", sorted_usable_lints)
+ .lines()
+ .map(ToString::to_string)
+ .collect::>()
+ },
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("README.md"),
+ &format!(
+ r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
+ DOCS_LINK
+ ),
+ "",
+ true,
+ update_mode == UpdateMode::Change,
+ || {
+ vec![format!(
+ "[There are over {} lints included in this crate!]({})",
+ usable_lint_count, DOCS_LINK
+ )]
+ },
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("CHANGELOG.md"),
+ "",
+ "",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ "begin deprecated lints",
+ "end deprecated lints",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_deprecated(deprecated_lints.iter()),
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ "begin register lints",
+ "end register lints",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())),
+ )
+ .changed;
+
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ "begin lints modules",
+ "end lints modules",
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_modules_list(usable_lints.iter()),
+ )
+ .changed;
+
+ // Generate lists of lints in the clippy::all lint group
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ r#"store.register_group\(true, "clippy::all""#,
+ r#"\]\);"#,
+ false,
+ update_mode == UpdateMode::Change,
+ || {
+ // clippy::all should only include the following lint groups:
+ let all_group_lints = usable_lints.iter().filter(|l| {
+ l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
+ });
+
+ gen_lint_group_list(all_group_lints)
+ },
+ )
+ .changed;
+
+ // Generate the list of lints for all other lint groups
+ for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
+ file_change |= replace_region_in_file(
+ Path::new("clippy_lints/src/lib.rs"),
+ &format!("store.register_group\\(true, \"clippy::{}\"", lint_group),
+ r#"\]\);"#,
+ false,
+ update_mode == UpdateMode::Change,
+ || gen_lint_group_list(lints.iter()),
+ )
+ .changed;
+ }
+
+ if update_mode == UpdateMode::Check && file_change {
+ println!(
+ "Not all lints defined properly. \
+ Please run `cargo dev update_lints` to make sure all lints are defined properly."
+ );
+ std::process::exit(1);
+ }
+}
+
+pub fn print_lints() {
+ let lint_list: Vec = gather_all().collect();
+ let usable_lints = Lint::usable_lints(&lint_list);
+ let usable_lint_count = usable_lints.len();
+ let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
+
+ for (lint_group, mut lints) in grouped_by_lint_group {
+ if lint_group == "Deprecated" {
+ continue;
+ }
+ println!("\n## {}", lint_group);
+
+ lints.sort_by_key(|l| l.name.clone());
+
+ for lint in lints {
+ println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
+ }
+ }
+
+ println!("there are {} lints", usable_lint_count);
+}
+
+fn round_to_fifty(count: usize) -> usize {
+ count / 50 * 50
+}
diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml
new file mode 100644
index 00000000000..7b11795fafd
--- /dev/null
+++ b/src/tools/clippy/clippy_dummy/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "clippy_dummy" # rename to clippy before publishing
+version = "0.0.303"
+authors = ["Manish Goregaokar "]
+edition = "2018"
+readme = "crates-readme.md"
+description = "A bunch of helpful lints to avoid common pitfalls in Rust."
+build = 'build.rs'
+
+repository = "https://github.com/rust-lang/rust-clippy"
+
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+categories = ["development-tools", "development-tools::cargo-plugins"]
+
+[build-dependencies]
+term = "0.6"
diff --git a/src/tools/clippy/clippy_dummy/PUBLISH.md b/src/tools/clippy/clippy_dummy/PUBLISH.md
new file mode 100644
index 00000000000..8e420ec959a
--- /dev/null
+++ b/src/tools/clippy/clippy_dummy/PUBLISH.md
@@ -0,0 +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.
+
+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/src/tools/clippy/clippy_dummy/build.rs b/src/tools/clippy/clippy_dummy/build.rs
new file mode 100644
index 00000000000..21af4f8244f
--- /dev/null
+++ b/src/tools/clippy/clippy_dummy/build.rs
@@ -0,0 +1,42 @@
+use term::color::{GREEN, RED, WHITE};
+use term::{Attr, Error, Result};
+
+fn main() {
+ if foo().is_err() {
+ eprintln!(
+ "error: Clippy is no longer available via crates.io\n\n\
+ help: please run `rustup component add clippy` instead"
+ );
+ }
+ std::process::exit(1);
+}
+
+fn foo() -> Result<()> {
+ let mut t = term::stderr().ok_or(Error::NotSupported)?;
+
+ t.attr(Attr::Bold)?;
+ t.fg(RED)?;
+ write!(t, "\nerror: ")?;
+
+ t.reset()?;
+ t.fg(WHITE)?;
+ writeln!(t, "Clippy is no longer available via crates.io\n")?;
+
+ t.attr(Attr::Bold)?;
+ t.fg(GREEN)?;
+ write!(t, "help: ")?;
+
+ t.reset()?;
+ t.fg(WHITE)?;
+ write!(t, "please run `")?;
+
+ t.attr(Attr::Bold)?;
+ write!(t, "rustup component add clippy")?;
+
+ t.reset()?;
+ t.fg(WHITE)?;
+ writeln!(t, "` instead")?;
+
+ t.reset()?;
+ Ok(())
+}
diff --git a/src/tools/clippy/clippy_dummy/crates-readme.md b/src/tools/clippy/clippy_dummy/crates-readme.md
new file mode 100644
index 00000000000..0decae8b910
--- /dev/null
+++ b/src/tools/clippy/clippy_dummy/crates-readme.md
@@ -0,0 +1,9 @@
+Installing clippy via crates.io is deprecated. Please use the following:
+
+```terminal
+rustup component add clippy
+```
+
+on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
+
+See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information
diff --git a/src/tools/clippy/clippy_dummy/src/main.rs b/src/tools/clippy/clippy_dummy/src/main.rs
new file mode 100644
index 00000000000..a118834f1fd
--- /dev/null
+++ b/src/tools/clippy/clippy_dummy/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ panic!("This shouldn't even compile")
+}
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
new file mode 100644
index 00000000000..1c0be727834
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "clippy_lints"
+# begin automatic update
+version = "0.0.212"
+# end automatic update
+authors = [
+ "Manish Goregaokar ",
+ "Andre Bogus ",
+ "Georg Brandl ",
+ "Martin Carton "
+]
+description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+edition = "2018"
+
+[dependencies]
+cargo_metadata = "0.9.0"
+if_chain = "1.0.0"
+itertools = "0.9"
+lazy_static = "1.0.2"
+pulldown-cmark = { version = "0.7", default-features = false }
+quine-mc_cluskey = "0.2.2"
+regex-syntax = "0.6"
+serde = { version = "1.0", features = ["derive"] }
+smallvec = { version = "1", features = ["union"] }
+toml = "0.5.3"
+unicode-normalization = "0.1"
+semver = "0.9.0"
+# NOTE: cargo requires serde feat in its url dep
+# see
+url = { version = "2.1.0", features = ["serde"] }
+
+[features]
+deny-warnings = []
diff --git a/src/tools/clippy/clippy_lints/README.md b/src/tools/clippy/clippy_lints/README.md
new file mode 100644
index 00000000000..513583b7e34
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/README.md
@@ -0,0 +1 @@
+This crate contains Clippy lints. For the main crate, check [GitHub](https://github.com/rust-lang/rust-clippy).
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
new file mode 100644
index 00000000000..7e6e2c7eaeb
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -0,0 +1,117 @@
+use crate::utils::span_lint;
+use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol;
+use std::f64::consts as f64;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for floating point literals that approximate
+ /// constants which are defined in
+ /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
+ /// or
+ /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
+ /// respectively, suggesting to use the predefined constant.
+ ///
+ /// **Why is this bad?** Usually, the definition in the standard library is more
+ /// precise than what people come up with. If you find that your definition is
+ /// actually more precise, please [file a Rust
+ /// issue](https://github.com/rust-lang/rust/issues).
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let x = 3.14;
+ /// let y = 1_f64 / x;
+ /// ```
+ /// Use predefined constants instead:
+ /// ```rust
+ /// let x = std::f32::consts::PI;
+ /// let y = std::f64::consts::FRAC_1_PI;
+ /// ```
+ pub APPROX_CONSTANT,
+ correctness,
+ "the approximate of a known float constant (in `std::fXX::consts`)"
+}
+
+// Tuples are of the form (constant, name, min_digits)
+const KNOWN_CONSTS: [(f64, &str, usize); 18] = [
+ (f64::E, "E", 4),
+ (f64::FRAC_1_PI, "FRAC_1_PI", 4),
+ (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
+ (f64::FRAC_2_PI, "FRAC_2_PI", 5),
+ (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
+ (f64::FRAC_PI_2, "FRAC_PI_2", 5),
+ (f64::FRAC_PI_3, "FRAC_PI_3", 5),
+ (f64::FRAC_PI_4, "FRAC_PI_4", 5),
+ (f64::FRAC_PI_6, "FRAC_PI_6", 5),
+ (f64::FRAC_PI_8, "FRAC_PI_8", 5),
+ (f64::LN_10, "LN_10", 5),
+ (f64::LN_2, "LN_2", 5),
+ (f64::LOG10_E, "LOG10_E", 5),
+ (f64::LOG2_E, "LOG2_E", 5),
+ (f64::LOG2_10, "LOG2_10", 5),
+ (f64::LOG10_2, "LOG10_2", 5),
+ (f64::PI, "PI", 3),
+ (f64::SQRT_2, "SQRT_2", 5),
+];
+
+declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Lit(lit) = &e.kind {
+ check_lit(cx, &lit.node, e);
+ }
+ }
+}
+
+fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr<'_>) {
+ match *lit {
+ LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
+ FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
+ FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
+ },
+ LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
+ _ => (),
+ }
+}
+
+fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
+ let s = s.as_str();
+ if s.parse::().is_ok() {
+ for &(constant, name, min_digits) in &KNOWN_CONSTS {
+ if is_approx_const(constant, &s, min_digits) {
+ span_lint(
+ cx,
+ APPROX_CONSTANT,
+ e.span,
+ &format!(
+ "approximate value of `{}::consts::{}` found. \
+ Consider using it directly",
+ module, &name
+ ),
+ );
+ return;
+ }
+ }
+ }
+}
+
+/// Returns `false` if the number of significant figures in `value` are
+/// less than `min_digits`; otherwise, returns true if `value` is equal
+/// to `constant`, rounded to the number of digits present in `value`.
+#[must_use]
+fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
+ if value.len() <= min_digits {
+ false
+ } else if constant.to_string().starts_with(value) {
+ // The value is a truncated constant
+ true
+ } else {
+ let round_const = format!("{:.*}", value.len() - 2, constant);
+ value == round_const
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/arithmetic.rs b/src/tools/clippy/clippy_lints/src/arithmetic.rs
new file mode 100644
index 00000000000..6cbe10a5352
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/arithmetic.rs
@@ -0,0 +1,149 @@
+use crate::consts::constant_simple;
+use crate::utils::span_lint;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for integer arithmetic operations which could overflow or panic.
+ ///
+ /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
+ /// of overflowing according to the [Rust
+ /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
+ /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
+ /// attempted.
+ ///
+ /// **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in
+ /// release mode. Division by zero will cause a panic in either mode. In some applications one
+ /// wants explicitly checked, wrapping or saturating arithmetic.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let a = 0;
+ /// a + 1;
+ /// ```
+ pub INTEGER_ARITHMETIC,
+ restriction,
+ "any integer arithmetic expression which could overflow or panic"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for float arithmetic.
+ ///
+ /// **Why is this bad?** For some embedded systems or kernel development, it
+ /// can be useful to rule out floating-point numbers.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let a = 0.0;
+ /// a + 1.0;
+ /// ```
+ pub FLOAT_ARITHMETIC,
+ restriction,
+ "any floating-point arithmetic statement"
+}
+
+#[derive(Copy, Clone, Default)]
+pub struct Arithmetic {
+ expr_span: Option,
+ /// This field is used to check whether expressions are constants, such as in enum discriminants
+ /// and consts
+ const_span: Option,
+}
+
+impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if self.expr_span.is_some() {
+ return;
+ }
+
+ if let Some(span) = self.const_span {
+ if span.contains(expr.span) {
+ return;
+ }
+ }
+ match &expr.kind {
+ hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
+ match op.node {
+ hir::BinOpKind::And
+ | hir::BinOpKind::Or
+ | hir::BinOpKind::BitAnd
+ | hir::BinOpKind::BitOr
+ | hir::BinOpKind::BitXor
+ | hir::BinOpKind::Eq
+ | hir::BinOpKind::Lt
+ | hir::BinOpKind::Le
+ | hir::BinOpKind::Ne
+ | hir::BinOpKind::Ge
+ | hir::BinOpKind::Gt => return,
+ _ => (),
+ }
+
+ let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
+ if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
+ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+ self.expr_span = Some(expr.span);
+ } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
+ span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+ self.expr_span = Some(expr.span);
+ }
+ },
+ hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
+ let ty = cx.tables.expr_ty(arg);
+ if constant_simple(cx, cx.tables, expr).is_none() {
+ if ty.is_integral() {
+ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+ self.expr_span = Some(expr.span);
+ } else if ty.is_floating_point() {
+ span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+ self.expr_span = Some(expr.span);
+ }
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if Some(expr.span) == self.expr_span {
+ self.expr_span = None;
+ }
+ }
+
+ fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
+ let body_owner = cx.tcx.hir().body_owner(body.id());
+
+ match cx.tcx.hir().body_owner_kind(body_owner) {
+ hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
+ let body_span = cx.tcx.hir().span(body_owner);
+
+ if let Some(span) = self.const_span {
+ if span.contains(body_span) {
+ return;
+ }
+ }
+ self.const_span = Some(body_span);
+ },
+ hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
+ }
+ }
+
+ fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
+ let body_owner = cx.tcx.hir().body_owner(body.id());
+ let body_span = cx.tcx.hir().span(body_owner);
+
+ if let Some(span) = self.const_span {
+ if span.contains(body_span) {
+ return;
+ }
+ }
+ self.const_span = None;
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
new file mode 100644
index 00000000000..0c8efd75514
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -0,0 +1,58 @@
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+use crate::utils::span_lint_and_help;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of `as` conversions.
+ ///
+ /// **Why is this bad?** `as` conversions will perform many kinds of
+ /// conversions, including silently lossy conversions and dangerous coercions.
+ /// There are cases when it makes sense to use `as`, so the lint is
+ /// Allow by default.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// let a: u32;
+ /// ...
+ /// f(a as u16);
+ /// ```
+ ///
+ /// Usually better represents the semantics you expect:
+ /// ```rust,ignore
+ /// f(a.try_into()?);
+ /// ```
+ /// or
+ /// ```rust,ignore
+ /// f(a.try_into().expect("Unexpected u16 overflow in f"));
+ /// ```
+ ///
+ pub AS_CONVERSIONS,
+ restriction,
+ "using a potentially dangerous silent `as` conversion"
+}
+
+declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
+
+impl EarlyLintPass for AsConversions {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ if let ExprKind::Cast(_, _) = expr.kind {
+ span_lint_and_help(
+ cx,
+ AS_CONVERSIONS,
+ expr.span,
+ "using a potentially dangerous silent `as` conversion",
+ None,
+ "consider using a safe wrapper for this conversion",
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
new file mode 100644
index 00000000000..f8a8fdcd3aa
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -0,0 +1,152 @@
+use crate::consts::{constant, Constant};
+use crate::utils::paths;
+use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, snippet_opt, span_lint_and_help};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
+ ///
+ /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
+ /// `panic!()` or `unreachable!()`
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// assert!(false)
+ /// assert!(true)
+ /// const B: bool = false;
+ /// assert!(B)
+ /// ```
+ pub ASSERTIONS_ON_CONSTANTS,
+ style,
+ "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
+}
+
+declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
+ let lint_true = |is_debug: bool| {
+ span_lint_and_help(
+ cx,
+ ASSERTIONS_ON_CONSTANTS,
+ e.span,
+ if is_debug {
+ "`debug_assert!(true)` will be optimized out by the compiler"
+ } else {
+ "`assert!(true)` will be optimized out by the compiler"
+ },
+ None,
+ "remove it",
+ );
+ };
+ let lint_false_without_message = || {
+ span_lint_and_help(
+ cx,
+ ASSERTIONS_ON_CONSTANTS,
+ e.span,
+ "`assert!(false)` should probably be replaced",
+ None,
+ "use `panic!()` or `unreachable!()`",
+ );
+ };
+ let lint_false_with_message = |panic_message: String| {
+ span_lint_and_help(
+ cx,
+ ASSERTIONS_ON_CONSTANTS,
+ e.span,
+ &format!("`assert!(false, {})` should probably be replaced", panic_message),
+ None,
+ &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
+ )
+ };
+
+ if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
+ if debug_assert_span.from_expansion() {
+ return;
+ }
+ if_chain! {
+ if let ExprKind::Unary(_, ref lit) = e.kind;
+ if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit);
+ if is_true;
+ then {
+ lint_true(true);
+ }
+ };
+ } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
+ if assert_span.from_expansion() {
+ return;
+ }
+ if let Some(assert_match) = match_assert_with_message(&cx, e) {
+ match assert_match {
+ // matched assert but not message
+ AssertKind::WithoutMessage(false) => lint_false_without_message(),
+ AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
+ AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
+ };
+ }
+ }
+ }
+}
+
+/// Result of calling `match_assert_with_message`.
+enum AssertKind {
+ WithMessage(String, bool),
+ WithoutMessage(bool),
+}
+
+/// Check if the expression matches
+///
+/// ```rust,ignore
+/// match { let _t = !c; _t } {
+/// true => {
+/// {
+/// ::std::rt::begin_panic(message, _)
+/// }
+/// }
+/// _ => { }
+/// };
+/// ```
+///
+/// where `message` is any expression and `c` is a constant bool.
+fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option {
+ if_chain! {
+ if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
+ // matches { let _t = expr; _t }
+ if let ExprKind::DropTemps(ref expr) = expr.kind;
+ if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
+ // bind the first argument of the `assert!` macro
+ if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr);
+ // arm 1 pattern
+ if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ // arm 1 block
+ if let ExprKind::Block(ref block, _) = arms[0].body.kind;
+ if block.stmts.is_empty();
+ if let Some(block_expr) = &block.expr;
+ if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
+ if let Some(begin_panic_call) = &inner_block.expr;
+ // function call
+ if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
+ if args.len() == 1;
+ // bind the second argument of the `assert!` macro if it exists
+ if let panic_message = snippet_opt(cx, args[0].span);
+ // second argument of begin_panic is irrelevant
+ // as is the second match arm
+ then {
+ // an empty message occurs when it was generated by the macro
+ // (and not passed by the user)
+ return panic_message
+ .filter(|msg| !msg.is_empty())
+ .map(|msg| AssertKind::WithMessage(msg, is_true))
+ .or(Some(AssertKind::WithoutMessage(is_true)));
+ }
+ }
+ None
+}
diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs
new file mode 100644
index 00000000000..05e2650d0b7
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs
@@ -0,0 +1,261 @@
+use crate::utils::{
+ get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
+};
+use crate::utils::{higher, sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
+ /// patterns.
+ ///
+ /// **Why is this bad?** These can be written as the shorter `a op= b`.
+ ///
+ /// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
+ /// implementations that differ from the regular `Op` impl.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let mut a = 5;
+ /// let b = 0;
+ /// // ...
+ /// a = a + b;
+ /// ```
+ pub ASSIGN_OP_PATTERN,
+ style,
+ "assigning the result of an operation on a variable to that same variable"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
+ ///
+ /// **Why is this bad?** Most likely these are bugs where one meant to write `a
+ /// op= b`.
+ ///
+ /// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
+ /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
+ /// If `a op= a op b` is really the correct behaviour it should be
+ /// written as `a = a op a op b` as it's less confusing.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let mut a = 5;
+ /// let b = 2;
+ /// // ...
+ /// a += a + b;
+ /// ```
+ pub MISREFACTORED_ASSIGN_OP,
+ complexity,
+ "having a variable on both sides of an assign op"
+}
+
+declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
+ #[allow(clippy::too_many_lines)]
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+ match &expr.kind {
+ hir::ExprKind::AssignOp(op, lhs, rhs) => {
+ if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
+ if op.node != binop.node {
+ return;
+ }
+ // lhs op= l op r
+ if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
+ lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
+ }
+ // lhs op= l commutative_op r
+ if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
+ lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
+ }
+ }
+ },
+ hir::ExprKind::Assign(assignee, e, _) => {
+ if let hir::ExprKind::Binary(op, l, r) = &e.kind {
+ let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
+ let ty = cx.tables.expr_ty(assignee);
+ let rty = cx.tables.expr_ty(rhs);
+ macro_rules! ops {
+ ($op:expr,
+ $cx:expr,
+ $ty:expr,
+ $rty:expr,
+ $($trait_name:ident),+) => {
+ match $op {
+ $(hir::BinOpKind::$trait_name => {
+ let [krate, module] = crate::utils::paths::OPS_MODULE;
+ let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")];
+ let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
+ trait_id
+ } else {
+ return; // useless if the trait doesn't exist
+ };
+ // check that we are not inside an `impl AssignOp` of this exact operation
+ let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
+ if_chain! {
+ if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
+ if trait_ref.path.res.def_id() == trait_id;
+ then { return; }
+ }
+ implements_trait($cx, $ty, trait_id, &[$rty])
+ },)*
+ _ => false,
+ }
+ }
+ }
+ if ops!(
+ op.node,
+ cx,
+ ty,
+ rty.into(),
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Rem,
+ And,
+ Or,
+ BitAnd,
+ BitOr,
+ BitXor,
+ Shr,
+ Shl
+ ) {
+ span_lint_and_then(
+ cx,
+ ASSIGN_OP_PATTERN,
+ expr.span,
+ "manual implementation of an assign operation",
+ |diag| {
+ if let (Some(snip_a), Some(snip_r)) =
+ (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
+ {
+ diag.span_suggestion(
+ expr.span,
+ "replace it with",
+ format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ );
+ }
+ };
+
+ let mut visitor = ExprVisitor {
+ assignee,
+ counter: 0,
+ cx,
+ };
+
+ walk_expr(&mut visitor, e);
+
+ if visitor.counter == 1 {
+ // a = a op b
+ if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
+ lint(assignee, r);
+ }
+ // a = b commutative_op a
+ // Limited to primitive type as these ops are know to be commutative
+ if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
+ && cx.tables.expr_ty(assignee).is_primitive_ty()
+ {
+ match op.node {
+ hir::BinOpKind::Add
+ | hir::BinOpKind::Mul
+ | hir::BinOpKind::And
+ | hir::BinOpKind::Or
+ | hir::BinOpKind::BitXor
+ | hir::BinOpKind::BitAnd
+ | hir::BinOpKind::BitOr => {
+ lint(assignee, l);
+ },
+ _ => {},
+ }
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+}
+
+fn lint_misrefactored_assign_op(
+ cx: &LateContext<'_, '_>,
+ expr: &hir::Expr<'_>,
+ op: hir::BinOp,
+ rhs: &hir::Expr<'_>,
+ assignee: &hir::Expr<'_>,
+ rhs_other: &hir::Expr<'_>,
+) {
+ span_lint_and_then(
+ cx,
+ MISREFACTORED_ASSIGN_OP,
+ expr.span,
+ "variable appears on both sides of an assignment operation",
+ |diag| {
+ if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+ let a = &sugg::Sugg::hir(cx, assignee, "..");
+ let r = &sugg::Sugg::hir(cx, rhs, "..");
+ let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
+ diag.span_suggestion(
+ expr.span,
+ &format!(
+ "Did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
+ snip_a,
+ snip_a,
+ op.node.as_str(),
+ snip_r,
+ long
+ ),
+ format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
+ Applicability::MaybeIncorrect,
+ );
+ diag.span_suggestion(
+ expr.span,
+ "or",
+ long,
+ Applicability::MaybeIncorrect, // snippet
+ );
+ }
+ },
+ );
+}
+
+#[must_use]
+fn is_commutative(op: hir::BinOpKind) -> bool {
+ use rustc_hir::BinOpKind::{
+ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
+ };
+ match op {
+ Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
+ Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
+ }
+}
+
+struct ExprVisitor<'a, 'tcx> {
+ assignee: &'a hir::Expr<'a>,
+ counter: u8,
+ cx: &'a LateContext<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
+ self.counter += 1;
+ }
+
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap {
+ NestedVisitorMap::None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs
new file mode 100644
index 00000000000..73b4cef4725
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs
@@ -0,0 +1,135 @@
+use crate::utils::{match_def_path, span_lint_and_help};
+use if_chain::if_chain;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of invalid atomic
+ /// ordering in atomic loads/stores and memory fences.
+ ///
+ /// **Why is this bad?** Using an invalid atomic ordering
+ /// will cause a panic at run-time.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,no_run
+ /// # use std::sync::atomic::{self, AtomicBool, Ordering};
+ ///
+ /// let x = AtomicBool::new(true);
+ ///
+ /// let _ = x.load(Ordering::Release);
+ /// let _ = x.load(Ordering::AcqRel);
+ ///
+ /// x.store(false, Ordering::Acquire);
+ /// x.store(false, Ordering::AcqRel);
+ ///
+ /// atomic::fence(Ordering::Relaxed);
+ /// atomic::compiler_fence(Ordering::Relaxed);
+ /// ```
+ pub INVALID_ATOMIC_ORDERING,
+ correctness,
+ "usage of invalid atomic ordering in atomic loads/stores and memory fences"
+}
+
+declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
+
+const ATOMIC_TYPES: [&str; 12] = [
+ "AtomicBool",
+ "AtomicI8",
+ "AtomicI16",
+ "AtomicI32",
+ "AtomicI64",
+ "AtomicIsize",
+ "AtomicPtr",
+ "AtomicU8",
+ "AtomicU16",
+ "AtomicU32",
+ "AtomicU64",
+ "AtomicUsize",
+];
+
+fn type_is_atomic(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+ if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.tables.expr_ty(expr).kind {
+ ATOMIC_TYPES
+ .iter()
+ .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
+ } else {
+ false
+ }
+}
+
+fn match_ordering_def_path(cx: &LateContext<'_, '_>, did: DefId, orderings: &[&str]) -> bool {
+ orderings
+ .iter()
+ .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
+}
+
+fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind;
+ let method = method_path.ident.name.as_str();
+ if type_is_atomic(cx, &args[0]);
+ if method == "load" || method == "store";
+ let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
+ if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
+ if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
+ then {
+ if method == "load" &&
+ match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
+ span_lint_and_help(
+ cx,
+ INVALID_ATOMIC_ORDERING,
+ ordering_arg.span,
+ "atomic loads cannot have `Release` and `AcqRel` ordering",
+ None,
+ "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
+ );
+ } else if method == "store" &&
+ match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
+ span_lint_and_help(
+ cx,
+ INVALID_ATOMIC_ORDERING,
+ ordering_arg.span,
+ "atomic stores cannot have `Acquire` and `AcqRel` ordering",
+ None,
+ "consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
+ );
+ }
+ }
+ }
+}
+
+fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Call(ref func, ref args) = expr.kind;
+ if let ExprKind::Path(ref func_qpath) = func.kind;
+ if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
+ if ["fence", "compiler_fence"]
+ .iter()
+ .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
+ if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
+ if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
+ if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
+ then {
+ span_lint_and_help(
+ cx,
+ INVALID_ATOMIC_ORDERING,
+ args[0].span,
+ "memory fences cannot have `Relaxed` ordering",
+ None,
+ "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
+ );
+ }
+ }
+}
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+ check_atomic_load_store(cx, expr);
+ check_memory_fence(cx, expr);
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
new file mode 100644
index 00000000000..64abc9fdc71
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -0,0 +1,657 @@
+//! checks for attributes
+
+use crate::reexport::Name;
+use crate::utils::{
+ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg,
+ span_lint_and_then, without_block_comments,
+};
+use if_chain::if_chain;
+use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_ast::util::lev_distance::find_best_match_for_name;
+use rustc_errors::Applicability;
+use rustc_hir::{
+ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
+};
+use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
+use semver::Version;
+
+static UNIX_SYSTEMS: &[&str] = &[
+ "android",
+ "dragonfly",
+ "emscripten",
+ "freebsd",
+ "fuchsia",
+ "haiku",
+ "illumos",
+ "ios",
+ "l4re",
+ "linux",
+ "macos",
+ "netbsd",
+ "openbsd",
+ "redox",
+ "solaris",
+ "vxworks",
+];
+
+// NOTE: windows is excluded from the list because it's also a valid target family.
+static NON_UNIX_SYSTEMS: &[&str] = &["cloudabi", "hermit", "none", "wasi"];
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for items annotated with `#[inline(always)]`,
+ /// unless the annotated function is empty or simply panics.
+ ///
+ /// **Why is this bad?** While there are valid uses of this annotation (and once
+ /// you know when to use it, by all means `allow` this lint), it's a common
+ /// newbie-mistake to pepper one's code with it.
+ ///
+ /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
+ /// measure if that additional function call really affects your runtime profile
+ /// sufficiently to make up for the increase in compile time.
+ ///
+ /// **Known problems:** False positives, big time. This lint is meant to be
+ /// deactivated by everyone doing serious performance work. This means having
+ /// done the measurement.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// #[inline(always)]
+ /// fn not_quite_hot_code(..) { ... }
+ /// ```
+ pub INLINE_ALWAYS,
+ pedantic,
+ "use of `#[inline(always)]`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `extern crate` and `use` items annotated with
+ /// lint attributes.
+ ///
+ /// This lint whitelists `#[allow(unused_imports)]`, `#[allow(deprecated)]` and
+ /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on
+ /// `extern crate` items with a `#[macro_use]` attribute.
+ ///
+ /// **Why is this bad?** Lint attributes have no effect on crate imports. Most
+ /// likely a `!` was forgotten.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// // Bad
+ /// #[deny(dead_code)]
+ /// extern crate foo;
+ /// #[forbid(dead_code)]
+ /// use foo::bar;
+ ///
+ /// // Ok
+ /// #[allow(unused_imports)]
+ /// use foo::baz;
+ /// #[allow(unused_imports)]
+ /// #[macro_use]
+ /// extern crate baz;
+ /// ```
+ pub USELESS_ATTRIBUTE,
+ correctness,
+ "use of lint attributes on `extern crate` items"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `#[deprecated]` annotations with a `since`
+ /// field that is not a valid semantic version.
+ ///
+ /// **Why is this bad?** For checking the version of the deprecation, it must be
+ /// a valid semver. Failing that, the contained information is useless.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// #[deprecated(since = "forever")]
+ /// fn something_else() { /* ... */ }
+ /// ```
+ pub DEPRECATED_SEMVER,
+ correctness,
+ "use of `#[deprecated(since = \"x\")]` where x is not semver"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for empty lines after outer attributes
+ ///
+ /// **Why is this bad?**
+ /// Most likely the attribute was meant to be an inner attribute using a '!'.
+ /// If it was meant to be an outer attribute, then the following item
+ /// should not be separated by empty lines.
+ ///
+ /// **Known problems:** Can cause false positives.
+ ///
+ /// From the clippy side it's difficult to detect empty lines between an attributes and the
+ /// following item because empty lines and comments are not part of the AST. The parsing
+ /// currently works for basic cases but is not perfect.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// // Good (as inner attribute)
+ /// #![inline(always)]
+ ///
+ /// fn this_is_fine() { }
+ ///
+ /// // Bad
+ /// #[inline(always)]
+ ///
+ /// fn not_quite_good_code() { }
+ ///
+ /// // Good (as outer attribute)
+ /// #[inline(always)]
+ /// fn this_is_fine_too() { }
+ /// ```
+ pub EMPTY_LINE_AFTER_OUTER_ATTR,
+ nursery,
+ "empty line after outer attribute"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
+ /// lints and if those lints exist in clippy. If there is an uppercase letter in the lint name
+ /// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
+ /// the lint name.
+ ///
+ /// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// Bad:
+ /// ```rust
+ /// #![warn(if_not_els)]
+ /// #![deny(clippy::All)]
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #![warn(if_not_else)]
+ /// #![deny(clippy::all)]
+ /// ```
+ pub UNKNOWN_CLIPPY_LINTS,
+ style,
+ "unknown_lints for scoped Clippy lints"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
+ /// with `#[rustfmt::skip]`.
+ ///
+ /// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
+ /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
+ ///
+ /// **Known problems:** This lint doesn't detect crate level inner attributes, because they get
+ /// processed before the PreExpansionPass lints get executed. See
+ /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
+ ///
+ /// **Example:**
+ ///
+ /// Bad:
+ /// ```rust
+ /// #[cfg_attr(rustfmt, rustfmt_skip)]
+ /// fn main() { }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #[rustfmt::skip]
+ /// fn main() { }
+ /// ```
+ pub DEPRECATED_CFG_ATTR,
+ complexity,
+ "usage of `cfg_attr(rustfmt)` instead of tool attributes"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for cfg attributes having operating systems used in target family position.
+ ///
+ /// **Why is this bad?** The configuration option will not be recognised and the related item will not be included
+ /// by the conditional compilation engine.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// Bad:
+ /// ```rust
+ /// #[cfg(linux)]
+ /// fn conditional() { }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust
+ /// #[cfg(target_os = "linux")]
+ /// fn conditional() { }
+ /// ```
+ ///
+ /// Or:
+ /// ```rust
+ /// #[cfg(unix)]
+ /// fn conditional() { }
+ /// ```
+ /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+ pub MISMATCHED_TARGET_OS,
+ correctness,
+ "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
+}
+
+declare_lint_pass!(Attributes => [
+ INLINE_ALWAYS,
+ DEPRECATED_SEMVER,
+ USELESS_ATTRIBUTE,
+ EMPTY_LINE_AFTER_OUTER_ATTR,
+ UNKNOWN_CLIPPY_LINTS,
+]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes {
+ fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
+ if let Some(items) = &attr.meta_item_list() {
+ if let Some(ident) = attr.ident() {
+ match &*ident.as_str() {
+ "allow" | "warn" | "deny" | "forbid" => {
+ check_clippy_lint_names(cx, items);
+ },
+ _ => {},
+ }
+ if items.is_empty() || !attr.check_name(sym!(deprecated)) {
+ return;
+ }
+ for item in items {
+ if_chain! {
+ if let NestedMetaItem::MetaItem(mi) = &item;
+ if let MetaItemKind::NameValue(lit) = &mi.kind;
+ if mi.check_name(sym!(since));
+ then {
+ check_semver(cx, item.span(), lit);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
+ if is_relevant_item(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, &item.attrs)
+ }
+ match item.kind {
+ ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
+ let skip_unused_imports = item.attrs.iter().any(|attr| attr.check_name(sym!(macro_use)));
+
+ for attr in item.attrs {
+ if in_external_macro(cx.sess(), attr.span) {
+ return;
+ }
+ if let Some(lint_list) = &attr.meta_item_list() {
+ if let Some(ident) = attr.ident() {
+ match &*ident.as_str() {
+ "allow" | "warn" | "deny" | "forbid" => {
+ // whitelist `unused_imports`, `deprecated` and `unreachable_pub` for `use` items
+ // and `unused_imports` for `extern crate` items with `macro_use`
+ for lint in lint_list {
+ match item.kind {
+ ItemKind::Use(..) => {
+ if is_word(lint, sym!(unused_imports))
+ || is_word(lint, sym!(deprecated))
+ || is_word(lint, sym!(unreachable_pub))
+ || is_word(lint, sym!(unused))
+ {
+ return;
+ }
+ },
+ ItemKind::ExternCrate(..) => {
+ if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
+ return;
+ }
+ if is_word(lint, sym!(unused_extern_crates)) {
+ return;
+ }
+ },
+ _ => {},
+ }
+ }
+ let line_span = first_line_of_span(cx, attr.span);
+
+ if let Some(mut sugg) = snippet_opt(cx, line_span) {
+ if sugg.contains("#[") {
+ span_lint_and_then(
+ cx,
+ USELESS_ATTRIBUTE,
+ line_span,
+ "useless lint attribute",
+ |diag| {
+ sugg = sugg.replacen("#[", "#![", 1);
+ diag.span_suggestion(
+ line_span,
+ "if you just forgot a `!`, use",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
+ if is_relevant_impl(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, &item.attrs)
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
+ if is_relevant_trait(cx, item) {
+ check_attrs(cx, item.span, item.ident.name, &item.attrs)
+ }
+ }
+}
+
+#[allow(clippy::single_match_else)]
+fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) {
+ let lint_store = cx.lints();
+ for lint in items {
+ if_chain! {
+ if let Some(meta_item) = lint.meta_item();
+ if meta_item.path.segments.len() > 1;
+ if let tool_name = meta_item.path.segments[0].ident;
+ if tool_name.as_str() == "clippy";
+ let name = meta_item.path.segments.last().unwrap().ident.name;
+ if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
+ &name.as_str(),
+ Some(tool_name.name),
+ );
+ then {
+ span_lint_and_then(
+ cx,
+ UNKNOWN_CLIPPY_LINTS,
+ lint.span(),
+ &format!("unknown clippy lint: clippy::{}", name),
+ |diag| {
+ let name_lower = name.as_str().to_lowercase();
+ let symbols = lint_store.get_lints().iter().map(
+ |l| Symbol::intern(&l.name_lower())
+ ).collect::>();
+ let sugg = find_best_match_for_name(
+ symbols.iter(),
+ &format!("clippy::{}", name_lower),
+ None,
+ );
+ if name.as_str().chars().any(char::is_uppercase)
+ && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
+ diag.span_suggestion(
+ lint.span(),
+ "lowercase the lint name",
+ format!("clippy::{}", name_lower),
+ Applicability::MachineApplicable,
+ );
+ } else if let Some(sugg) = sugg {
+ diag.span_suggestion(
+ lint.span(),
+ "did you mean",
+ sugg.to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ );
+ }
+ };
+ }
+}
+
+fn is_relevant_item(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool {
+ if let ItemKind::Fn(_, _, eid) = item.kind {
+ is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value)
+ } else {
+ true
+ }
+}
+
+fn is_relevant_impl(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) -> bool {
+ match item.kind {
+ ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value),
+ _ => false,
+ }
+}
+
+fn is_relevant_trait(cx: &LateContext<'_, '_>, item: &TraitItem<'_>) -> bool {
+ match item.kind {
+ TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
+ TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
+ is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value)
+ },
+ _ => false,
+ }
+}
+
+fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
+ if let Some(stmt) = block.stmts.first() {
+ match &stmt.kind {
+ StmtKind::Local(_) => true,
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
+ _ => false,
+ }
+ } else {
+ block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
+ }
+}
+
+fn is_relevant_expr(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
+ match &expr.kind {
+ ExprKind::Block(block, _) => is_relevant_block(cx, tables, block),
+ ExprKind::Ret(Some(e)) => is_relevant_expr(cx, tables, e),
+ ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
+ ExprKind::Call(path_expr, _) => {
+ if let ExprKind::Path(qpath) = &path_expr.kind {
+ if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
+ !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ },
+ _ => true,
+ }
+}
+
+fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attribute]) {
+ if span.from_expansion() {
+ return;
+ }
+
+ for attr in attrs {
+ let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
+ attr
+ } else {
+ continue;
+ };
+
+ if attr.style == AttrStyle::Outer {
+ if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
+ return;
+ }
+
+ let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt());
+ let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt());
+
+ if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
+ let lines = snippet.split('\n').collect::>();
+ let lines = without_block_comments(lines);
+
+ if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
+ span_lint(
+ cx,
+ EMPTY_LINE_AFTER_OUTER_ATTR,
+ begin_of_attr_to_item,
+ "Found an empty line after an outer attribute. \
+ Perhaps you forgot to add a `!` to make it an inner attribute?",
+ );
+ }
+ }
+ }
+
+ if let Some(values) = attr.meta_item_list() {
+ if values.len() != 1 || !attr.check_name(sym!(inline)) {
+ continue;
+ }
+ if is_word(&values[0], sym!(always)) {
+ span_lint(
+ cx,
+ INLINE_ALWAYS,
+ attr.span,
+ &format!(
+ "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
+ name
+ ),
+ );
+ }
+ }
+ }
+}
+
+fn check_semver(cx: &LateContext<'_, '_>, span: Span, lit: &Lit) {
+ if let LitKind::Str(is, _) = lit.kind {
+ if Version::parse(&is.as_str()).is_ok() {
+ return;
+ }
+ }
+ span_lint(
+ cx,
+ DEPRECATED_SEMVER,
+ span,
+ "the since field must contain a semver-compliant version",
+ );
+}
+
+fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
+ if let NestedMetaItem::MetaItem(mi) = &nmi {
+ mi.is_word() && mi.check_name(expected)
+ } else {
+ false
+ }
+}
+
+declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
+
+impl EarlyLintPass for EarlyAttributes {
+ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
+ check_deprecated_cfg_attr(cx, attr);
+ check_mismatched_target_os(cx, attr);
+ }
+}
+
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
+ if_chain! {
+ // check cfg_attr
+ if attr.check_name(sym!(cfg_attr));
+ if let Some(items) = attr.meta_item_list();
+ if items.len() == 2;
+ // check for `rustfmt`
+ if let Some(feature_item) = items[0].meta_item();
+ if feature_item.check_name(sym!(rustfmt));
+ // check for `rustfmt_skip` and `rustfmt::skip`
+ if let Some(skip_item) = &items[1].meta_item();
+ if skip_item.check_name(sym!(rustfmt_skip)) ||
+ skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip);
+ // Only lint outer attributes, because custom inner attributes are unstable
+ // Tracking issue: https://github.com/rust-lang/rust/issues/54726
+ if let AttrStyle::Outer = attr.style;
+ then {
+ span_lint_and_sugg(
+ cx,
+ DEPRECATED_CFG_ATTR,
+ attr.span,
+ "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
+ "use",
+ "#[rustfmt::skip]".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
+ fn find_os(name: &str) -> Option<&'static str> {
+ UNIX_SYSTEMS
+ .iter()
+ .chain(NON_UNIX_SYSTEMS.iter())
+ .find(|&&os| os == name)
+ .copied()
+ }
+
+ fn is_unix(name: &str) -> bool {
+ UNIX_SYSTEMS.iter().any(|&os| os == name)
+ }
+
+ fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
+ let mut mismatched = Vec::new();
+
+ for item in items {
+ if let NestedMetaItem::MetaItem(meta) = item {
+ match &meta.kind {
+ MetaItemKind::List(list) => {
+ mismatched.extend(find_mismatched_target_os(&list));
+ },
+ MetaItemKind::Word => {
+ if_chain! {
+ if let Some(ident) = meta.ident();
+ if let Some(os) = find_os(&*ident.name.as_str());
+ then {
+ mismatched.push((os, ident.span));
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+
+ mismatched
+ }
+
+ if_chain! {
+ if attr.check_name(sym!(cfg));
+ if let Some(list) = attr.meta_item_list();
+ let mismatched = find_mismatched_target_os(&list);
+ if !mismatched.is_empty();
+ then {
+ let mess = "operating system used in target family position";
+
+ span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| {
+ // Avoid showing the unix suggestion multiple times in case
+ // we have more than one mismatch for unix-like systems
+ let mut unix_suggested = false;
+
+ for (os, span) in mismatched {
+ let sugg = format!("target_os = \"{}\"", os);
+ diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
+
+ if !unix_suggested && is_unix(os) {
+ diag.help("Did you mean `unix`?");
+ unix_suggested = true;
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
new file mode 100644
index 00000000000..832910763e6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
@@ -0,0 +1,97 @@
+use crate::utils::{match_def_path, paths, span_lint_and_note};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::GeneratorInteriorTypeCause;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for calls to await while holding a
+ /// non-async-aware MutexGuard.
+ ///
+ /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
+ /// are not designed to operator in an async context across await points.
+ ///
+ /// There are two potential solutions. One is to use an asynx-aware Mutex
+ /// type. Many asynchronous foundation crates provide such a Mutex type. The
+ /// other solution is to ensure the mutex is unlocked before calling await,
+ /// either by introducing a scope or an explicit call to Drop::drop.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust,ignore
+ /// use std::sync::Mutex;
+ ///
+ /// async fn foo(x: &Mutex) {
+ /// let guard = x.lock().unwrap();
+ /// *guard += 1;
+ /// bar.await;
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// use std::sync::Mutex;
+ ///
+ /// async fn foo(x: &Mutex) {
+ /// {
+ /// let guard = x.lock().unwrap();
+ /// *guard += 1;
+ /// }
+ /// bar.await;
+ /// }
+ /// ```
+ pub AWAIT_HOLDING_LOCK,
+ pedantic,
+ "Inside an async function, holding a MutexGuard while calling await"
+}
+
+declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
+
+impl LateLintPass<'_, '_> for AwaitHoldingLock {
+ fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) {
+ use AsyncGeneratorKind::{Block, Closure, Fn};
+ match body.generator_kind {
+ Some(GeneratorKind::Async(Block))
+ | Some(GeneratorKind::Async(Closure))
+ | Some(GeneratorKind::Async(Fn)) => {
+ let body_id = BodyId {
+ hir_id: body.value.hir_id,
+ };
+ let def_id = cx.tcx.hir().body_owner_def_id(body_id);
+ let tables = cx.tcx.typeck_tables_of(def_id);
+ check_interior_types(cx, &tables.generator_interior_types, body.value.span);
+ },
+ _ => {},
+ }
+ }
+}
+
+fn check_interior_types(cx: &LateContext<'_, '_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
+ for ty_cause in ty_causes {
+ if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind {
+ if is_mutex_guard(cx, adt.did) {
+ span_lint_and_note(
+ cx,
+ AWAIT_HOLDING_LOCK,
+ ty_cause.span,
+ "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.",
+ ty_cause.scope_span.or(Some(span)),
+ "these are all the await points this lock is held through",
+ );
+ }
+ }
+ }
+}
+
+fn is_mutex_guard(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
+ match_def_path(cx, def_id, &paths::MUTEX_GUARD)
+ || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
+ || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
+ || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
+}
diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/bit_mask.rs
new file mode 100644
index 00000000000..ccb62cb038f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/bit_mask.rs
@@ -0,0 +1,326 @@
+use crate::consts::{constant, Constant};
+use crate::utils::sugg::Sugg;
+use crate::utils::{span_lint, span_lint_and_then};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for incompatible bit masks in comparisons.
+ ///
+ /// The formula for detecting if an expression of the type `_ m
+ /// c` (where `` is one of {`&`, `|`} and `` is one of
+ /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
+ /// table:
+ ///
+ /// |Comparison |Bit Op|Example |is always|Formula |
+ /// |------------|------|------------|---------|----------------------|
+ /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
+ /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
+ /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
+ /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
+ /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
+ /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
+ ///
+ /// **Why is this bad?** If the bits that the comparison cares about are always
+ /// set to zero or one by the bit mask, the comparison is constant `true` or
+ /// `false` (depending on mask, compared value, and operators).
+ ///
+ /// So the code is actively misleading, and the only reason someone would write
+ /// this intentionally is to win an underhanded Rust contest or create a
+ /// test-case for this lint.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let x = 1;
+ /// if (x & 1 == 2) { }
+ /// ```
+ pub BAD_BIT_MASK,
+ correctness,
+ "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for bit masks in comparisons which can be removed
+ /// without changing the outcome. The basic structure can be seen in the
+ /// following table:
+ ///
+ /// |Comparison| Bit Op |Example |equals |
+ /// |----------|---------|-----------|-------|
+ /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
+ /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
+ ///
+ /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
+ /// but still a bit misleading, because the bit mask is ineffective.
+ ///
+ /// **Known problems:** False negatives: This lint will only match instances
+ /// where we have figured out the math (which is for a power-of-two compared
+ /// value). This means things like `x | 1 >= 7` (which would be better written
+ /// as `x >= 6`) will not be reported (but bit masks like this are fairly
+ /// uncommon).
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let x = 1;
+ /// if (x | 1 > 3) { }
+ /// ```
+ pub INEFFECTIVE_BIT_MASK,
+ correctness,
+ "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for bit masks that can be replaced by a call
+ /// to `trailing_zeros`
+ ///
+ /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
+ /// == 0`
+ ///
+ /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let x = 1;
+ /// if x & 0b1111 == 0 { }
+ /// ```
+ pub VERBOSE_BIT_MASK,
+ style,
+ "expressions where a bit mask is less readable than the corresponding method call"
+}
+
+#[derive(Copy, Clone)]
+pub struct BitMask {
+ verbose_bit_mask_threshold: u64,
+}
+
+impl BitMask {
+ #[must_use]
+ pub fn new(verbose_bit_mask_threshold: u64) -> Self {
+ Self {
+ verbose_bit_mask_threshold,
+ }
+ }
+}
+
+impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Binary(cmp, left, right) = &e.kind {
+ if cmp.node.is_comparison() {
+ if let Some(cmp_opt) = fetch_int_literal(cx, right) {
+ check_compare(cx, left, cmp.node, cmp_opt, e.span)
+ } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
+ check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span)
+ }
+ }
+ }
+ if_chain! {
+ if let ExprKind::Binary(op, left, right) = &e.kind;
+ if BinOpKind::Eq == op.node;
+ if let ExprKind::Binary(op1, left1, right1) = &left.kind;
+ if BinOpKind::BitAnd == op1.node;
+ if let ExprKind::Lit(lit) = &right1.kind;
+ if let LitKind::Int(n, _) = lit.node;
+ if let ExprKind::Lit(lit1) = &right.kind;
+ if let LitKind::Int(0, _) = lit1.node;
+ if n.leading_zeros() == n.count_zeros();
+ if n > u128::from(self.verbose_bit_mask_threshold);
+ then {
+ span_lint_and_then(cx,
+ VERBOSE_BIT_MASK,
+ e.span,
+ "bit mask could be simplified with a call to `trailing_zeros`",
+ |diag| {
+ let sugg = Sugg::hir(cx, left1, "...").maybe_par();
+ diag.span_suggestion(
+ e.span,
+ "try",
+ format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
+ Applicability::MaybeIncorrect,
+ );
+ });
+ }
+ }
+ }
+}
+
+#[must_use]
+fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
+ match cmp {
+ BinOpKind::Eq => BinOpKind::Eq,
+ BinOpKind::Ne => BinOpKind::Ne,
+ BinOpKind::Lt => BinOpKind::Gt,
+ BinOpKind::Gt => BinOpKind::Lt,
+ BinOpKind::Le => BinOpKind::Ge,
+ BinOpKind::Ge => BinOpKind::Le,
+ _ => BinOpKind::Or, // Dummy
+ }
+}
+
+fn check_compare(cx: &LateContext<'_, '_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
+ if let ExprKind::Binary(op, left, right) = &bit_op.kind {
+ if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
+ return;
+ }
+ fetch_int_literal(cx, right)
+ .or_else(|| fetch_int_literal(cx, left))
+ .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span))
+ }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_bit_mask(
+ cx: &LateContext<'_, '_>,
+ bit_op: BinOpKind,
+ cmp_op: BinOpKind,
+ mask_value: u128,
+ cmp_value: u128,
+ span: Span,
+) {
+ match cmp_op {
+ BinOpKind::Eq | BinOpKind::Ne => match bit_op {
+ BinOpKind::BitAnd => {
+ if mask_value & cmp_value != cmp_value {
+ if cmp_value != 0 {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ & {}` can never be equal to `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ }
+ } else if mask_value == 0 {
+ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+ }
+ },
+ BinOpKind::BitOr => {
+ if mask_value | cmp_value != cmp_value {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ | {}` can never be equal to `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ }
+ },
+ _ => (),
+ },
+ BinOpKind::Lt | BinOpKind::Ge => match bit_op {
+ BinOpKind::BitAnd => {
+ if mask_value < cmp_value {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ & {}` will always be lower than `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ } else if mask_value == 0 {
+ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+ }
+ },
+ BinOpKind::BitOr => {
+ if mask_value >= cmp_value {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ | {}` will never be lower than `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ } else {
+ check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
+ }
+ },
+ BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
+ _ => (),
+ },
+ BinOpKind::Le | BinOpKind::Gt => match bit_op {
+ BinOpKind::BitAnd => {
+ if mask_value <= cmp_value {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ & {}` will never be higher than `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ } else if mask_value == 0 {
+ span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+ }
+ },
+ BinOpKind::BitOr => {
+ if mask_value > cmp_value {
+ span_lint(
+ cx,
+ BAD_BIT_MASK,
+ span,
+ &format!(
+ "incompatible bit mask: `_ | {}` will always be higher than `{}`",
+ mask_value, cmp_value
+ ),
+ );
+ } else {
+ check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
+ }
+ },
+ BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
+ _ => (),
+ },
+ _ => (),
+ }
+}
+
+fn check_ineffective_lt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
+ if c.is_power_of_two() && m < c {
+ span_lint(
+ cx,
+ INEFFECTIVE_BIT_MASK,
+ span,
+ &format!(
+ "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
+ op, m, c
+ ),
+ );
+ }
+}
+
+fn check_ineffective_gt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
+ if (c + 1).is_power_of_two() && m <= c {
+ span_lint(
+ cx,
+ INEFFECTIVE_BIT_MASK,
+ span,
+ &format!(
+ "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
+ op, m, c
+ ),
+ );
+ }
+}
+
+fn fetch_int_literal(cx: &LateContext<'_, '_>, lit: &Expr<'_>) -> Option {
+ match constant(cx, cx.tables, lit)?.0 {
+ Constant::Int(n) => Some(n),
+ _ => None,
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs
new file mode 100644
index 00000000000..6f26f031431
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs
@@ -0,0 +1,51 @@
+use crate::utils::span_lint;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{Pat, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of blacklisted names for variables, such
+ /// as `foo`.
+ ///
+ /// **Why is this bad?** These names are usually placeholder names and should be
+ /// avoided.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let foo = 3.14;
+ /// ```
+ pub BLACKLISTED_NAME,
+ style,
+ "usage of a blacklisted/placeholder name"
+}
+
+#[derive(Clone, Debug)]
+pub struct BlacklistedName {
+ blacklist: FxHashSet,
+}
+
+impl BlacklistedName {
+ pub fn new(blacklist: FxHashSet) -> Self {
+ Self { blacklist }
+ }
+}
+
+impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlacklistedName {
+ fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
+ if let PatKind::Binding(.., ident, _) = pat.kind {
+ if self.blacklist.contains(&ident.name.to_string()) {
+ span_lint(
+ cx,
+ BLACKLISTED_NAME,
+ ident.span,
+ &format!("use of a blacklisted/placeholder name `{}`", ident.name),
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/block_in_if_condition.rs b/src/tools/clippy/clippy_lints/src/block_in_if_condition.rs
new file mode 100644
index 00000000000..9e533eaa32c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/block_in_if_condition.rs
@@ -0,0 +1,148 @@
+use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{BlockCheckMode, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `if` conditions that use blocks to contain an
+ /// expression.
+ ///
+ /// **Why is this bad?** It isn't really Rust style, same as using parentheses
+ /// to contain expressions.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// if { true } { /* ... */ }
+ /// ```
+ pub BLOCK_IN_IF_CONDITION_EXPR,
+ style,
+ "braces that can be eliminated in conditions, e.g., `if { true } ...`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `if` conditions that use blocks containing
+ /// statements, or conditions that use closures with blocks.
+ ///
+ /// **Why is this bad?** Using blocks in the condition makes it hard to read.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// if { let x = somefunc(); x } {}
+ /// // or
+ /// if somefunc(|x| { x == 47 }) {}
+ /// ```
+ pub BLOCK_IN_IF_CONDITION_STMT,
+ style,
+ "complex blocks in conditions, e.g., `if { let x = true; x } ...`"
+}
+
+declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]);
+
+struct ExVisitor<'a, 'tcx> {
+ found_block: Option<&'tcx Expr<'tcx>>,
+ cx: &'a LateContext<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
+ let body = self.cx.tcx.hir().body(eid);
+ let ex = &body.value;
+ if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
+ self.found_block = Some(ex);
+ return;
+ }
+ }
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap {
+ NestedVisitorMap::None
+ }
+}
+
+const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
+const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
+ instead, move the block or closure higher and bind it with a `let`";
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+ if let Some((cond, _, _)) = higher::if_block(&expr) {
+ if let ExprKind::Block(block, _) = &cond.kind {
+ if block.rules == BlockCheckMode::DefaultBlock {
+ if block.stmts.is_empty() {
+ if let Some(ex) = &block.expr {
+ // don't dig into the expression here, just suggest that they remove
+ // the block
+ if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
+ return;
+ }
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ BLOCK_IN_IF_CONDITION_EXPR,
+ cond.span,
+ BRACED_EXPR_MESSAGE,
+ "try",
+ format!(
+ "{}",
+ snippet_block_with_applicability(
+ cx,
+ ex.span,
+ "..",
+ Some(expr.span),
+ &mut applicability
+ )
+ ),
+ applicability,
+ );
+ }
+ } else {
+ let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
+ if span.from_expansion() || differing_macro_contexts(expr.span, span) {
+ return;
+ }
+ // move block higher
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ BLOCK_IN_IF_CONDITION_STMT,
+ expr.span.with_hi(cond.span.hi()),
+ COMPLEX_BLOCK_MESSAGE,
+ "try",
+ format!(
+ "let res = {}; if res",
+ snippet_block_with_applicability(
+ cx,
+ block.span,
+ "..",
+ Some(expr.span),
+ &mut applicability
+ ),
+ ),
+ applicability,
+ );
+ }
+ }
+ } else {
+ let mut visitor = ExVisitor { found_block: None, cx };
+ walk_expr(&mut visitor, cond);
+ if let Some(block) = visitor.found_block {
+ span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE);
+ }
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
new file mode 100644
index 00000000000..8031052e073
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -0,0 +1,499 @@
+use crate::utils::{
+ get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg,
+ span_lint_and_then, SpanlessEq,
+};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for boolean expressions that can be written more
+ /// concisely.
+ ///
+ /// **Why is this bad?** Readability of boolean expressions suffers from
+ /// unnecessary duplication.
+ ///
+ /// **Known problems:** Ignores short circuiting behavior of `||` and
+ /// `&&`. Ignores `|`, `&` and `^`.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// if a && true // should be: if a
+ /// if !(a == b) // should be: if a != b
+ /// ```
+ pub NONMINIMAL_BOOL,
+ complexity,
+ "boolean expressions that can be written more concisely"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for boolean expressions that contain terminals that
+ /// can be eliminated.
+ ///
+ /// **Why is this bad?** This is most likely a logic bug.
+ ///
+ /// **Known problems:** Ignores short circuiting behavior.
+ ///
+ /// **Example:**
+ /// ```ignore
+ /// if a && b || a { ... }
+ /// ```
+ /// The `b` is unnecessary, the expression is equivalent to `if a`.
+ pub LOGIC_BUG,
+ correctness,
+ "boolean expressions that contain terminals which can be eliminated"
+}
+
+// For each pairs, both orders are considered.
+const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
+
+declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, LOGIC_BUG]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'a, 'tcx>,
+ _: FnKind<'tcx>,
+ _: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ _: Span,
+ _: HirId,
+ ) {
+ NonminimalBoolVisitor { cx }.visit_body(body)
+ }
+}
+
+struct NonminimalBoolVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+}
+
+use quine_mc_cluskey::Bool;
+struct Hir2Qmm<'a, 'tcx, 'v> {
+ terminals: Vec<&'v Expr<'v>>,
+ cx: &'a LateContext<'a, 'tcx>,
+}
+
+impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
+ fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> {
+ for a in a {
+ if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
+ if binop.node == op {
+ v = self.extract(op, &[lhs, rhs], v)?;
+ continue;
+ }
+ }
+ v.push(self.run(a)?);
+ }
+ Ok(v)
+ }
+
+ fn run(&mut self, e: &'v Expr<'_>) -> Result {
+ fn negate(bin_op_kind: BinOpKind) -> Option {
+ match bin_op_kind {
+ BinOpKind::Eq => Some(BinOpKind::Ne),
+ BinOpKind::Ne => Some(BinOpKind::Eq),
+ BinOpKind::Gt => Some(BinOpKind::Le),
+ BinOpKind::Ge => Some(BinOpKind::Lt),
+ BinOpKind::Lt => Some(BinOpKind::Ge),
+ BinOpKind::Le => Some(BinOpKind::Gt),
+ _ => None,
+ }
+ }
+
+ // prevent folding of `cfg!` macros and the like
+ if !e.span.from_expansion() {
+ match &e.kind {
+ ExprKind::Unary(UnOp::UnNot, inner) => return Ok(Bool::Not(box self.run(inner)?)),
+ ExprKind::Binary(binop, lhs, rhs) => match &binop.node {
+ BinOpKind::Or => return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?)),
+ BinOpKind::And => return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?)),
+ _ => (),
+ },
+ ExprKind::Lit(lit) => match lit.node {
+ LitKind::Bool(true) => return Ok(Bool::True),
+ LitKind::Bool(false) => return Ok(Bool::False),
+ _ => (),
+ },
+ _ => (),
+ }
+ }
+ for (n, expr) in self.terminals.iter().enumerate() {
+ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
+ #[allow(clippy::cast_possible_truncation)]
+ return Ok(Bool::Term(n as u8));
+ }
+
+ if_chain! {
+ if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
+ if implements_ord(self.cx, e_lhs);
+ if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
+ if negate(e_binop.node) == Some(expr_binop.node);
+ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs);
+ if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs);
+ then {
+ #[allow(clippy::cast_possible_truncation)]
+ return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
+ }
+ }
+ }
+ let n = self.terminals.len();
+ self.terminals.push(e);
+ if n < 32 {
+ #[allow(clippy::cast_possible_truncation)]
+ Ok(Bool::Term(n as u8))
+ } else {
+ Err("too many literals".to_owned())
+ }
+ }
+}
+
+struct SuggestContext<'a, 'tcx, 'v> {
+ terminals: &'v [&'v Expr<'v>],
+ cx: &'a LateContext<'a, 'tcx>,
+ output: String,
+}
+
+impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
+ fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
+ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
+ match suggestion {
+ True => {
+ self.output.push_str("true");
+ },
+ False => {
+ self.output.push_str("false");
+ },
+ Not(inner) => match **inner {
+ And(_) | Or(_) => {
+ self.output.push('!');
+ self.output.push('(');
+ self.recurse(inner);
+ self.output.push(')');
+ },
+ Term(n) => {
+ let terminal = self.terminals[n as usize];
+ if let Some(str) = simplify_not(self.cx, terminal) {
+ self.output.push_str(&str)
+ } else {
+ self.output.push('!');
+ let snip = snippet_opt(self.cx, terminal.span)?;
+ self.output.push_str(&snip);
+ }
+ },
+ True | False | Not(_) => {
+ self.output.push('!');
+ self.recurse(inner)?;
+ },
+ },
+ And(v) => {
+ for (index, inner) in v.iter().enumerate() {
+ if index > 0 {
+ self.output.push_str(" && ");
+ }
+ if let Or(_) = *inner {
+ self.output.push('(');
+ self.recurse(inner);
+ self.output.push(')');
+ } else {
+ self.recurse(inner);
+ }
+ }
+ },
+ Or(v) => {
+ for (index, inner) in v.iter().rev().enumerate() {
+ if index > 0 {
+ self.output.push_str(" || ");
+ }
+ self.recurse(inner);
+ }
+ },
+ &Term(n) => {
+ let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?;
+ self.output.push_str(&snip);
+ },
+ }
+ Some(())
+ }
+}
+
+fn simplify_not(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option {
+ match &expr.kind {
+ ExprKind::Binary(binop, lhs, rhs) => {
+ if !implements_ord(cx, lhs) {
+ return None;
+ }
+
+ match binop.node {
+ BinOpKind::Eq => Some(" != "),
+ BinOpKind::Ne => Some(" == "),
+ BinOpKind::Lt => Some(" >= "),
+ BinOpKind::Gt => Some(" <= "),
+ BinOpKind::Le => Some(" > "),
+ BinOpKind::Ge => Some(" < "),
+ _ => None,
+ }
+ .and_then(|op| {
+ Some(format!(
+ "{}{}{}",
+ snippet_opt(cx, lhs.span)?,
+ op,
+ snippet_opt(cx, rhs.span)?
+ ))
+ })
+ },
+ ExprKind::MethodCall(path, _, args) if args.len() == 1 => {
+ let type_of_receiver = cx.tables.expr_ty(&args[0]);
+ if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type))
+ && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type))
+ {
+ return None;
+ }
+ METHODS_WITH_NEGATION
+ .iter()
+ .cloned()
+ .flat_map(|(a, b)| vec![(a, b), (b, a)])
+ .find(|&(a, _)| {
+ let path: &str = &path.ident.name.as_str();
+ a == path
+ })
+ .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method)))
+ },
+ _ => None,
+ }
+}
+
+fn suggest(cx: &LateContext<'_, '_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
+ let mut suggest_context = SuggestContext {
+ terminals,
+ cx,
+ output: String::new(),
+ };
+ suggest_context.recurse(suggestion);
+ suggest_context.output
+}
+
+fn simple_negate(b: Bool) -> Bool {
+ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
+ match b {
+ True => False,
+ False => True,
+ t @ Term(_) => Not(Box::new(t)),
+ And(mut v) => {
+ for el in &mut v {
+ *el = simple_negate(::std::mem::replace(el, True));
+ }
+ Or(v)
+ },
+ Or(mut v) => {
+ for el in &mut v {
+ *el = simple_negate(::std::mem::replace(el, True));
+ }
+ And(v)
+ },
+ Not(inner) => *inner,
+ }
+}
+
+#[derive(Default)]
+struct Stats {
+ terminals: [usize; 32],
+ negations: usize,
+ ops: usize,
+}
+
+fn terminal_stats(b: &Bool) -> Stats {
+ fn recurse(b: &Bool, stats: &mut Stats) {
+ match b {
+ True | False => stats.ops += 1,
+ Not(inner) => {
+ match **inner {
+ And(_) | Or(_) => stats.ops += 1, // brackets are also operations
+ _ => stats.negations += 1,
+ }
+ recurse(inner, stats);
+ },
+ And(v) | Or(v) => {
+ stats.ops += v.len() - 1;
+ for inner in v {
+ recurse(inner, stats);
+ }
+ },
+ &Term(n) => stats.terminals[n as usize] += 1,
+ }
+ }
+ use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
+ let mut stats = Stats::default();
+ recurse(b, &mut stats);
+ stats
+}
+
+impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
+ fn bool_expr(&self, e: &'tcx Expr<'_>) {
+ let mut h2q = Hir2Qmm {
+ terminals: Vec::new(),
+ cx: self.cx,
+ };
+ if let Ok(expr) = h2q.run(e) {
+ if h2q.terminals.len() > 8 {
+ // QMC has exponentially slow behavior as the number of terminals increases
+ // 8 is reasonable, it takes approximately 0.2 seconds.
+ // See #825
+ return;
+ }
+
+ let stats = terminal_stats(&expr);
+ let mut simplified = expr.simplify();
+ for simple in Bool::Not(Box::new(expr)).simplify() {
+ match simple {
+ Bool::Not(_) | Bool::True | Bool::False => {},
+ _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
+ }
+ let simple_negated = simple_negate(simple);
+ if simplified.iter().any(|s| *s == simple_negated) {
+ continue;
+ }
+ simplified.push(simple_negated);
+ }
+ let mut improvements = Vec::with_capacity(simplified.len());
+ 'simplified: for suggestion in &simplified {
+ let simplified_stats = terminal_stats(suggestion);
+ let mut improvement = false;
+ for i in 0..32 {
+ // ignore any "simplifications" that end up requiring a terminal more often
+ // than in the original expression
+ if stats.terminals[i] < simplified_stats.terminals[i] {
+ continue 'simplified;
+ }
+ if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
+ span_lint_and_then(
+ self.cx,
+ LOGIC_BUG,
+ e.span,
+ "this boolean expression contains a logic bug",
+ |diag| {
+ diag.span_help(
+ h2q.terminals[i].span,
+ "this expression can be optimized out by applying boolean operations to the \
+ outer expression",
+ );
+ diag.span_suggestion(
+ e.span,
+ "it would look like the following",
+ suggest(self.cx, suggestion, &h2q.terminals),
+ // nonminimal_bool can produce minimal but
+ // not human readable expressions (#3141)
+ Applicability::Unspecified,
+ );
+ },
+ );
+ // don't also lint `NONMINIMAL_BOOL`
+ return;
+ }
+ // if the number of occurrences of a terminal decreases or any of the stats
+ // decreases while none increases
+ improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
+ || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
+ || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
+ }
+ if improvement {
+ improvements.push(suggestion);
+ }
+ }
+ let nonminimal_bool_lint = |suggestions: Vec<_>| {
+ span_lint_and_then(
+ self.cx,
+ NONMINIMAL_BOOL,
+ e.span,
+ "this boolean expression can be simplified",
+ |diag| {
+ diag.span_suggestions(
+ e.span,
+ "try",
+ suggestions.into_iter(),
+ // nonminimal_bool can produce minimal but
+ // not human readable expressions (#3141)
+ Applicability::Unspecified,
+ );
+ },
+ );
+ };
+ if improvements.is_empty() {
+ let mut visitor = NotSimplificationVisitor { cx: self.cx };
+ visitor.visit_expr(e);
+ } else {
+ nonminimal_bool_lint(
+ improvements
+ .into_iter()
+ .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
+ .collect(),
+ );
+ }
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if in_macro(e.span) {
+ return;
+ }
+ match &e.kind {
+ ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
+ self.bool_expr(e)
+ },
+ ExprKind::Unary(UnOp::UnNot, inner) => {
+ if self.cx.tables.node_types()[inner.hir_id].is_bool() {
+ self.bool_expr(e);
+ } else {
+ walk_expr(self, e);
+ }
+ },
+ _ => walk_expr(self, e),
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap {
+ NestedVisitorMap::None
+ }
+}
+
+fn implements_ord<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
+ let ty = cx.tables.expr_ty(expr);
+ get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]))
+}
+
+struct NotSimplificationVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if let ExprKind::Unary(UnOp::UnNot, inner) = &expr.kind {
+ if let Some(suggestion) = simplify_not(self.cx, inner) {
+ span_lint_and_sugg(
+ self.cx,
+ NONMINIMAL_BOOL,
+ expr.span,
+ "this boolean expression can be simplified",
+ "try",
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ walk_expr(self, expr);
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap {
+ NestedVisitorMap::None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs
new file mode 100644
index 00000000000..91d3e47d787
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/bytecount.rs
@@ -0,0 +1,117 @@
+use crate::utils::{
+ contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
+ span_lint_and_sugg, walk_ptrs_ty,
+};
+use if_chain::if_chain;
+use rustc_ast::ast::{Name, UintTy};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for naive byte counts
+ ///
+ /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
+ /// crate has methods to count your bytes faster, especially for large slices.
+ ///
+ /// **Known problems:** If you have predominantly small slices, the
+ /// `bytecount::count(..)` method may actually be slower. However, if you can
+ /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
+ /// faster in those cases.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// # let vec = vec![1_u8];
+ /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
+ /// ```
+ pub NAIVE_BYTECOUNT,
+ perf,
+ "use of naive `.filter(|&x| x == y).count()` to count byte values"
+}
+
+declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
+ fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.kind;
+ if count.ident.name == sym!(count);
+ if count_args.len() == 1;
+ if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].kind;
+ if filter.ident.name == sym!(filter);
+ if filter_args.len() == 2;
+ if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
+ then {
+ let body = cx.tcx.hir().body(body_id);
+ if_chain! {
+ if body.params.len() == 1;
+ if let Some(argname) = get_pat_name(&body.params[0].pat);
+ if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
+ if op.node == BinOpKind::Eq;
+ if match_type(cx,
+ walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
+ &paths::SLICE_ITER);
+ then {
+ let needle = match get_path_name(l) {
+ Some(name) if check_arg(name, argname, r) => r,
+ _ => match get_path_name(r) {
+ Some(name) if check_arg(name, argname, l) => l,
+ _ => { return; }
+ }
+ };
+ if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).kind {
+ return;
+ }
+ let haystack = if let ExprKind::MethodCall(ref path, _, ref args) =
+ filter_args[0].kind {
+ let p = path.ident.name;
+ if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 {
+ &args[0]
+ } else {
+ &filter_args[0]
+ }
+ } else {
+ &filter_args[0]
+ };
+ let mut applicability = Applicability::MaybeIncorrect;
+ span_lint_and_sugg(
+ cx,
+ NAIVE_BYTECOUNT,
+ expr.span,
+ "You appear to be counting bytes the naive way",
+ "Consider using the bytecount crate",
+ format!("bytecount::count({}, {})",
+ snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
+ snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
+ applicability,
+ );
+ }
+ };
+ }
+ };
+ }
+}
+
+fn check_arg(name: Name, arg: Name, needle: &Expr<'_>) -> bool {
+ name == arg && !contains_name(name, needle)
+}
+
+fn get_path_name(expr: &Expr<'_>) -> Option {
+ match expr.kind {
+ ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => {
+ get_path_name(e)
+ },
+ ExprKind::Block(ref b, _) => {
+ if b.stmts.is_empty() {
+ b.expr.as_ref().and_then(|p| get_path_name(p))
+ } else {
+ None
+ }
+ },
+ ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
+ _ => None,
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
new file mode 100644
index 00000000000..782da249808
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
@@ -0,0 +1,105 @@
+//! lint on missing cargo common metadata
+
+use std::path::PathBuf;
+
+use crate::utils::{run_lints, span_lint};
+use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::DUMMY_SP;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks to see if all common metadata is defined in
+ /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
+ ///
+ /// **Why is this bad?** It will be more difficult for users to discover the
+ /// purpose of the crate, and key information related to it.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```toml
+ /// # This `Cargo.toml` is missing an authors field:
+ /// [package]
+ /// name = "clippy"
+ /// version = "0.0.212"
+ /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
+ /// repository = "https://github.com/rust-lang/rust-clippy"
+ /// readme = "README.md"
+ /// license = "MIT OR Apache-2.0"
+ /// keywords = ["clippy", "lint", "plugin"]
+ /// categories = ["development-tools", "development-tools::cargo-plugins"]
+ /// ```
+ pub CARGO_COMMON_METADATA,
+ cargo,
+ "common metadata is defined in `Cargo.toml`"
+}
+
+fn warning(cx: &LateContext<'_, '_>, message: &str) {
+ span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
+}
+
+fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) {
+ let message = format!("package `{}` is missing `{}` metadata", package.name, field);
+ warning(cx, &message);
+}
+
+fn is_empty_str(value: &Option) -> bool {
+ value.as_ref().map_or(true, String::is_empty)
+}
+
+fn is_empty_path(value: &Option) -> bool {
+ value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty)
+}
+
+fn is_empty_vec(value: &[String]) -> bool {
+ // This works because empty iterators return true
+ value.iter().all(String::is_empty)
+}
+
+declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
+
+impl LateLintPass<'_, '_> for CargoCommonMetadata {
+ fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
+ if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
+ return;
+ }
+
+ let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
+ metadata
+ } else {
+ warning(cx, "could not read cargo metadata");
+ return;
+ };
+
+ for package in metadata.packages {
+ if is_empty_vec(&package.authors) {
+ missing_warning(cx, &package, "package.authors");
+ }
+
+ if is_empty_str(&package.description) {
+ missing_warning(cx, &package, "package.description");
+ }
+
+ if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
+ missing_warning(cx, &package, "either package.license or package.license_file");
+ }
+
+ if is_empty_str(&package.repository) {
+ missing_warning(cx, &package, "package.repository");
+ }
+
+ if is_empty_path(&package.readme) {
+ missing_warning(cx, &package, "package.readme");
+ }
+
+ if is_empty_vec(&package.keywords) {
+ missing_warning(cx, &package, "package.keywords");
+ }
+
+ if is_empty_vec(&package.categories) {
+ missing_warning(cx, &package, "package.categories");
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
new file mode 100644
index 00000000000..d9776dd50a8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -0,0 +1,345 @@
+//! lint on manually implemented checked conversions that could be transformed into `try_from`
+
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for explicit bounds checking when casting.
+ ///
+ /// **Why is this bad?** Reduces the readability of statements & is error prone.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # let foo: u32 = 5;
+ /// # let _ =
+ /// foo <= i32::MAX as u32
+ /// # ;
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```rust
+ /// # use std::convert::TryFrom;
+ /// # let foo = 1;
+ /// # let _ =
+ /// i32::try_from(foo).is_ok()
+ /// # ;
+ /// ```
+ pub CHECKED_CONVERSIONS,
+ pedantic,
+ "`try_from` could replace manual bounds checking when casting"
+}
+
+declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions {
+ fn check_expr(&mut self, cx: &LateContext<'_, '_>, item: &Expr<'_>) {
+ let result = if_chain! {
+ if !in_external_macro(cx.sess(), item.span);
+ if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
+
+ then {
+ match op.node {
+ BinOpKind::Ge | BinOpKind::Le => single_check(item),
+ BinOpKind::And => double_check(cx, left, right),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ };
+
+ if_chain! {
+ if let Some(cv) = result;
+ if let Some(to_type) = cv.to_type;
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut
+ applicability);
+ span_lint_and_sugg(
+ cx,
+ CHECKED_CONVERSIONS,
+ item.span,
+ "Checked cast can be simplified.",
+ "try",
+ format!("{}::try_from({}).is_ok()",
+ to_type,
+ snippet),
+ applicability
+ );
+ }
+ }
+ }
+}
+
+/// Searches for a single check from unsigned to _ is done
+/// todo: check for case signed -> larger unsigned == only x >= 0
+fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> {
+ check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
+}
+
+/// Searches for a combination of upper & lower bound checks
+fn double_check<'a>(cx: &LateContext<'_, '_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option> {
+ let upper_lower = |l, r| {
+ let upper = check_upper_bound(l);
+ let lower = check_lower_bound(r);
+
+ transpose(upper, lower).and_then(|(l, r)| l.combine(r, cx))
+ };
+
+ upper_lower(left, right).or_else(|| upper_lower(right, left))
+}
+
+/// Contains the result of a tried conversion check
+#[derive(Clone, Debug)]
+struct Conversion<'a> {
+ cvt: ConversionType,
+ expr_to_cast: &'a Expr<'a>,
+ to_type: Option<&'a str>,
+}
+
+/// The kind of conversion that is checked
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum ConversionType {
+ SignedToUnsigned,
+ SignedToSigned,
+ FromUnsigned,
+}
+
+impl<'a> Conversion<'a> {
+ /// Combine multiple conversions if the are compatible
+ pub fn combine(self, other: Self, cx: &LateContext<'_, '_>) -> Option> {
+ if self.is_compatible(&other, cx) {
+ // Prefer a Conversion that contains a type-constraint
+ Some(if self.to_type.is_some() { self } else { other })
+ } else {
+ None
+ }
+ }
+
+ /// Checks if two conversions are compatible
+ /// same type of conversion, same 'castee' and same 'to type'
+ pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_, '_>) -> bool {
+ (self.cvt == other.cvt)
+ && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
+ && (self.has_compatible_to_type(other))
+ }
+
+ /// Checks if the to-type is the same (if there is a type constraint)
+ fn has_compatible_to_type(&self, other: &Self) -> bool {
+ transpose(self.to_type.as_ref(), other.to_type.as_ref()).map_or(true, |(l, r)| l == r)
+ }
+
+ /// Try to construct a new conversion if the conversion type is valid
+ fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option> {
+ ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
+ cvt,
+ expr_to_cast,
+ to_type: Some(to_type),
+ })
+ }
+
+ /// Construct a new conversion without type constraint
+ fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> {
+ Conversion {
+ cvt: ConversionType::SignedToUnsigned,
+ expr_to_cast,
+ to_type: None,
+ }
+ }
+}
+
+impl ConversionType {
+ /// Creates a conversion type if the type is allowed & conversion is valid
+ #[must_use]
+ fn try_new(from: &str, to: &str) -> Option {
+ if UINTS.contains(&from) {
+ Some(Self::FromUnsigned)
+ } else if SINTS.contains(&from) {
+ if UINTS.contains(&to) {
+ Some(Self::SignedToUnsigned)
+ } else if SINTS.contains(&to) {
+ Some(Self::SignedToSigned)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ }
+}
+
+/// Check for `expr <= (to_type::MAX as from_type)`
+fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> {
+ if_chain! {
+ if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind;
+ if let Some((candidate, check)) = normalize_le_ge(op, left, right);
+ if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS);
+
+ then {
+ Conversion::try_new(candidate, from, to)
+ } else {
+ None
+ }
+ }
+}
+
+/// Check for `expr >= 0|(to_type::MIN as from_type)`
+fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> {
+ fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option> {
+ (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
+ }
+
+ // First of we need a binary containing the expression & the cast
+ if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind {
+ normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
+ } else {
+ None
+ }
+}
+
+/// Check for `expr >= 0`
+fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> {
+ if_chain! {
+ if let ExprKind::Lit(ref lit) = &check.kind;
+ if let LitKind::Int(0, _) = &lit.node;
+
+ then {
+ Some(Conversion::new_any(candidate))
+ } else {
+ None
+ }
+ }
+}
+
+/// Check for `expr >= (to_type::MIN as from_type)`
+fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> {
+ if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) {
+ Conversion::try_new(candidate, from, to)
+ } else {
+ None
+ }
+}
+
+/// Tries to extract the from- and to-type from a cast expression
+fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> {
+ // `to_type::maxmin_value() as from_type`
+ let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
+ // to_type::maxmin_value(), from_type
+ if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind;
+ if let TyKind::Path(ref from_type_path) = &from_type.kind;
+ if let Some(from_sym) = int_ty_to_sym(from_type_path);
+
+ then {
+ Some((limit, from_sym))
+ } else {
+ None
+ }
+ };
+
+ // `from_type::from(to_type::maxmin_value())`
+ let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
+ if_chain! {
+ // `from_type::from, to_type::maxmin_value()`
+ if let ExprKind::Call(ref from_func, ref args) = &expr.kind;
+ // `to_type::maxmin_value()`
+ if args.len() == 1;
+ if let limit = &args[0];
+ // `from_type::from`
+ if let ExprKind::Path(ref path) = &from_func.kind;
+ if let Some(from_sym) = get_implementing_type(path, INTS, FROM);
+
+ then {
+ Some((limit, from_sym))
+ } else {
+ None
+ }
+ }
+ });
+
+ if let Some((limit, from_type)) = limit_from {
+ if_chain! {
+ if let ExprKind::Call(ref fun_name, _) = &limit.kind;
+ // `to_type, maxmin_value`
+ if let ExprKind::Path(ref path) = &fun_name.kind;
+ // `to_type`
+ if let Some(to_type) = get_implementing_type(path, types, func);
+
+ then {
+ Some((from_type, to_type))
+ } else {
+ None
+ }
+ }
+ } else {
+ None
+ }
+}
+
+/// Gets the type which implements the called function
+fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
+ if_chain! {
+ if let QPath::TypeRelative(ref ty, ref path) = &path;
+ if path.ident.name.as_str() == function;
+ if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind;
+ if let [int] = &*tp.segments;
+ let name = &int.ident.name.as_str();
+
+ then {
+ candidates.iter().find(|c| name == *c).cloned()
+ } else {
+ None
+ }
+ }
+}
+
+/// Gets the type as a string, if it is a supported integer
+fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
+ if_chain! {
+ if let QPath::Resolved(_, ref path) = *path;
+ if let [ty] = &*path.segments;
+ let name = &ty.ident.name.as_str();
+
+ then {
+ INTS.iter().find(|c| name == *c).cloned()
+ } else {
+ None
+ }
+ }
+}
+
+/// (Option, Option) -> Option<(T, U)>
+fn transpose(lhs: Option, rhs: Option) -> Option<(T, U)> {
+ match (lhs, rhs) {
+ (Some(l), Some(r)) => Some((l, r)),
+ _ => None,
+ }
+}
+
+/// Will return the expressions as if they were expr1 <= expr2
+fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
+ match op.node {
+ BinOpKind::Le => Some((left, right)),
+ BinOpKind::Ge => Some((right, left)),
+ _ => None,
+ }
+}
+
+// Constants
+const FROM: &str = "from";
+const MAX_VALUE: &str = "max_value";
+const MIN_VALUE: &str = "min_value";
+
+const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
+const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
+const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
new file mode 100644
index 00000000000..3ba72e84fa8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -0,0 +1,163 @@
+//! calculate cognitive complexity and warn about overly complex functions
+
+use rustc_ast::ast::Attribute;
+use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+use rustc_span::BytePos;
+
+use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for methods with high cognitive complexity.
+ ///
+ /// **Why is this bad?** Methods of high cognitive complexity tend to be hard to
+ /// both read and maintain. Also LLVM will tend to optimize small methods better.
+ ///
+ /// **Known problems:** Sometimes it's hard to find a way to reduce the
+ /// complexity.
+ ///
+ /// **Example:** No. You'll see it when you get the warning.
+ pub COGNITIVE_COMPLEXITY,
+ nursery,
+ "functions that should be split up into multiple functions"
+}
+
+pub struct CognitiveComplexity {
+ limit: LimitStack,
+}
+
+impl CognitiveComplexity {
+ #[must_use]
+ pub fn new(limit: u64) -> Self {
+ Self {
+ limit: LimitStack::new(limit),
+ }
+ }
+}
+
+impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
+
+impl CognitiveComplexity {
+ #[allow(clippy::cast_possible_truncation)]
+ fn check<'a, 'tcx>(
+ &mut self,
+ cx: &'a LateContext<'a, 'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ body_span: Span,
+ ) {
+ if body_span.from_expansion() {
+ return;
+ }
+
+ let expr = &body.value;
+
+ let mut helper = CCHelper { cc: 1, returns: 0 };
+ helper.visit_expr(expr);
+ let CCHelper { cc, returns } = helper;
+ let ret_ty = cx.tables.node_type(expr.hir_id);
+ let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) {
+ returns
+ } else {
+ #[allow(clippy::integer_division)]
+ (returns / 2)
+ };
+
+ let mut rust_cc = cc;
+ // prevent degenerate cases where unreachable code contains `return` statements
+ if rust_cc >= ret_adjust {
+ rust_cc -= ret_adjust;
+ }
+
+ if rust_cc > self.limit.limit() {
+ let fn_span = match kind {
+ FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span,
+ FnKind::Closure(_) => {
+ let header_span = body_span.with_hi(decl.output.span().lo());
+ let pos = snippet_opt(cx, header_span).and_then(|snip| {
+ let low_offset = snip.find('|')?;
+ let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
+ let low = header_span.lo() + BytePos(low_offset as u32);
+ let high = low + BytePos(high_offset as u32 + 1);
+
+ Some((low, high))
+ });
+
+ if let Some((low, high)) = pos {
+ Span::new(low, high, header_span.ctxt())
+ } else {
+ return;
+ }
+ },
+ };
+
+ span_lint_and_help(
+ cx,
+ COGNITIVE_COMPLEXITY,
+ fn_span,
+ &format!(
+ "the function has a cognitive complexity of ({}/{})",
+ rust_cc,
+ self.limit.limit()
+ ),
+ None,
+ "you could split it up into multiple smaller functions",
+ );
+ }
+ }
+}
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'a, 'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ hir_id: HirId,
+ ) {
+ let def_id = cx.tcx.hir().local_def_id(hir_id);
+ if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) {
+ self.check(cx, kind, decl, body, span);
+ }
+ }
+
+ fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
+ self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity");
+ }
+ fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
+ self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
+ }
+}
+
+struct CCHelper {
+ cc: u64,
+ returns: u64,
+}
+
+impl<'tcx> Visitor<'tcx> for CCHelper {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ walk_expr(self, e);
+ match e.kind {
+ ExprKind::Match(_, ref arms, _) => {
+ if arms.len() > 1 {
+ self.cc += 1;
+ }
+ self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
+ },
+ ExprKind::Ret(_) => self.returns += 1,
+ _ => {},
+ }
+ }
+ fn nested_visit_map(&mut self) -> NestedVisitorMap {
+ NestedVisitorMap::None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
new file mode 100644
index 00000000000..8090f4673aa
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -0,0 +1,170 @@
+//! Checks for if expressions that contain only an if expression.
+//!
+//! For example, the lint would catch:
+//!
+//! ```rust,ignore
+//! if x {
+//! if y {
+//! println!("Hello world");
+//! }
+//! }
+//! ```
+//!
+//! This lint is **warn** by default
+
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+use crate::utils::sugg::Sugg;
+use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then};
+use rustc_errors::Applicability;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for nested `if` statements which can be collapsed
+ /// by `&&`-combining their conditions and for `else { if ... }` expressions
+ /// that
+ /// can be collapsed to `else if ...`.
+ ///
+ /// **Why is this bad?** Each `if`-statement adds one level of nesting, which
+ /// makes code look more complex than it really is.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// if x {
+ /// if y {
+ /// …
+ /// }
+ /// }
+ ///
+ /// // or
+ ///
+ /// if x {
+ /// …
+ /// } else {
+ /// if y {
+ /// …
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Should be written:
+ ///
+ /// ```rust.ignore
+ /// if x && y {
+ /// …
+ /// }
+ ///
+ /// // or
+ ///
+ /// if x {
+ /// …
+ /// } else if y {
+ /// …
+ /// }
+ /// ```
+ pub COLLAPSIBLE_IF,
+ style,
+ "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
+}
+
+declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]);
+
+impl EarlyLintPass for CollapsibleIf {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+ if !expr.span.from_expansion() {
+ check_if(cx, expr)
+ }
+ }
+}
+
+fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
+ if let ast::ExprKind::If(check, then, else_) = &expr.kind {
+ if let Some(else_) = else_ {
+ check_collapsible_maybe_if_let(cx, else_);
+ } else if let ast::ExprKind::Let(..) = check.kind {
+ // Prevent triggering on `if let a = b { if c { .. } }`.
+ } else {
+ check_collapsible_no_if_let(cx, expr, check, then);
+ }
+ }
+}
+
+fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
+ // We trim all opening braces and whitespaces and then check if the next string is a comment.
+ let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
+ .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
+ .to_owned();
+ trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
+}
+
+fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
+ if_chain! {
+ if let ast::ExprKind::Block(ref block, _) = else_.kind;
+ if !block_starts_with_comment(cx, block);
+ if let Some(else_) = expr_block(block);
+ if !else_.span.from_expansion();
+ if let ast::ExprKind::If(..) = else_.kind;
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ COLLAPSIBLE_IF,
+ block.span,
+ "this `else { if .. }` block can be collapsed",
+ "try",
+ snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
+ applicability,
+ );
+ }
+ }
+}
+
+fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
+ if_chain! {
+ if !block_starts_with_comment(cx, then);
+ if let Some(inner) = expr_block(then);
+ if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
+ then {
+ if let ast::ExprKind::Let(..) = check_inner.kind {
+ // Prevent triggering on `if c { if let a = b { .. } }`.
+ return;
+ }
+
+ if expr.span.ctxt() != inner.span.ctxt() {
+ return;
+ }
+ span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
+ let lhs = Sugg::ast(cx, check, "..");
+ let rhs = Sugg::ast(cx, check_inner, "..");
+ diag.span_suggestion(
+ expr.span,
+ "try",
+ format!(
+ "if {} {}",
+ lhs.and(&rhs),
+ snippet_block(cx, content.span, "..", Some(expr.span)),
+ ),
+ Applicability::MachineApplicable, // snippet
+ );
+ });
+ }
+ }
+}
+
+/// If the block contains only one expression, return it.
+fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
+ let mut it = block.stmts.iter();
+
+ if let (Some(stmt), None) = (it.next(), it.next()) {
+ match stmt.kind {
+ ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
new file mode 100644
index 00000000000..96df3ffe3ce
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -0,0 +1,118 @@
+use crate::utils::{
+ get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq,
+};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks comparison chains written with `if` that can be
+ /// rewritten with `match` and `cmp`.
+ ///
+ /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
+ /// repetitive
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// # fn a() {}
+ /// # fn b() {}
+ /// # fn c() {}
+ /// fn f(x: u8, y: u8) {
+ /// if x > y {
+ /// a()
+ /// } else if x < y {
+ /// b()
+ /// } else {
+ /// c()
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```rust,ignore
+ /// use std::cmp::Ordering;
+ /// # fn a() {}
+ /// # fn b() {}
+ /// # fn c() {}
+ /// fn f(x: u8, y: u8) {
+ /// match x.cmp(&y) {
+ /// Ordering::Greater => a(),
+ /// Ordering::Less => b(),
+ /// Ordering::Equal => c()
+ /// }
+ /// }
+ /// ```
+ pub COMPARISON_CHAIN,
+ style,
+ "`if`s that can be rewritten with `match` and `cmp`"
+}
+
+declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ // We only care about the top-most `if` in the chain
+ if parent_node_is_if_expr(expr, cx) {
+ return;
+ }
+
+ // Check that there exists at least one explicit else condition
+ let (conds, _) = if_sequence(expr);
+ if conds.len() < 2 {
+ return;
+ }
+
+ for cond in conds.windows(2) {
+ if let (
+ &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1),
+ &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2),
+ ) = (&cond[0].kind, &cond[1].kind)
+ {
+ if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
+ return;
+ }
+
+ // Check that both sets of operands are equal
+ let mut spanless_eq = SpanlessEq::new(cx);
+ if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2))
+ && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2))
+ {
+ return;
+ }
+
+ // Check that the type being compared implements `core::cmp::Ord`
+ let ty = cx.tables.expr_ty(lhs1);
+ let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
+
+ if !is_ord {
+ return;
+ }
+ } else {
+ // We only care about comparison chains
+ return;
+ }
+ }
+ span_lint_and_help(
+ cx,
+ COMPARISON_CHAIN,
+ expr.span,
+ "`if` chain can be rewritten with `match`",
+ None,
+ "Consider rewriting the `if` chain to use `cmp` and `match`.",
+ )
+ }
+}
+
+fn kind_is_cmp(kind: BinOpKind) -> bool {
+ match kind {
+ BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
+ _ => false,
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/consts.rs b/src/tools/clippy/clippy_lints/src/consts.rs
new file mode 100644
index 00000000000..81ddc8c0067
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/consts.rs
@@ -0,0 +1,559 @@
+#![allow(clippy::float_cmp)]
+
+use crate::utils::{clip, higher, sext, unsext};
+use if_chain::if_chain;
+use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
+use rustc_data_structures::sync::Lrc;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty::subst::{Subst, SubstsRef};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_span::symbol::Symbol;
+use std::cmp::Ordering::{self, Equal};
+use std::convert::TryInto;
+use std::hash::{Hash, Hasher};
+
+/// A `LitKind`-like enum to fold constant `Expr`s into.
+#[derive(Debug, Clone)]
+pub enum Constant {
+ /// A `String` (e.g., "abc").
+ Str(String),
+ /// A binary string (e.g., `b"abc"`).
+ Binary(Lrc>),
+ /// A single `char` (e.g., `'a'`).
+ Char(char),
+ /// An integer's bit representation.
+ Int(u128),
+ /// An `f32`.
+ F32(f32),
+ /// An `f64`.
+ F64(f64),
+ /// `true` or `false`.
+ Bool(bool),
+ /// An array of constants.
+ Vec(Vec),
+ /// Also an array, but with only one constant, repeated N times.
+ Repeat(Box, u64),
+ /// A tuple of constants.
+ Tuple(Vec),
+ /// A raw pointer.
+ RawPtr(u128),
+ /// A literal with syntax error.
+ Err(Symbol),
+}
+
+impl PartialEq for Constant {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
+ (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
+ (&Self::Char(l), &Self::Char(r)) => l == r,
+ (&Self::Int(l), &Self::Int(r)) => l == r,
+ (&Self::F64(l), &Self::F64(r)) => {
+ // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+ // `Fw32 == Fw64`, so don’t compare them.
+ // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+ l.to_bits() == r.to_bits()
+ },
+ (&Self::F32(l), &Self::F32(r)) => {
+ // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+ // `Fw32 == Fw64`, so don’t compare them.
+ // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+ f64::from(l).to_bits() == f64::from(r).to_bits()
+ },
+ (&Self::Bool(l), &Self::Bool(r)) => l == r,
+ (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
+ (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
+ // TODO: are there inter-type equalities?
+ _ => false,
+ }
+ }
+}
+
+impl Hash for Constant {
+ fn hash(&self, state: &mut H)
+ where
+ H: Hasher,
+ {
+ std::mem::discriminant(self).hash(state);
+ match *self {
+ Self::Str(ref s) => {
+ s.hash(state);
+ },
+ Self::Binary(ref b) => {
+ b.hash(state);
+ },
+ Self::Char(c) => {
+ c.hash(state);
+ },
+ Self::Int(i) => {
+ i.hash(state);
+ },
+ Self::F32(f) => {
+ f64::from(f).to_bits().hash(state);
+ },
+ Self::F64(f) => {
+ f.to_bits().hash(state);
+ },
+ Self::Bool(b) => {
+ b.hash(state);
+ },
+ Self::Vec(ref v) | Self::Tuple(ref v) => {
+ v.hash(state);
+ },
+ Self::Repeat(ref c, l) => {
+ c.hash(state);
+ l.hash(state);
+ },
+ Self::RawPtr(u) => {
+ u.hash(state);
+ },
+ Self::Err(ref s) => {
+ s.hash(state);
+ },
+ }
+ }
+}
+
+impl Constant {
+ pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option {
+ match (left, right) {
+ (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
+ (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
+ (&Self::Int(l), &Self::Int(r)) => {
+ if let ty::Int(int_ty) = cmp_type.kind {
+ Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
+ } else {
+ Some(l.cmp(&r))
+ }
+ },
+ (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
+ (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
+ (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
+ (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
+ .iter()
+ .zip(r.iter())
+ .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
+ .find(|r| r.map_or(true, |o| o != Ordering::Equal))
+ .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
+ (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
+ match Self::partial_cmp(tcx, cmp_type, lv, rv) {
+ Some(Equal) => Some(ls.cmp(rs)),
+ x => x,
+ }
+ },
+ // TODO: are there any useful inter-type orderings?
+ _ => None,
+ }
+ }
+}
+
+/// Parses a `LitKind` to a `Constant`.
+pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant {
+ match *lit {
+ LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
+ LitKind::Byte(b) => Constant::Int(u128::from(b)),
+ LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
+ LitKind::Char(c) => Constant::Char(c),
+ LitKind::Int(n, _) => Constant::Int(n),
+ LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
+ FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
+ FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
+ },
+ LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind {
+ ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
+ ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
+ _ => bug!(),
+ },
+ LitKind::Bool(b) => Constant::Bool(b),
+ LitKind::Err(s) => Constant::Err(s),
+ }
+}
+
+pub fn constant<'c, 'cc>(
+ lcx: &LateContext<'c, 'cc>,
+ tables: &'c ty::TypeckTables<'cc>,
+ e: &Expr<'_>,
+) -> Option<(Constant, bool)> {
+ let mut cx = ConstEvalLateContext {
+ lcx,
+ tables,
+ param_env: lcx.param_env,
+ needed_resolution: false,
+ substs: lcx.tcx.intern_substs(&[]),
+ };
+ cx.expr(e).map(|cst| (cst, cx.needed_resolution))
+}
+
+pub fn constant_simple<'c, 'cc>(
+ lcx: &LateContext<'c, 'cc>,
+ tables: &'c ty::TypeckTables<'cc>,
+ e: &Expr<'_>,
+) -> Option {
+ constant(lcx, tables, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
+}
+
+/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`.
+pub fn constant_context<'c, 'cc>(
+ lcx: &'c LateContext<'c, 'cc>,
+ tables: &'c ty::TypeckTables<'cc>,
+) -> ConstEvalLateContext<'c, 'cc> {
+ ConstEvalLateContext {
+ lcx,
+ tables,
+ param_env: lcx.param_env,
+ needed_resolution: false,
+ substs: lcx.tcx.intern_substs(&[]),
+ }
+}
+
+pub struct ConstEvalLateContext<'a, 'tcx> {
+ lcx: &'a LateContext<'a, 'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ needed_resolution: bool,
+ substs: SubstsRef<'tcx>,
+}
+
+impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
+ /// Simple constant folding: Insert an expression, get a constant or none.
+ pub fn expr(&mut self, e: &Expr<'_>) -> Option {
+ if let Some((ref cond, ref then, otherwise)) = higher::if_block(&e) {
+ return self.ifthenelse(cond, then, otherwise);
+ }
+ match e.kind {
+ ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.tables.expr_ty(e)),
+ ExprKind::Block(ref block, _) => self.block(block),
+ ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.tables.expr_ty_opt(e))),
+ ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
+ ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
+ ExprKind::Repeat(ref value, _) => {
+ let n = match self.tables.expr_ty(e).kind {
+ ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
+ _ => span_bug!(e.span, "typeck error"),
+ };
+ self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
+ },
+ ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
+ UnOp::UnNot => self.constant_not(&o, self.tables.expr_ty(e)),
+ UnOp::UnNeg => self.constant_negate(&o, self.tables.expr_ty(e)),
+ UnOp::UnDeref => Some(o),
+ }),
+ ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
+ ExprKind::Call(ref callee, ref args) => {
+ // We only handle a few const functions for now.
+ if_chain! {
+ if args.is_empty();
+ if let ExprKind::Path(qpath) = &callee.kind;
+ let res = self.tables.qpath_res(qpath, callee.hir_id);
+ if let Some(def_id) = res.opt_def_id();
+ let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
+ let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
+ if let ["core", "num", int_impl, "max_value"] = *def_path;
+ then {
+ let value = match int_impl {
+ "" => i8::max_value() as u128,
+ "" => i16::max_value() as u128,
+ "" => i32::max_value() as u128,
+ "" => i64::max_value() as u128,
+ "" => i128::max_value() as u128,
+ _ => return None,
+ };
+ Some(Constant::Int(value))
+ }
+ else {
+ None
+ }
+ }
+ },
+ ExprKind::Index(ref arr, ref index) => self.index(arr, index),
+ // TODO: add other expressions.
+ _ => None,
+ }
+ }
+
+ #[allow(clippy::cast_possible_wrap)]
+ fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option {
+ use self::Constant::{Bool, Int};
+ match *o {
+ Bool(b) => Some(Bool(!b)),
+ Int(value) => {
+ let value = !value;
+ match ty.kind {
+ ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
+ ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+ }
+
+ fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option {
+ use self::Constant::{Int, F32, F64};
+ match *o {
+ Int(value) => {
+ let ity = match ty.kind {
+ ty::Int(ity) => ity,
+ _ => return None,
+ };
+ // sign extend
+ let value = sext(self.lcx.tcx, value, ity);
+ let value = value.checked_neg()?;
+ // clear unused bits
+ Some(Int(unsext(self.lcx.tcx, value, ity)))
+ },
+ F32(f) => Some(F32(-f)),
+ F64(f) => Some(F64(-f)),
+ _ => None,
+ }
+ }
+
+ /// Create `Some(Vec![..])` of all constants, unless there is any
+ /// non-constant part.
+ fn multi(&mut self, vec: &[Expr<'_>]) -> Option> {
+ vec.iter().map(|elem| self.expr(elem)).collect::