mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit 'd7b5cbf065b88830ca519adcb73fad4c0d24b1c7' into clippyup
This commit is contained in:
parent
bd071bf5b2
commit
f8f9d01c2a
199 changed files with 4158 additions and 1931 deletions
19
.github/workflows/clippy_bors.yml
vendored
19
.github/workflows/clippy_bors.yml
vendored
|
@ -143,6 +143,25 @@ jobs:
|
|||
env:
|
||||
OS: ${{ runner.os }}
|
||||
|
||||
metadata_collection:
|
||||
needs: base
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
- name: Test metadata collection
|
||||
run: cargo collect-metadata
|
||||
|
||||
integration_build:
|
||||
needs: changelog
|
||||
runs-on: ubuntu-latest
|
||||
|
|
9
.github/workflows/remark.yml
vendored
9
.github/workflows/remark.yml
vendored
|
@ -26,10 +26,19 @@ jobs:
|
|||
- name: Install remark
|
||||
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
|
||||
|
||||
- name: Install mdbook
|
||||
run: |
|
||||
mkdir mdbook
|
||||
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.18/mdbook-v0.4.18-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||
echo `pwd`/mdbook >> $GITHUB_PATH
|
||||
|
||||
# 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
|
||||
|
||||
- name: Build mdbook
|
||||
run: mdbook build book
|
||||
|
||||
# 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.
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -39,3 +39,6 @@ helper.txt
|
|||
*.iml
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# mdbook generated output
|
||||
/book/book
|
||||
|
|
|
@ -3605,6 +3605,7 @@ Released 2018-09-13
|
|||
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
||||
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
|
||||
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
|
||||
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
|
||||
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
|
||||
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
|
||||
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
|
||||
|
@ -3677,6 +3678,7 @@ Released 2018-09-13
|
|||
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
|
||||
[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
|
||||
[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
|
||||
[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
|
||||
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
|
||||
[`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
|
||||
|
|
168
CONTRIBUTING.md
168
CONTRIBUTING.md
|
@ -13,19 +13,14 @@ anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip]
|
|||
All contributors are expected to follow the [Rust Code of Conduct].
|
||||
|
||||
- [Contributing to Clippy](#contributing-to-clippy)
|
||||
- [Getting started](#getting-started)
|
||||
- [High level approach](#high-level-approach)
|
||||
- [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
- [The Clippy book](#the-clippy-book)
|
||||
- [High level approach](#high-level-approach)
|
||||
- [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
- [Writing code](#writing-code)
|
||||
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
|
||||
- [IntelliJ Rust](#intellij-rust)
|
||||
- [Rust Analyzer](#rust-analyzer)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
|
||||
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
|
||||
- [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
|
||||
- [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
|
||||
- [Defining remotes](#defining-remotes)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
- [Contributions](#contributions)
|
||||
|
@ -33,24 +28,29 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
|||
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
|
||||
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
|
||||
|
||||
## Getting started
|
||||
## The Clippy book
|
||||
|
||||
**Note: If this is your first time contributing to Clippy, you should
|
||||
first read the [Basics docs](doc/basics.md).**
|
||||
If you're new to Clippy and don't know where to start the [Clippy book] includes
|
||||
a developer guide and is a good place to start your journey.
|
||||
|
||||
### High level approach
|
||||
<!-- FIXME: Link to the deployed book, once it is deployed through CI -->
|
||||
[Clippy book]: book/src
|
||||
|
||||
## High level approach
|
||||
|
||||
1. Find something to fix/improve
|
||||
2. Change code (likely some file in `clippy_lints/src/`)
|
||||
3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
|
||||
3. Follow the instructions in the [Basics docs](book/src/development/basics.md)
|
||||
to get set up
|
||||
4. Run `cargo test` in the root directory and wiggle code until it passes
|
||||
5. Open a PR (also can be done after 2. if you run into problems)
|
||||
|
||||
### Finding something to fix/improve
|
||||
## Finding something to fix/improve
|
||||
|
||||
All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch
|
||||
or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date.
|
||||
All currently active mentors can be found [here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
|
||||
All issues on Clippy are mentored, if you want help simply ask someone from the
|
||||
Clippy team directly by mentioning them in the issue or over on [Zulip]. All
|
||||
currently active team members can be found
|
||||
[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
|
||||
|
||||
Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
|
||||
issues. You can use `@rustbot claim` to assign the issue to yourself.
|
||||
|
@ -91,20 +91,6 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us
|
|||
[let chains]: https://github.com/rust-lang/rust/pull/94927
|
||||
[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159
|
||||
|
||||
## Writing code
|
||||
|
||||
Have a look at the [docs for writing lints][adding_lints] for more details.
|
||||
|
||||
If you want to add a new lint or change existing ones apart from bugfixing, it's
|
||||
also a good idea to give the [stability guarantees][rfc_stability] and
|
||||
[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
|
||||
quick read.
|
||||
|
||||
[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
|
||||
[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md
|
||||
[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
|
||||
[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
|
||||
|
||||
## Getting code-completion for rustc internals to work
|
||||
|
||||
### IntelliJ Rust
|
||||
|
@ -205,126 +191,6 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun
|
|||
[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
|
||||
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
|
||||
|
||||
## Syncing changes between Clippy and [`rust-lang/rust`]
|
||||
|
||||
Clippy currently gets built with a pinned nightly version.
|
||||
|
||||
In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
|
||||
that compiler hackers modify from time to time to adapt to changes in the unstable
|
||||
API of the compiler.
|
||||
|
||||
We need to sync these changes back to this repository periodically, and the changes
|
||||
made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
|
||||
|
||||
To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
|
||||
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
|
||||
the Rust stable release and then every other week. That way we guarantee that we keep
|
||||
this repo up to date with the latest compiler API, and every feature in Clippy is available
|
||||
for 2 weeks in nightly, before it can get to beta. For reference, the first sync
|
||||
following this cadence was performed the 2020-08-27.
|
||||
|
||||
This process is described in detail in the following sections. For general information
|
||||
about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
|
||||
|
||||
### Patching git-subtree to work with big repos
|
||||
|
||||
Currently, there's a bug in `git-subtree` that prevents it from working properly
|
||||
with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
|
||||
Before continuing with the following steps, we need to manually apply that fix to
|
||||
our local copy of `git-subtree`.
|
||||
|
||||
You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
|
||||
Put this file under `/usr/lib/git-core` (taking a backup of the previous file)
|
||||
and make sure it has the proper permissions:
|
||||
|
||||
```bash
|
||||
sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
|
||||
sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
```
|
||||
|
||||
_Note:_ The first time running `git subtree push` a cache has to be built. This
|
||||
involves going through the complete Clippy history once. For this you have to
|
||||
increase the stack limit though, which you can do with `ulimit -s 60000`.
|
||||
Make sure to run the `ulimit` command from the same session you call git subtree.
|
||||
|
||||
_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`.
|
||||
This shell has a hardcoded recursion limit set to 1000. In order to make this process work,
|
||||
you need to force the script to run `bash` instead. You can do this by editing the first
|
||||
line of the `git-subtree` script and changing `sh` to `bash`.
|
||||
|
||||
### Performing the sync from [`rust-lang/rust`] to Clippy
|
||||
|
||||
Here is a TL;DR version of the sync process (all of the following commands have
|
||||
to be run inside the `rust` directory):
|
||||
|
||||
1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
|
||||
2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
|
||||
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
```bash
|
||||
# Make sure to change `your-github-name` to your github name in the following command. Also be
|
||||
# sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand
|
||||
# because changes cannot be fast forwarded
|
||||
git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
|
||||
```
|
||||
|
||||
_Note:_ This will directly push to the remote repository. You can also push
|
||||
to your local copy by replacing the remote address with `/path/to/rust-clippy`
|
||||
directory.
|
||||
|
||||
_Note:_ Most of the time you have to create a merge commit in the
|
||||
`rust-clippy` repo (this has to be done in the Clippy repo, not in the
|
||||
rust-copy of Clippy):
|
||||
```bash
|
||||
git fetch origin && git fetch upstream
|
||||
git checkout sync-from-rust
|
||||
git merge upstream/master
|
||||
```
|
||||
4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
|
||||
~~annoy~~ ask them in the [Zulip] stream.)
|
||||
|
||||
### Performing the sync from Clippy to [`rust-lang/rust`]
|
||||
|
||||
All of the following commands have to be run inside the `rust` directory.
|
||||
|
||||
1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous
|
||||
section if necessary.
|
||||
|
||||
2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
|
||||
```bash
|
||||
git checkout -b sync-from-clippy
|
||||
git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
|
||||
```
|
||||
3. Open a PR to [`rust-lang/rust`]
|
||||
|
||||
### Defining remotes
|
||||
|
||||
You may want to define remotes, so you don't have to type out the remote
|
||||
addresses on every sync. You can do this with the following commands (these
|
||||
commands still have to be run inside the `rust` directory):
|
||||
|
||||
```bash
|
||||
# Set clippy-upstream remote for pulls
|
||||
$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy
|
||||
# Make sure to not push to the upstream repo
|
||||
$ git remote set-url --push clippy-upstream DISABLED
|
||||
# Set clippy-origin remote to your fork for pushes
|
||||
$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy
|
||||
# Set a local remote
|
||||
$ git remote add clippy-local /path/to/rust-clippy
|
||||
```
|
||||
|
||||
You can then sync with the remote names from above, e.g.:
|
||||
|
||||
```bash
|
||||
$ git subtree push -P src/tools/clippy clippy-local sync-from-rust
|
||||
```
|
||||
|
||||
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
|
||||
[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
|
||||
[`rust-lang/rust`]: https://github.com/rust-lang/rust
|
||||
|
||||
## Issue and PR triage
|
||||
|
||||
Clippy is following the [Rust triage procedure][triage] for issues and pull
|
||||
|
|
|
@ -58,7 +58,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
|
|||
[features]
|
||||
deny-warnings = ["clippy_lints/deny-warnings"]
|
||||
integration = ["tempfile"]
|
||||
internal = ["clippy_lints/internal"]
|
||||
internal = ["clippy_lints/internal", "tempfile"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This package uses #[feature(rustc_private)]
|
||||
|
|
4
book/README.md
Normal file
4
book/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Clippy Book
|
||||
|
||||
This is the source for the Clippy Book. See the
|
||||
[book](src/infrastructure/book.md) for more information.
|
28
book/book.toml
Normal file
28
book/book.toml
Normal file
|
@ -0,0 +1,28 @@
|
|||
[book]
|
||||
authors = ["The Rust Clippy Developers"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Clippy Documentation"
|
||||
|
||||
[rust]
|
||||
edition = "2018"
|
||||
|
||||
[output.html]
|
||||
edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}"
|
||||
git-repository-url = "https://github.com/rust-lang/rust-clippy/tree/master/book"
|
||||
mathjax-support = true
|
||||
site-url = "/rust-clippy/"
|
||||
|
||||
[output.html.playground]
|
||||
editable = true
|
||||
line-numbers = true
|
||||
|
||||
[output.html.search]
|
||||
boost-hierarchy = 2
|
||||
boost-paragraph = 1
|
||||
boost-title = 2
|
||||
expand = true
|
||||
heading-split-level = 2
|
||||
limit-results = 20
|
||||
use-boolean-and = true
|
34
book/src/README.md
Normal file
34
book/src/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 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 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
Lints are divided into categories, each with a default [lint
|
||||
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
|
||||
much Clippy is supposed to ~~annoy~~ help you by changing the lint level by
|
||||
category.
|
||||
|
||||
| Category | Description | Default level |
|
||||
| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
|
||||
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
|
||||
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
|
||||
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
|
||||
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
|
||||
| `clippy::perf` | code that can be written to run faster | **warn** |
|
||||
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
|
||||
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
|
||||
| `clippy::nursery` | new lints that are still under development | allow |
|
||||
| `clippy::cargo` | lints for the cargo manifest | allow | | allow |
|
||||
|
||||
More to come, please [file an
|
||||
issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
|
||||
|
||||
The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also
|
||||
contains "restriction lints", which are for things which are usually not
|
||||
considered "bad", but may be useful to turn on in specific cases. These should
|
||||
be used very selectively, if at all.
|
23
book/src/SUMMARY.md
Normal file
23
book/src/SUMMARY.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Summary
|
||||
|
||||
[Introduction](README.md)
|
||||
|
||||
- [Installation](installation.md)
|
||||
- [Usage](usage.md)
|
||||
- [Configuration](configuration.md)
|
||||
- [Clippy's Lints](lints.md)
|
||||
- [Continuous Integration](continuous_integration/README.md)
|
||||
- [GitHub Actions](continuous_integration/github_actions.md)
|
||||
- [Travis CI](continuous_integration/travis.md)
|
||||
- [Development](development/README.md)
|
||||
- [Basics](development/basics.md)
|
||||
- [Adding Lints](development/adding_lints.md)
|
||||
- [Common Tools](development/common_tools_writing_lints.md)
|
||||
- [Infrastructure](development/infrastructure/README.md)
|
||||
- [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
|
||||
- [Backporting Changes](development/infrastructure/backport.md)
|
||||
- [Updating the Changelog](development/infrastructure/changelog_update.md)
|
||||
- [Release a New Version](development/infrastructure/release.md)
|
||||
- [The Clippy Book](development/infrastructure/book.md)
|
||||
- [Proposals](development/proposals/README.md)
|
||||
- [Roadmap 2021](development/proposals/roadmap-2021.md)
|
92
book/src/configuration.md
Normal file
92
book/src/configuration.md
Normal file
|
@ -0,0 +1,92 @@
|
|||
# Configuring Clippy
|
||||
|
||||
> **Note:** The configuration file is unstable and may be deprecated in the future.
|
||||
|
||||
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
|
||||
basic `variable = value` mapping eg.
|
||||
|
||||
```toml
|
||||
avoid-breaking-exported-api = false
|
||||
blacklisted-names = ["toto", "tata", "titi"]
|
||||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
|
||||
lints can be configured and the meaning of the variables.
|
||||
|
||||
To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
|
||||
environment variable.
|
||||
|
||||
### Allowing/denying lints
|
||||
|
||||
You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
|
||||
|
||||
* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
|
||||
|
||||
* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
|
||||
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false
|
||||
positives.
|
||||
|
||||
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
|
||||
|
||||
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
|
||||
|
||||
Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny`
|
||||
the lint will emit an error, when triggering for your code. An error causes clippy to exit with an error code, so is
|
||||
useful in scripts like CI/CD.
|
||||
|
||||
If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra
|
||||
flags to Clippy during the run:
|
||||
|
||||
To allow `lint_name`, run
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -A clippy::lint_name
|
||||
```
|
||||
|
||||
And to warn on `lint_name`, run
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -W clippy::lint_name
|
||||
```
|
||||
|
||||
This also works with lint groups. For example you can run Clippy with warnings for all lints enabled:
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -W clippy::pedantic
|
||||
```
|
||||
|
||||
If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are
|
||||
interested in:
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
|
||||
```
|
||||
|
||||
### Specifying the minimum supported Rust version
|
||||
|
||||
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
|
||||
minimum supported Rust version (MSRV) in the clippy configuration file.
|
||||
|
||||
```toml
|
||||
msrv = "1.30.0"
|
||||
```
|
||||
|
||||
The MSRV can also be specified as an inner attribute, like below.
|
||||
|
||||
```rust
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.30.0"]
|
||||
|
||||
fn main() {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
|
||||
is equivalent to `msrv = 1.30.0`.
|
||||
|
||||
Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
|
||||
|
||||
Lints that recognize this configuration option can be
|
||||
found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
|
18
book/src/continuous_integration/README.md
Normal file
18
book/src/continuous_integration/README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Continuous Integration
|
||||
|
||||
It is recommended to run Clippy on CI with `-Dwarnings`, so that Clippy lints
|
||||
prevent CI from passing. To enforce errors on warnings on all `cargo` commands
|
||||
not just `cargo clippy`, you can set the env var `RUSTFLAGS="-Dwarnings"`.
|
||||
|
||||
We recommend to use Clippy from the same toolchain, that you use for compiling
|
||||
your crate for maximum compatibility. E.g. if your crate is compiled with the
|
||||
`stable` toolchain, you should also use `stable` Clippy.
|
||||
|
||||
> _Note:_ New Clippy lints are first added to the `nightly` toolchain. If you
|
||||
> want to help with improving Clippy and have CI resources left, please consider
|
||||
> adding a `nightly` Clippy check to your CI and report problems like false
|
||||
> positives back to us. With that we can fix bugs early, before they can get to
|
||||
> stable.
|
||||
|
||||
This chapter will give an overview on how to use Clippy on different popular CI
|
||||
providers.
|
21
book/src/continuous_integration/github_actions.md
Normal file
21
book/src/continuous_integration/github_actions.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# GitHub Actions
|
||||
|
||||
On the GitHub hosted runners, Clippy from the latest stable Rust version comes
|
||||
pre-installed. So all you have to do is to run `cargo clippy`.
|
||||
|
||||
```yml
|
||||
on: push
|
||||
name: Clippy check
|
||||
|
||||
# Make sure CI fails on all warnings, including Clippy lints
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
jobs:
|
||||
clippy_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Run Clippy
|
||||
run: cargo clippy --all-targets --all-features
|
||||
```
|
20
book/src/continuous_integration/travis.md
Normal file
20
book/src/continuous_integration/travis.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# 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.
|
||||
```
|
43
book/src/development/README.md
Normal file
43
book/src/development/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Clippy Development
|
||||
|
||||
Hello fellow Rustacean! If you made it here, you're probably interested in
|
||||
making Clippy better by contributing to it. In that case, welcome to the
|
||||
project!
|
||||
|
||||
> _Note:_ If you're just interested in using Clippy, there's nothing to see from
|
||||
> this point onward and you should return to one of the earlier chapters.
|
||||
|
||||
## Getting started
|
||||
|
||||
If this is your first time contributing to Clippy, you should first read the
|
||||
[Basics docs](basics.md). This will explain the basics on how to get the source
|
||||
code and how to compile and test the code.
|
||||
|
||||
## Writing code
|
||||
|
||||
If you have done the basic setup, it's time to start hacking.
|
||||
|
||||
The [Adding lints](adding_lints.md) chapter is a walk through on how to add a
|
||||
new lint to Clippy. This is also interesting if you just want to fix a lint,
|
||||
because it also covers how to test lints and gives an overview of the bigger
|
||||
picture.
|
||||
|
||||
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. The lint categories are also described [earlier in this
|
||||
book](../lints.md).
|
||||
|
||||
> _Note:_ Some higher level things about contributing to Clippy are still
|
||||
> covered in the [`CONTRIBUTING.md`] document. Some of those will be moved to
|
||||
> the book over time, like:
|
||||
> - Finding something to fix
|
||||
> - IDE setup
|
||||
> - High level overview on how Clippy works
|
||||
> - Triage procedure
|
||||
> - Bors and Homu
|
||||
|
||||
[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
|
||||
[`CONTRIBUTING.md`]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md
|
|
@ -45,9 +45,9 @@ take a look at our [lint naming guidelines][lint_naming]. To get started on this
|
|||
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
|
||||
--category=pedantic` (category will default to nursery if not provided). This
|
||||
command will create two files: `tests/ui/foo_functions.rs` and
|
||||
`clippy_lints/src/foo_functions.rs`, as well as
|
||||
[registering the lint](#lint-registration). For cargo lints, two project
|
||||
hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
|
||||
`clippy_lints/src/foo_functions.rs`, as well as [registering the
|
||||
lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass)
|
||||
will be created by default under `tests/ui-cargo`.
|
||||
|
||||
Next, we'll open up these files and add our lint!
|
||||
|
||||
|
@ -58,8 +58,8 @@ Let's write some tests first that we can execute while we iterate on our lint.
|
|||
Clippy uses UI tests for testing. UI tests check that the output of Clippy is
|
||||
exactly as expected. Each test is just a plain Rust file that contains the code
|
||||
we want to check. The output of Clippy is compared against a `.stderr` file.
|
||||
Note that you don't have to create this file yourself, we'll get to
|
||||
generating the `.stderr` files further down.
|
||||
Note that you don't have to create this file yourself, we'll get to generating
|
||||
the `.stderr` files further down.
|
||||
|
||||
We start by opening the test file created at `tests/ui/foo_functions.rs`.
|
||||
|
||||
|
@ -96,61 +96,57 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
|
||||
currently this test is meaningless though.
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
|
||||
this test is meaningless though.
|
||||
|
||||
While we are working on implementing our lint, we can keep running the UI
|
||||
test. That allows us to check if the output is turning into what we want.
|
||||
While we are working on implementing our lint, we can keep running the UI test.
|
||||
That allows us to check if the output is turning into what we want.
|
||||
|
||||
Once we are satisfied with the output, we need to run
|
||||
`cargo dev bless` to update the `.stderr` file for our lint.
|
||||
Please note that, we should run `TESTNAME=foo_functions cargo uitest`
|
||||
every time before running `cargo dev bless`.
|
||||
Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
|
||||
our lint, we need to commit the generated `.stderr` files, too. In general, you
|
||||
should only commit files changed by `cargo dev bless` for the
|
||||
Once we are satisfied with the output, we need to run `cargo dev bless` to
|
||||
update the `.stderr` file for our lint. Please note that, we should run
|
||||
`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
|
||||
bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
|
||||
commit our lint, we need to commit the generated `.stderr` files, too. In
|
||||
general, you should only commit files changed by `cargo dev bless` for the
|
||||
specific lint you are creating/editing. Note that if the generated files are
|
||||
empty, they should be removed.
|
||||
|
||||
Note that you can run multiple test files by specifying a comma separated list:
|
||||
`TESTNAME=foo_functions,test2,test3`.
|
||||
> _Note:_ you can run multiple test files by specifying a comma separated list:
|
||||
> `TESTNAME=foo_functions,test2,test3`.
|
||||
|
||||
### Cargo lints
|
||||
|
||||
For cargo lints, the process of testing differs in that we are interested in
|
||||
the `Cargo.toml` manifest file. We also need a minimal crate associated
|
||||
with that manifest.
|
||||
For cargo lints, the process of testing differs in that we are interested in the
|
||||
`Cargo.toml` manifest file. We also need a minimal crate associated with that
|
||||
manifest.
|
||||
|
||||
If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
|
||||
we will find by default two new crates, each with its manifest file:
|
||||
If our new lint is named e.g. `foo_categories`, after running `cargo dev
|
||||
new_lint` we will find by default two new crates, each with its manifest file:
|
||||
|
||||
* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
|
||||
* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
|
||||
* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the
|
||||
new lint to raise an error.
|
||||
* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger
|
||||
the lint.
|
||||
|
||||
If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
|
||||
If you need more cases, you can copy one of those crates (under
|
||||
`foo_categories`) and rename it.
|
||||
|
||||
The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
|
||||
variable to `cargo uitest` works too.
|
||||
The process of generating the `.stderr` file is the same, and prepending the
|
||||
`TESTNAME` variable to `cargo uitest` works too.
|
||||
|
||||
## Rustfix tests
|
||||
|
||||
If the lint you are working on is making use of structured suggestions, the
|
||||
test file should include a `// run-rustfix` comment at the top. This will
|
||||
If the lint you are working on is making use of structured suggestions, the test
|
||||
file should include a `// run-rustfix` comment at the top. This will
|
||||
additionally run [rustfix] for that test. Rustfix will apply the suggestions
|
||||
from the lint to the code of the test file and compare that to the contents of
|
||||
a `.fixed` file.
|
||||
from the lint to the code of the test file and compare that to the contents of a
|
||||
`.fixed` file.
|
||||
|
||||
Use `cargo dev bless` to automatically generate the
|
||||
`.fixed` file after running the tests.
|
||||
Use `cargo dev bless` to automatically generate the `.fixed` file after running
|
||||
the tests.
|
||||
|
||||
[rustfix]: https://github.com/rust-lang/rustfix
|
||||
|
||||
## Edition 2018 tests
|
||||
|
||||
Some features require the 2018 edition to work (e.g. `async_await`), but
|
||||
compile-test tests run on the 2015 edition by default. To change this behavior
|
||||
add `// edition:2018` at the top of the test file (note that it's space-sensitive).
|
||||
|
||||
## Testing manually
|
||||
|
||||
Manually testing against an example file can be useful if you have added some
|
||||
|
@ -166,9 +162,9 @@ implementing our lint now.
|
|||
|
||||
## Lint declaration
|
||||
|
||||
Let's start by opening the new file created in the `clippy_lints` crate
|
||||
at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
|
||||
lint code is. This file has already imported some initial things we will need:
|
||||
Let's start by opening the new file created in the `clippy_lints` crate at
|
||||
`clippy_lints/src/foo_functions.rs`. That's the crate where all the lint code
|
||||
is. This file has already imported some initial things we will need:
|
||||
|
||||
```rust
|
||||
use rustc_lint::{EarlyLintPass, EarlyContext};
|
||||
|
@ -178,7 +174,8 @@ use rustc_ast::ast::*;
|
|||
|
||||
The next step is to update the lint declaration. Lints are declared using the
|
||||
[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
|
||||
the auto-generated lint declaration to have a real description, something like this:
|
||||
the auto-generated lint declaration to have a real description, something like
|
||||
this:
|
||||
|
||||
```rust
|
||||
declare_clippy_lint! {
|
||||
|
@ -198,24 +195,25 @@ declare_clippy_lint! {
|
|||
```
|
||||
|
||||
* The section of lines prefixed with `///` constitutes the lint documentation
|
||||
section. This is the default documentation style and will be displayed
|
||||
[like this][example_lint_page]. To render and open this documentation locally
|
||||
in a browser, run `cargo dev serve`.
|
||||
* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
|
||||
The value should be set to the current Rust version that the lint is developed in,
|
||||
it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
|
||||
is listed under *release*. (Use the version without the `-nightly`) suffix.
|
||||
* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
|
||||
[lint naming guidelines][lint_naming] here when naming your lint.
|
||||
In short, the name should state the thing that is being checked for and
|
||||
read well when used with `allow`/`warn`/`deny`.
|
||||
* `pedantic` sets the lint level to `Allow`.
|
||||
The exact mapping can be found [here][category_level_mapping]
|
||||
section. This is the default documentation style and will be displayed [like
|
||||
this][example_lint_page]. To render and open this documentation locally in a
|
||||
browser, run `cargo dev serve`.
|
||||
* The `#[clippy::version]` attribute will be rendered as part of the lint
|
||||
documentation. The value should be set to the current Rust version that the
|
||||
lint is developed in, it can be retrieved by running `rustc -vV` in the
|
||||
rust-clippy directory. The version is listed under *release*. (Use the version
|
||||
without the `-nightly`) suffix.
|
||||
* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming
|
||||
guidelines][lint_naming] here when naming your lint. In short, the name should
|
||||
state the thing that is being checked for and read well when used with
|
||||
`allow`/`warn`/`deny`.
|
||||
* `pedantic` sets the lint level to `Allow`. The exact mapping can be found
|
||||
[here][category_level_mapping]
|
||||
* The last part should be a text that explains what exactly is wrong with the
|
||||
code
|
||||
|
||||
The rest of this file contains an empty implementation for our lint pass,
|
||||
which in this case is `EarlyLintPass` and should look like this:
|
||||
The rest of this file contains an empty implementation for our lint pass, which
|
||||
in this case is `EarlyLintPass` and should look like this:
|
||||
|
||||
```rust
|
||||
// clippy_lints/src/foo_functions.rs
|
||||
|
@ -324,9 +322,9 @@ impl EarlyLintPass for FooFunctions {
|
|||
Running our UI test should now produce output that contains the lint message.
|
||||
|
||||
According to [the rustc-dev-guide], the text should be matter of fact and avoid
|
||||
capitalization and periods, unless multiple sentences are needed.
|
||||
When code or an identifier must appear in a message or label, it should be
|
||||
surrounded with single grave accents \`.
|
||||
capitalization and periods, unless multiple sentences are needed. When code or
|
||||
an identifier must appear in a message or label, it should be surrounded with
|
||||
single grave accents \`.
|
||||
|
||||
[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
|
||||
[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
|
||||
|
@ -382,8 +380,8 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
|
|||
```
|
||||
|
||||
Now we should also run the full test suite with `cargo test`. At this point
|
||||
running `cargo test` should produce the expected output. Remember to run
|
||||
`cargo dev bless` to update the `.stderr` file.
|
||||
running `cargo test` should produce the expected output. Remember to run `cargo
|
||||
dev bless` to update the `.stderr` file.
|
||||
|
||||
`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
|
||||
implementation is not violating any Clippy lints itself.
|
||||
|
@ -397,13 +395,16 @@ pass.
|
|||
|
||||
## Specifying the lint's minimum supported Rust version (MSRV)
|
||||
|
||||
Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
|
||||
using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
|
||||
ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
|
||||
required, just use the one with a lower MSRV.
|
||||
Sometimes a lint makes suggestions that require a certain version of Rust. For
|
||||
example, the `manual_strip` lint suggests using `str::strip_prefix` and
|
||||
`str::strip_suffix` which is only available after Rust 1.45. In such cases, you
|
||||
need to ensure that the MSRV configured for the project is >= the MSRV of the
|
||||
required Rust feature. If multiple features are required, just use the one with
|
||||
a lower MSRV.
|
||||
|
||||
First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
|
||||
accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
|
||||
First, add an MSRV alias for the required feature in
|
||||
[`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be accessed later
|
||||
as `msrvs::STR_STRIP_PREFIX`, for example.
|
||||
|
||||
```rust
|
||||
msrv_aliases! {
|
||||
|
@ -412,8 +413,9 @@ msrv_aliases! {
|
|||
}
|
||||
```
|
||||
|
||||
In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
|
||||
constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
|
||||
In order to access the project-configured MSRV, you need to have an `msrv` field
|
||||
in the LintPass struct, and a constructor to initialize the field. The `msrv`
|
||||
value is passed to the constructor in `clippy_lints/lib.rs`.
|
||||
|
||||
```rust
|
||||
pub struct ManualStrip {
|
||||
|
@ -472,11 +474,10 @@ If you have trouble implementing your lint, there is also the internal `author`
|
|||
lint to generate Clippy code that detects the offending pattern. It does not
|
||||
work for all of the Rust syntax, but can give a good starting point.
|
||||
|
||||
The quickest way to use it, is the
|
||||
[Rust playground: play.rust-lang.org][author_example].
|
||||
Put the code you want to lint into the editor and add the `#[clippy::author]`
|
||||
attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
|
||||
see the generated code in the output below.
|
||||
The quickest way to use it, is the [Rust playground:
|
||||
play.rust-lang.org][author_example]. Put the code you want to lint into the
|
||||
editor and add the `#[clippy::author]` attribute above the item. Then run Clippy
|
||||
via `Tools -> Clippy` and you should see the generated code in the output below.
|
||||
|
||||
[Here][author_example] is an example on the playground.
|
||||
|
||||
|
@ -487,13 +488,15 @@ you are implementing your lint.
|
|||
|
||||
## Print HIR lint
|
||||
|
||||
To implement a lint, it's helpful to first understand the internal representation
|
||||
that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
|
||||
[_High-Level Intermediate Representation (HIR)_] of the item, statement, or
|
||||
expression that the attribute is attached to. To attach the attribute to expressions
|
||||
you often need to enable `#![feature(stmt_expr_attributes)]`.
|
||||
To implement a lint, it's helpful to first understand the internal
|
||||
representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that
|
||||
prints the [_High-Level Intermediate Representation (HIR)_] of the item,
|
||||
statement, or expression that the attribute is attached to. To attach the
|
||||
attribute to expressions you often need to enable
|
||||
`#![feature(stmt_expr_attributes)]`.
|
||||
|
||||
[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
|
||||
[Here][print_hir_example] you can find an example, just select _Tools_ and run
|
||||
_Clippy_.
|
||||
|
||||
[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
|
||||
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
|
||||
|
@ -518,7 +521,7 @@ declare_clippy_lint! {
|
|||
/// ```rust,ignore
|
||||
/// // A short example of code that triggers the lint
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // A short example of improved code that doesn't trigger the lint
|
||||
|
@ -537,9 +540,9 @@ list][lint_list].
|
|||
|
||||
## Running rustfmt
|
||||
|
||||
[Rustfmt] is a tool for formatting Rust code according to style guidelines.
|
||||
Your code has to be formatted by `rustfmt` before a PR can be merged.
|
||||
Clippy uses nightly `rustfmt` in the CI.
|
||||
[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your
|
||||
code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses
|
||||
nightly `rustfmt` in the CI.
|
||||
|
||||
It can be installed via `rustup`:
|
||||
|
||||
|
@ -575,94 +578,105 @@ Before submitting your PR make sure you followed all of the basic requirements:
|
|||
|
||||
## Adding configuration to a lint
|
||||
|
||||
Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
|
||||
directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
|
||||
behavior that can be seen as a false positive for some users. Adding a configuration is done
|
||||
in the following steps:
|
||||
Clippy supports the configuration of lints values using a `clippy.toml` file in
|
||||
the workspace directory. Adding a configuration to a lint can be useful for
|
||||
thresholds or to constrain some behavior that can be seen as a false positive
|
||||
for some users. Adding a configuration is done in the following steps:
|
||||
|
||||
1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
|
||||
like this:
|
||||
```rust
|
||||
/// Lint: LINT_NAME.
|
||||
///
|
||||
/// <The configuration field doc comment>
|
||||
(configuration_ident: Type = DefaultValue),
|
||||
```
|
||||
The doc comment is automatically added to the documentation of the listed lints. The default
|
||||
value will be formatted using the `Debug` implementation of the type.
|
||||
1. Adding a new configuration entry to
|
||||
[clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) like this:
|
||||
|
||||
```rust
|
||||
/// Lint: LINT_NAME.
|
||||
///
|
||||
/// <The configuration field doc comment>
|
||||
(configuration_ident: Type = DefaultValue),
|
||||
```
|
||||
|
||||
The doc comment is automatically added to the documentation of the listed
|
||||
lints. The default value will be formatted using the `Debug` implementation
|
||||
of the type.
|
||||
2. Adding the configuration value to the lint impl struct:
|
||||
1. This first requires the definition of a lint impl struct. Lint impl structs are usually
|
||||
generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
|
||||
to add some kind of metadata to it:
|
||||
```rust
|
||||
// Generated struct definition
|
||||
declare_lint_pass!(StructName => [
|
||||
LINT_NAME
|
||||
]);
|
||||
1. This first requires the definition of a lint impl struct. Lint impl
|
||||
structs are usually generated with the `declare_lint_pass!` macro. This
|
||||
struct needs to be defined manually to add some kind of metadata to it:
|
||||
```rust
|
||||
// Generated struct definition
|
||||
declare_lint_pass!(StructName => [
|
||||
LINT_NAME
|
||||
]);
|
||||
|
||||
// New manual definition struct
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {}
|
||||
// New manual definition struct
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {}
|
||||
|
||||
impl_lint_pass!(StructName => [
|
||||
LINT_NAME
|
||||
]);
|
||||
```
|
||||
impl_lint_pass!(StructName => [
|
||||
LINT_NAME
|
||||
]);
|
||||
```
|
||||
|
||||
2. Next add the configuration value and a corresponding creation method like this:
|
||||
```rust
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {
|
||||
configuration_ident: Type,
|
||||
}
|
||||
2. Next add the configuration value and a corresponding creation method like
|
||||
this:
|
||||
```rust
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructName {
|
||||
configuration_ident: Type,
|
||||
}
|
||||
|
||||
// ...
|
||||
// ...
|
||||
|
||||
impl StructName {
|
||||
pub fn new(configuration_ident: Type) -> Self {
|
||||
Self {
|
||||
configuration_ident,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
impl StructName {
|
||||
pub fn new(configuration_ident: Type) -> Self {
|
||||
Self {
|
||||
configuration_ident,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Passing the configuration value to the lint impl struct:
|
||||
|
||||
First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
|
||||
The configuration value is now cloned or copied into a local value that is then passed to the
|
||||
impl struct like this:
|
||||
```rust
|
||||
// Default generated registration:
|
||||
store.register_*_pass(|| box module::StructName);
|
||||
First find the struct construction in the [clippy_lints lib
|
||||
file](/clippy_lints/src/lib.rs). The configuration value is now cloned or
|
||||
copied into a local value that is then passed to the impl struct like this:
|
||||
|
||||
// New registration with configuration value
|
||||
let configuration_ident = conf.configuration_ident.clone();
|
||||
store.register_*_pass(move || box module::StructName::new(configuration_ident));
|
||||
```
|
||||
```rust
|
||||
// Default generated registration:
|
||||
store.register_*_pass(|| box module::StructName);
|
||||
|
||||
Congratulations the work is almost done. The configuration value can now be accessed
|
||||
in the linting code via `self.configuration_ident`.
|
||||
// New registration with configuration value
|
||||
let configuration_ident = conf.configuration_ident.clone();
|
||||
store.register_*_pass(move || box module::StructName::new(configuration_ident));
|
||||
```
|
||||
|
||||
Congratulations the work is almost done. The configuration value can now be
|
||||
accessed in the linting code via `self.configuration_ident`.
|
||||
|
||||
4. Adding tests:
|
||||
1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
|
||||
2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
|
||||
Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
|
||||
with the configuration value and a rust file that should be linted by Clippy. The test can
|
||||
otherwise be written as usual.
|
||||
1. The default configured value can be tested like any normal lint in
|
||||
[`tests/ui`](/tests/ui).
|
||||
2. The configuration itself will be tested separately in
|
||||
[`tests/ui-toml`](/tests/ui-toml). Simply add a new subfolder with a
|
||||
fitting name. This folder contains a `clippy.toml` file with the
|
||||
configuration value and a rust file that should be linted by Clippy. The
|
||||
test can otherwise be written as usual.
|
||||
|
||||
## Cheat Sheet
|
||||
|
||||
Here are some pointers to things you are likely going to need for every lint:
|
||||
|
||||
* [Clippy utils][utils] - Various helper functions. Maybe the function you need
|
||||
is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
|
||||
is already in here ([`is_type_diagnostic_item`], [`implements_trait`],
|
||||
[`snippet`], etc)
|
||||
* [Clippy diagnostics][diagnostics]
|
||||
* [Let chains][let-chains]
|
||||
* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
|
||||
* [`from_expansion`][from_expansion] and
|
||||
[`in_external_macro`][in_external_macro]
|
||||
* [`Span`][span]
|
||||
* [`Applicability`][applicability]
|
||||
* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
|
||||
* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
|
||||
* [Common tools for writing lints](common_tools_writing_lints.md) helps with
|
||||
common operations
|
||||
* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler
|
||||
concepts
|
||||
* [The nightly rustc docs][nightly_docs] which has been linked to throughout
|
||||
this guide
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Basics for hacking on Clippy
|
||||
|
||||
This document explains the basics for hacking on Clippy. Besides others, this
|
||||
includes how to build and test Clippy. For a more in depth description on
|
||||
the codebase take a look at [Adding Lints] or [Common Tools].
|
||||
includes how to build and test Clippy. For a more in depth description on the
|
||||
codebase take a look at [Adding Lints] or [Common Tools].
|
||||
|
||||
[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
|
||||
[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
|
||||
|
@ -62,8 +62,8 @@ TESTNAME="test_" cargo uitest
|
|||
cargo test --test dogfood
|
||||
```
|
||||
|
||||
If the output of a [UI test] differs from the expected output, you can update the
|
||||
reference file with:
|
||||
If the output of a [UI test] differs from the expected output, you can update
|
||||
the reference file with:
|
||||
|
||||
```bash
|
||||
cargo dev bless
|
||||
|
@ -72,8 +72,8 @@ cargo dev bless
|
|||
For example, this is necessary, if you fix a typo in an error message of a lint
|
||||
or if you modify a test file to add a test case.
|
||||
|
||||
_Note:_ This command may update more files than you intended. In that case only
|
||||
commit the files you wanted to update.
|
||||
> _Note:_ This command may update more files than you intended. In that case
|
||||
> only commit the files you wanted to update.
|
||||
|
||||
[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
|
||||
|
||||
|
@ -96,22 +96,26 @@ cargo dev setup git-hook
|
|||
# (experimental) Setup Clippy to work with IntelliJ-Rust
|
||||
cargo dev setup intellij
|
||||
```
|
||||
More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust)
|
||||
|
||||
More about intellij command usage and reasons
|
||||
[here](../CONTRIBUTING.md#intellij-rust)
|
||||
|
||||
## lintcheck
|
||||
`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results.
|
||||
You can `git diff` the updated log against its previous version and
|
||||
see what impact your lint made on a small set of crates.
|
||||
If you add a new lint, please audit the resulting warnings and make sure
|
||||
there are no false positives and that the suggestions are valid.
|
||||
|
||||
`cargo lintcheck` will build and run clippy on a fixed set of crates and
|
||||
generate a log of the results. You can `git diff` the updated log against its
|
||||
previous version and see what impact your lint made on a small set of crates.
|
||||
If you add a new lint, please audit the resulting warnings and make sure there
|
||||
are no false positives and that the suggestions are valid.
|
||||
|
||||
Refer to the tools [README] for more details.
|
||||
|
||||
[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md
|
||||
|
||||
## PR
|
||||
|
||||
We follow a rustc no merge-commit policy.
|
||||
See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
|
||||
We follow a rustc no merge-commit policy. See
|
||||
<https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
|
||||
|
||||
## Common Abbreviations
|
||||
|
||||
|
@ -126,27 +130,34 @@ See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
|
|||
| HIR | High-Level Intermediate Representation |
|
||||
| TCX | Type context |
|
||||
|
||||
This is a concise list of abbreviations that can come up during Clippy development. An extensive
|
||||
general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if
|
||||
an abbreviation or meaning is unclear to you.
|
||||
This is a concise list of abbreviations that can come up during Clippy
|
||||
development. An extensive general list can be found in the [rustc-dev-guide
|
||||
glossary][glossary]. Always feel free to ask if an abbreviation or meaning is
|
||||
unclear to you.
|
||||
|
||||
## Install from source
|
||||
|
||||
If you are hacking on Clippy and want to install it from source, do the following:
|
||||
If you are hacking on Clippy and want to install it from source, do the
|
||||
following:
|
||||
|
||||
First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`.
|
||||
We will use this override to install Clippy into the right toolchain.
|
||||
First, take note of the toolchain
|
||||
[override](https://rust-lang.github.io/rustup/overrides.html) in
|
||||
`/rust-toolchain`. We will use this override to install Clippy into the right
|
||||
toolchain.
|
||||
|
||||
> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`.
|
||||
> Tip: You can view the active toolchain for the current directory with `rustup
|
||||
> show active-toolchain`.
|
||||
|
||||
From the Clippy project root, run the following command to build the Clippy binaries and copy them into the
|
||||
toolchain directory. This will override the currently installed Clippy component.
|
||||
From the Clippy project root, run the following command to build the Clippy
|
||||
binaries and copy them into the toolchain directory. This will override the
|
||||
currently installed Clippy component.
|
||||
|
||||
```terminal
|
||||
cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin"
|
||||
```
|
||||
|
||||
Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy.
|
||||
Now you may run `cargo clippy` in any project, using the toolchain where you
|
||||
just installed Clippy.
|
||||
|
||||
```terminal
|
||||
cd my-project
|
||||
|
@ -159,16 +170,19 @@ cargo +nightly-2021-07-01 clippy
|
|||
clippy-driver +nightly-2021-07-01 <filename>
|
||||
```
|
||||
|
||||
If you need to restore the default Clippy installation, run the following (from the Clippy project root).
|
||||
If you need to restore the default Clippy installation, run the following (from
|
||||
the Clippy project root).
|
||||
|
||||
```terminal
|
||||
rustup component remove clippy
|
||||
rustup component add clippy
|
||||
```
|
||||
|
||||
> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup
|
||||
> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and
|
||||
> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running
|
||||
> `rustup update`.
|
||||
> **DO NOT** install using `cargo install --path . --force` since this will
|
||||
> overwrite rustup
|
||||
> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is,
|
||||
> `~/.cargo/bin/cargo-clippy` and `~/.cargo/bin/clippy-driver` should be hard or
|
||||
> soft links to `~/.cargo/bin/rustup`. You can repair these by running `rustup
|
||||
> update`.
|
||||
|
||||
[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html
|
|
@ -18,15 +18,17 @@ Useful Rustc dev guide links:
|
|||
|
||||
## Retrieving the type of an expression
|
||||
|
||||
Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
|
||||
Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for
|
||||
example to answer following questions:
|
||||
|
||||
- which type does this expression correspond to (using its [`TyKind`][TyKind])?
|
||||
- is it a sized type?
|
||||
- is it a primitive type?
|
||||
- does it implement a trait?
|
||||
|
||||
This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
|
||||
that gives you access to the underlying structure [`Ty`][Ty].
|
||||
This operation is performed using the [`expr_ty()`][expr_ty] method from the
|
||||
[`TypeckResults`][TypeckResults] struct, that gives you access to the underlying
|
||||
structure [`Ty`][Ty].
|
||||
|
||||
Example of use:
|
||||
```rust
|
||||
|
@ -43,8 +45,8 @@ impl LateLintPass<'_> for MyStructLint {
|
|||
}
|
||||
```
|
||||
|
||||
Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method
|
||||
to retrieve a type from a pattern.
|
||||
Similarly in [`TypeckResults`][TypeckResults] methods, you have the
|
||||
[`pat_ty()`][pat_ty] method to retrieve a type from a pattern.
|
||||
|
||||
Two noticeable items here:
|
||||
- `cx` is the lint context [`LateContext`][LateContext]. The two most useful
|
||||
|
@ -52,12 +54,13 @@ Two noticeable items here:
|
|||
`LateContext::typeck_results`, allowing us to jump to type definitions and
|
||||
other compilation stages such as HIR.
|
||||
- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is
|
||||
created by type checking step, it includes useful information such as types
|
||||
of expressions, ways to resolve methods and so on.
|
||||
created by type checking step, it includes useful information such as types of
|
||||
expressions, ways to resolve methods and so on.
|
||||
|
||||
## Checking if an expr is calling a specific method
|
||||
|
||||
Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
|
||||
Starting with an `expr`, you can check whether it is calling a specific method
|
||||
`some_method`:
|
||||
|
||||
```rust
|
||||
impl<'tcx> LateLintPass<'tcx> for MyStructLint {
|
||||
|
@ -77,8 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint {
|
|||
|
||||
## Checking for a specific type
|
||||
|
||||
There are three ways to check if an expression type is a specific type we want to check for.
|
||||
All of these methods only check for the base type, generic arguments have to be checked separately.
|
||||
There are three ways to check if an expression type is a specific type we want
|
||||
to check for. All of these methods only check for the base type, generic
|
||||
arguments have to be checked separately.
|
||||
|
||||
```rust
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
|
@ -115,7 +119,8 @@ Prefer using diagnostic items and lang items where possible.
|
|||
|
||||
## Checking if a type implements a specific trait
|
||||
|
||||
There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
|
||||
There are three ways to do this, depending on if the target trait has a
|
||||
diagnostic item, lang item or neither.
|
||||
|
||||
```rust
|
||||
use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths};
|
||||
|
@ -151,8 +156,9 @@ impl LateLintPass<'_> for MyStructLint {
|
|||
|
||||
> Prefer using diagnostic and lang items, if the target trait has one.
|
||||
|
||||
We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
|
||||
A list of defined paths for Clippy can be found in [paths.rs][paths]
|
||||
We access lang items through the type context `tcx`. `tcx` is of type
|
||||
[`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. A list of defined
|
||||
paths for Clippy can be found in [paths.rs][paths]
|
||||
|
||||
## Checking if a type defines a specific method
|
||||
|
||||
|
@ -182,14 +188,15 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
|
|||
## Dealing with macros and expansions
|
||||
|
||||
Keep in mind that macros are already expanded and desugaring is already applied
|
||||
to the code representation that you are working with in Clippy. This unfortunately causes a lot of
|
||||
false positives because macro expansions are "invisible" unless you actively check for them.
|
||||
Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
|
||||
dynamic in ways that are difficult or impossible to see.
|
||||
Use the following functions to deal with macros:
|
||||
to the code representation that you are working with in Clippy. This
|
||||
unfortunately causes a lot of false positives because macro expansions are
|
||||
"invisible" unless you actively check for them. Generally speaking, code with
|
||||
macro expansions should just be ignored by Clippy because that code can be
|
||||
dynamic in ways that are difficult or impossible to see. Use the following
|
||||
functions to deal with macros:
|
||||
|
||||
- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
|
||||
Checking this is a common first step in a lint.
|
||||
- `span.from_expansion()`: detects if a span is from macro expansion or
|
||||
desugaring. Checking this is a common first step in a lint.
|
||||
|
||||
```rust
|
||||
if expr.span.from_expansion() {
|
||||
|
@ -198,45 +205,51 @@ Use the following functions to deal with macros:
|
|||
}
|
||||
```
|
||||
|
||||
- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
|
||||
It is sometimes useful to check if the context of two spans are equal.
|
||||
- `span.ctxt()`: the span's context represents whether it is from expansion, and
|
||||
if so, which macro call expanded it. It is sometimes useful to check if the
|
||||
context of two spans are equal.
|
||||
|
||||
```rust
|
||||
// expands to `1 + 0`, but don't lint
|
||||
1 + mac!()
|
||||
```
|
||||
```rust
|
||||
if left.span.ctxt() != right.span.ctxt() {
|
||||
// the coder most likely cannot modify this expression
|
||||
return;
|
||||
}
|
||||
```
|
||||
Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
|
||||
be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
|
||||
```rust
|
||||
// expands to `1 + 0`, but don't lint
|
||||
1 + mac!()
|
||||
```
|
||||
```rust
|
||||
if left.span.ctxt() != right.span.ctxt() {
|
||||
// the coder most likely cannot modify this expression
|
||||
return;
|
||||
}
|
||||
```
|
||||
> Note: Code that is not from expansion is in the "root" context. So any spans
|
||||
> where `from_expansion` returns `true` can be assumed to have the same
|
||||
> context. And so just using `span.from_expansion()` is often good enough.
|
||||
|
||||
|
||||
- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
|
||||
If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
|
||||
not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
|
||||
- `in_external_macro(span)`: detect if the given span is from a macro defined in
|
||||
a foreign crate. If you want the lint to work with macro-generated code, this
|
||||
is the next line of defense to avoid macros not defined in the current crate.
|
||||
It doesn't make sense to lint code that the coder can't change.
|
||||
|
||||
You may want to use it for example to not start linting in macros from other crates
|
||||
You may want to use it for example to not start linting in macros from other
|
||||
crates
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate a_crate_with_macros;
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate a_crate_with_macros;
|
||||
|
||||
// `foo` is defined in `a_crate_with_macros`
|
||||
foo!("bar");
|
||||
// `foo` is defined in `a_crate_with_macros`
|
||||
foo!("bar");
|
||||
|
||||
// if we lint the `match` of `foo` call and test its span
|
||||
assert_eq!(in_external_macro(cx.sess(), match_span), true);
|
||||
```
|
||||
// if we lint the `match` of `foo` call and test its span
|
||||
assert_eq!(in_external_macro(cx.sess(), match_span), true);
|
||||
```
|
||||
|
||||
- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it
|
||||
- `span.ctxt()`: the span's context represents whether it is from expansion, and
|
||||
if so, what expanded it
|
||||
|
||||
One thing `SpanContext` is useful for is to check if two spans are in the same context. For example,
|
||||
in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some
|
||||
expression with a different context from `a`.
|
||||
One thing `SpanContext` is useful for is to check if two spans are in the same
|
||||
context. For example, in `a == b`, `a` and `b` have the same context. In a
|
||||
`macro_rules!` with `a == $b`, `$b` is expanded to some expression with a
|
||||
different context from `a`.
|
||||
|
||||
```rust
|
||||
macro_rules! m {
|
19
book/src/development/infrastructure/README.md
Normal file
19
book/src/development/infrastructure/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Infrastructure
|
||||
|
||||
In order to deploy Clippy over `rustup`, some infrastructure is necessary. This
|
||||
chapter describes the different parts of the Clippy infrastructure that need to
|
||||
be maintained to make this possible.
|
||||
|
||||
The most important part is the sync between the `rust-lang/rust` repository and
|
||||
the Clippy repository that takes place every two weeks. This process is
|
||||
described in the [Syncing changes between Clippy and `rust-lang/rust`](sync.md)
|
||||
section.
|
||||
|
||||
A new Clippy release is done together with every Rust release, so every six
|
||||
weeks. The release process is described in the [Release a new Clippy
|
||||
Version](release.md) section. During a release cycle a changelog entry for the
|
||||
next release has to be written. The format of that and how to do that is
|
||||
documented in the [Changelog Update](changelog_update.md) section.
|
||||
|
||||
> _Note:_ The Clippy CI should also be described in this chapter, but for now is
|
||||
> left as a TODO.
|
42
book/src/development/infrastructure/book.md
Normal file
42
book/src/development/infrastructure/book.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# The Clippy Book
|
||||
|
||||
This document explains how to make additions and changes to the Clippy book, the
|
||||
guide to Clippy that you're reading right now. The Clippy book is formatted with
|
||||
[Markdown](https://www.markdownguide.org) and generated by
|
||||
[mdbook](https://github.com/rust-lang/mdBook).
|
||||
|
||||
- [Get mdbook](#get-mdbook)
|
||||
- [Make changes](#make-changes)
|
||||
|
||||
## Get mdbook
|
||||
|
||||
While not strictly necessary since the book source is simply Markdown text
|
||||
files, having mdbook locally will allow you to build, test and serve the book
|
||||
locally to view changes before you commit them to the repository. You likely
|
||||
already have `cargo` installed, so the easiest option is to simply:
|
||||
|
||||
```shell
|
||||
cargo install mdbook
|
||||
```
|
||||
|
||||
See the mdbook [installation](https://github.com/rust-lang/mdBook#installation)
|
||||
instructions for other options.
|
||||
|
||||
## Make changes
|
||||
|
||||
The book's
|
||||
[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src)
|
||||
directory contains all of the markdown files used to generate the book. If you
|
||||
want to see your changes in real time, you can use the mdbook `serve` command to
|
||||
run a web server locally that will automatically update changes as they are
|
||||
made. From the top level of your `rust-clippy` directory:
|
||||
|
||||
```shell
|
||||
mdbook serve book --open
|
||||
```
|
||||
|
||||
Then navigate to `http://localhost:3000` to see the generated book. While the
|
||||
server is running, changes you make will automatically be updated.
|
||||
|
||||
For more information, see the mdbook
|
||||
[guide](https://rust-lang.github.io/mdBook/).
|
|
@ -1,6 +1,6 @@
|
|||
# Changelog Update
|
||||
|
||||
If you want to help with updating the [changelog][changelog], you're in the right place.
|
||||
If you want to help with updating the [changelog], you're in the right place.
|
||||
|
||||
## When to update
|
||||
|
||||
|
@ -11,8 +11,8 @@ Rust release. For that purpose, the changelog is ideally updated during the week
|
|||
before an upcoming stable release. You can find the release dates on the [Rust
|
||||
Forge][forge].
|
||||
|
||||
Most of the time we only need to update the changelog for minor Rust releases. It's
|
||||
been very rare that Clippy changes were included in a patch release.
|
||||
Most of the time we only need to update the changelog for minor Rust releases.
|
||||
It's been very rare that Clippy changes were included in a patch release.
|
||||
|
||||
## Changelog update walkthrough
|
||||
|
||||
|
@ -24,10 +24,12 @@ be found in the `tools` directory of the Rust repository.
|
|||
Depending on the current time and what exactly you want to update, the following
|
||||
bullet points might be helpful:
|
||||
|
||||
* When writing the release notes for the **upcoming stable release** you need to check
|
||||
out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools]
|
||||
* When writing the release notes for the **upcoming beta release**, you need to check
|
||||
out the Clippy commit of the current Rust `master`. [Link][rust_master_tools]
|
||||
* When writing the release notes for the **upcoming stable release** you need to
|
||||
check out the Clippy commit of the current Rust `beta` branch.
|
||||
[Link][rust_beta_tools]
|
||||
* When writing the release notes for the **upcoming beta release**, you need to
|
||||
check out the Clippy commit of the current Rust `master`.
|
||||
[Link][rust_master_tools]
|
||||
* When writing the (forgotten) release notes for a **past stable release**, you
|
||||
need to check out the Rust release tag of the stable release.
|
||||
[Link][rust_stable_tools]
|
||||
|
@ -35,7 +37,8 @@ bullet points might be helpful:
|
|||
Usually you want to write the changelog of the **upcoming stable release**. Make
|
||||
sure though, that `beta` was already branched in the Rust repository.
|
||||
|
||||
To find the commit hash, issue the following command when in a `rust-lang/rust` checkout:
|
||||
To find the commit hash, issue the following command when in a `rust-lang/rust`
|
||||
checkout:
|
||||
```
|
||||
git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g"
|
||||
```
|
||||
|
@ -44,7 +47,9 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into
|
|||
|
||||
Once you've got the correct commit range, run
|
||||
|
||||
util/fetch_prs_between.sh commit1 commit2 > changes.txt
|
||||
```
|
||||
util/fetch_prs_between.sh commit1 commit2 > changes.txt
|
||||
```
|
||||
|
||||
and open that file in your editor of choice.
|
||||
|
||||
|
@ -54,14 +59,14 @@ already correct in the current changelog.
|
|||
### 3. Authoring the final changelog
|
||||
|
||||
The above script should have dumped all the relevant PRs to the file you
|
||||
specified. It should have filtered out most of the irrelevant PRs
|
||||
already, but it's a good idea to do a manual cleanup pass where you look for
|
||||
more irrelevant PRs. If you're not sure about some PRs, just leave them in for
|
||||
the review and ask for feedback.
|
||||
specified. It should have filtered out most of the irrelevant PRs already, but
|
||||
it's a good idea to do a manual cleanup pass where you look for more irrelevant
|
||||
PRs. If you're not sure about some PRs, just leave them in for the review and
|
||||
ask for feedback.
|
||||
|
||||
With the PRs filtered, you can start to take each PR and move the
|
||||
`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but
|
||||
try to keep it somewhat coherent.
|
||||
With the PRs filtered, you can start to take each PR and move the `changelog: `
|
||||
content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it
|
||||
somewhat coherent.
|
||||
|
||||
The order should roughly be:
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Release a new Clippy Version
|
||||
|
||||
_NOTE: This document is probably only relevant to you, if you're a member of the
|
||||
Clippy team._
|
||||
> _NOTE:_ This document is probably only relevant to you, if you're a member of
|
||||
> the Clippy team.
|
||||
|
||||
Clippy is released together with stable Rust releases. The dates for these
|
||||
releases can be found at the [Rust Forge]. This document explains the necessary
|
||||
|
@ -13,12 +13,11 @@ steps to create a Clippy release.
|
|||
4. [Tag the stable commit](#tag-the-stable-commit)
|
||||
5. [Update `CHANGELOG.md`](#update-changelogmd)
|
||||
|
||||
_NOTE: This document is for stable Rust releases, not for point releases. For
|
||||
point releases, step 1. and 2. should be enough._
|
||||
> _NOTE:_ This document is for stable Rust releases, not for point releases. For
|
||||
> point releases, step 1. and 2. should be enough.
|
||||
|
||||
[Rust Forge]: https://forge.rust-lang.org/
|
||||
|
||||
|
||||
## Remerge the `beta` branch
|
||||
|
||||
This step is only necessary, if since the last release something was backported
|
||||
|
@ -29,7 +28,7 @@ tree of the Clippy repository.
|
|||
To find out if this step is necessary run
|
||||
|
||||
```bash
|
||||
# Assumes that the local master branch is up-to-date
|
||||
# Assumes that the local master branch of rust-lang/rust-clippy is up-to-date
|
||||
$ git fetch upstream
|
||||
$ git branch master --contains upstream/beta
|
||||
```
|
||||
|
@ -45,9 +44,8 @@ $ git push origin backport_remerge # This can be pushed to your fork
|
|||
```
|
||||
|
||||
After this, open a PR to the master branch. In this PR, the commit hash of the
|
||||
`HEAD` of the `beta` branch must exists. In addition to that, no files should
|
||||
be changed by this PR.
|
||||
|
||||
`HEAD` of the `beta` branch must exists. In addition to that, no files should be
|
||||
changed by this PR.
|
||||
|
||||
## Update the `beta` branch
|
||||
|
||||
|
@ -58,7 +56,8 @@ determined.
|
|||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Rust repository
|
||||
$ git checkout beta
|
||||
$ git fetch upstream
|
||||
$ git checkout upstream/beta
|
||||
$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
|
||||
```
|
||||
|
||||
|
@ -72,7 +71,6 @@ $ git reset --hard $BETA_SHA
|
|||
$ git push upstream beta
|
||||
```
|
||||
|
||||
|
||||
## Find the Clippy commit
|
||||
|
||||
The first step is to tag the Clippy commit, that is included in the stable Rust
|
||||
|
@ -85,7 +83,6 @@ $ git checkout 1.XX.0 # XX should be exchanged with the corresponding version
|
|||
$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
|
||||
```
|
||||
|
||||
|
||||
## Tag the stable commit
|
||||
|
||||
After finding the Clippy commit, it can be tagged with the release number.
|
||||
|
@ -112,10 +109,10 @@ tag. Updating the stable branch from here is as easy as:
|
|||
$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote
|
||||
```
|
||||
|
||||
_NOTE: Usually there are no stable backports for Clippy, so this update should
|
||||
be possible without force pushing or anything like this. If there should have
|
||||
happened a stable backport, make sure to re-merge those changes just as with the
|
||||
`beta` branch._
|
||||
> _NOTE:_ Usually there are no stable backports for Clippy, so this update
|
||||
> should be possible without force pushing or anything like this. If there
|
||||
> should have happened a stable backport, make sure to re-merge those changes
|
||||
> just as with the `beta` branch.
|
||||
|
||||
## Update `CHANGELOG.md`
|
||||
|
||||
|
@ -142,4 +139,4 @@ the following parts:
|
|||
Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD
|
||||
```
|
||||
|
||||
[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
|
||||
[how to update the changelog]: changelog_update.md
|
123
book/src/development/infrastructure/sync.md
Normal file
123
book/src/development/infrastructure/sync.md
Normal file
|
@ -0,0 +1,123 @@
|
|||
# Syncing changes between Clippy and [`rust-lang/rust`]
|
||||
|
||||
Clippy currently gets built with a pinned nightly version.
|
||||
|
||||
In the `rust-lang/rust` repository, where rustc resides, there's a copy of
|
||||
Clippy that compiler hackers modify from time to time to adapt to changes in the
|
||||
unstable API of the compiler.
|
||||
|
||||
We need to sync these changes back to this repository periodically, and the
|
||||
changes made to this repository in the meantime also need to be synced to the
|
||||
`rust-lang/rust` repository.
|
||||
|
||||
To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is
|
||||
done in a bi-weekly basis if there's no urgent changes. This is done starting on
|
||||
the day of the Rust stable release and then every other week. That way we
|
||||
guarantee that we keep this repo up to date with the latest compiler API, and
|
||||
every feature in Clippy is available for 2 weeks in nightly, before it can get
|
||||
to beta. For reference, the first sync following this cadence was performed the
|
||||
2020-08-27.
|
||||
|
||||
This process is described in detail in the following sections. For general
|
||||
information about `subtree`s in the Rust repository see [Rust's
|
||||
`CONTRIBUTING.md`][subtree].
|
||||
|
||||
## Patching git-subtree to work with big repos
|
||||
|
||||
Currently, there's a bug in `git-subtree` that prevents it from working properly
|
||||
with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's
|
||||
stale. Before continuing with the following steps, we need to manually apply
|
||||
that fix to our local copy of `git-subtree`.
|
||||
|
||||
You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
|
||||
Put this file under `/usr/lib/git-core` (making a backup of the previous file)
|
||||
and make sure it has the proper permissions:
|
||||
|
||||
```bash
|
||||
sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
|
||||
sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
|
||||
```
|
||||
|
||||
> _Note:_ The first time running `git subtree push` a cache has to be built.
|
||||
> This involves going through the complete Clippy history once. For this you
|
||||
> have to increase the stack limit though, which you can do with `ulimit -s
|
||||
> 60000`. Make sure to run the `ulimit` command from the same session you call
|
||||
> git subtree.
|
||||
|
||||
> _Note:_ If you are a Debian user, `dash` is the shell used by default for
|
||||
> scripts instead of `sh`. This shell has a hardcoded recursion limit set to
|
||||
> 1000. In order to make this process work, you need to force the script to run
|
||||
> `bash` instead. You can do this by editing the first line of the `git-subtree`
|
||||
> script and changing `sh` to `bash`.
|
||||
|
||||
## Defining remotes
|
||||
|
||||
You may want to define remotes, so you don't have to type out the remote
|
||||
addresses on every sync. You can do this with the following commands (these
|
||||
commands still have to be run inside the `rust` directory):
|
||||
|
||||
```bash
|
||||
# Set clippy-upstream remote for pulls
|
||||
$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy
|
||||
# Make sure to not push to the upstream repo
|
||||
$ git remote set-url --push clippy-upstream DISABLED
|
||||
# Set a local remote
|
||||
$ git remote add clippy-local /path/to/rust-clippy
|
||||
```
|
||||
|
||||
> Note: The following sections assume that you have set those remotes with the
|
||||
> above remote names.
|
||||
|
||||
## Performing the sync from [`rust-lang/rust`] to Clippy
|
||||
|
||||
Here is a TL;DR version of the sync process (all of the following commands have
|
||||
to be run inside the `rust` directory):
|
||||
|
||||
1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
|
||||
2. Checkout the commit from the latest available nightly. You can get it using
|
||||
`rustup check`.
|
||||
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
```bash
|
||||
# Make sure to change `your-github-name` to your github name in the following command. Also be
|
||||
# sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand
|
||||
# because changes cannot be fast forwarded and you have to run this command again.
|
||||
git subtree push -P src/tools/clippy clippy-local sync-from-rust
|
||||
```
|
||||
|
||||
> _Note:_ Most of the time you have to create a merge commit in the
|
||||
> `rust-clippy` repo (this has to be done in the Clippy repo, not in the
|
||||
> rust-copy of Clippy):
|
||||
```bash
|
||||
git fetch upstream # assuming upstream is the rust-lang/rust remote
|
||||
git checkout sync-from-rust
|
||||
git merge upstream/master --no-ff
|
||||
```
|
||||
> Note: This is one of the few instances where a merge commit is allowed in
|
||||
> a PR.
|
||||
4. Bump the nightly version in the Clippy repository by changing the date in the
|
||||
rust-toolchain file to the current date and committing it with the message:
|
||||
```bash
|
||||
git commit -m "Bump nightly version -> YYYY-MM-DD"
|
||||
```
|
||||
5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
|
||||
ask them in the [Zulip] stream.)
|
||||
|
||||
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
|
||||
|
||||
## Performing the sync from Clippy to [`rust-lang/rust`]
|
||||
|
||||
All of the following commands have to be run inside the `rust` directory.
|
||||
|
||||
1. Make sure you have checked out the latest `master` of `rust-lang/rust`.
|
||||
2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
|
||||
```bash
|
||||
git checkout -b sync-from-clippy
|
||||
git subtree pull -P src/tools/clippy clippy-upstream master
|
||||
```
|
||||
3. Open a PR to [`rust-lang/rust`]
|
||||
|
||||
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
|
||||
[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
|
||||
[`rust-lang/rust`]: https://github.com/rust-lang/rust
|
11
book/src/development/proposals/README.md
Normal file
11
book/src/development/proposals/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Proposals
|
||||
|
||||
This chapter is about accepted proposals for changes that should be worked on in
|
||||
or around Clippy in the long run.
|
||||
|
||||
Besides adding more and more lints and improve the lints that Clippy already
|
||||
has, Clippy is also interested in making the experience of its users, developers
|
||||
and maintainers better over time. Projects that address bigger picture things
|
||||
like this usually take more time and it is useful to have a proposal for those
|
||||
first. This is the place where such proposals are collected, so that we can
|
||||
refer to them when working on them.
|
24
book/src/installation.md
Normal file
24
book/src/installation.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Installation
|
||||
|
||||
If you're using `rustup` to install and manage you're Rust toolchains, Clippy is
|
||||
usually **already installed**. In that case you can skip this chapter and go to
|
||||
the [Usage] chapter.
|
||||
|
||||
> Note: If you used the `minimal` profile when installing a Rust toolchain,
|
||||
> Clippy is not automatically installed.
|
||||
|
||||
## Using Rustup
|
||||
|
||||
If Clippy was not installed for a toolchain, it can be installed with
|
||||
|
||||
```
|
||||
$ rustup component add clippy [--toolchain=<name>]
|
||||
```
|
||||
|
||||
## From Source
|
||||
|
||||
Take a look at the [Basics] chapter in the Clippy developer guide to find step
|
||||
by step instructions on how to build and install Clippy from source.
|
||||
|
||||
[Basics]: development/basics.md#install-from-source
|
||||
[Usage]: usage.md
|
105
book/src/lints.md
Normal file
105
book/src/lints.md
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Clippy's Lints
|
||||
|
||||
Clippy offers a bunch of additional lints, to help its users write more correct
|
||||
and idiomatic Rust code. A full list of all lints, that can be filtered by
|
||||
category, lint level or keywords, can be found in the [Clippy lint
|
||||
documentation].
|
||||
|
||||
This chapter will give an overview of the different lint categories, which kind
|
||||
of lints they offer and recommended actions when you should see a lint out of
|
||||
that category. For examples, see the [Clippy lint documentation] and filter by
|
||||
category.
|
||||
|
||||
The different lint groups were defined in the [Clippy 1.0 RFC].
|
||||
|
||||
## Correctness
|
||||
|
||||
The `clippy::correctness` group is the only lint group in Clippy which lints are
|
||||
deny-by-default and abort the compilation when triggered. This is for good
|
||||
reason: If you see a `correctness` lint, it means that your code is outright
|
||||
wrong or useless and you should try to fix it.
|
||||
|
||||
Lints in this category are carefully picked and should be free of false
|
||||
positives. So just `#[allow]`ing those lints is not recommended.
|
||||
|
||||
## Suspicious
|
||||
|
||||
The `clippy::suspicious` group is similar to the correctness lints in that it
|
||||
contains lints that trigger on code that is really _sus_ and should be fixed. As
|
||||
opposed to correctness lints, it might be possible that the linted code is
|
||||
intentionally written like it is.
|
||||
|
||||
It is still recommended to fix code that is linted by lints out of this group
|
||||
instead of `#[allow]`ing the lint. In case you intentionally have written code
|
||||
that offends the lint you should specifically and locally `#[allow]` the lint
|
||||
and add give a reason why the code is correct as written.
|
||||
|
||||
## Complexity
|
||||
|
||||
The `clippy::complexity` group offers lints that give you suggestions on how to
|
||||
simplify your code. It mostly focuses on code that can be written in a shorter
|
||||
and more readable way, while preserving the semantics.
|
||||
|
||||
If you should see a complexity lint, it usually means that you can remove or
|
||||
replace some code and it is recommended to do so. However, if you need the more
|
||||
complex code for some expressiveness reason, it is recommended to allow
|
||||
complexity lints on a case-by-case basis.
|
||||
|
||||
## Perf
|
||||
|
||||
The `clippy::perf` group gives you suggestions on how you can increase the
|
||||
performance of your code. Those lints are mostly about code that the compiler
|
||||
can't trivially optimize, but has to be written in a slightly different way to
|
||||
make the optimizer's job easier.
|
||||
|
||||
Perf lints are usually easy to apply and it is recommended to do so.
|
||||
|
||||
## Style
|
||||
|
||||
The `clippy::style` group is mostly about writing idiomatic code. Because style
|
||||
is subjective, this lint group is the most opinionated warn-by-default group in
|
||||
Clippy.
|
||||
|
||||
If you see a style lint, applying the suggestion usually makes your code more
|
||||
readable and idiomatic. But because we know that this is opinionated, feel free
|
||||
to sprinkle `#[allow]`s for style lints in your code or `#![allow]` a style lint
|
||||
on your whole crate if you disagree with the suggested style completely.
|
||||
|
||||
## Pedantic
|
||||
|
||||
The `clippy::pedantic` group makes Clippy even more _pedantic_. You can enable
|
||||
the whole group with `#![warn(clippy::pedantic)]` in the `lib.rs`/`main.rs` of
|
||||
your crate. This lint group is for Clippy power users that want an in depth
|
||||
check of their code.
|
||||
|
||||
> _Note:_ Instead of enabling the whole group (like Clippy itself does), you may
|
||||
> want to cherry-pick lints out of the pedantic group.
|
||||
|
||||
If you enable this group, expect to also use `#[allow]` attributes generously
|
||||
throughout your code. Lints in this group are designed to be pedantic and false
|
||||
positives sometimes are intentional in order to prevent false negatives.
|
||||
|
||||
## Restriction
|
||||
|
||||
The `clippy::restriction` group contains lints that will _restrict_ you from
|
||||
using certain parts of the Rust language. It is **not** recommended to enable
|
||||
the whole group, but rather cherry-pick lints that are useful for your code base
|
||||
and your use case.
|
||||
|
||||
> _Note:_ Clippy will produce a warning if it finds a
|
||||
> `#![warn(clippy::restriction)]` attribute in your code!
|
||||
|
||||
Lints from this group will restrict you in some way. If you enable a restriction
|
||||
lint for your crate it is recommended to also fix code that this lint triggers
|
||||
on. However, those lints are really strict by design and you might want to
|
||||
`#[allow]` them in some special cases, with a comment justifying that.
|
||||
|
||||
## Cargo
|
||||
|
||||
The `clippy::cargo` group gives you suggestions on how to improve your
|
||||
`Cargo.toml` file. This might be especially interesting if you want to publish
|
||||
your crate and are not sure if you have all useful information in your
|
||||
`Cargo.toml`.
|
||||
|
||||
[Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/
|
||||
[Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
|
151
book/src/usage.md
Normal file
151
book/src/usage.md
Normal file
|
@ -0,0 +1,151 @@
|
|||
# Usage
|
||||
|
||||
This chapter describes how to use Clippy to get the most out of it. Clippy can
|
||||
be used as a `cargo` subcommand or, like `rustc`, directly with the
|
||||
`clippy-driver` binary.
|
||||
|
||||
> _Note:_ This chapter assumes that you have Clippy installed already. If you're
|
||||
> not sure, take a look at the [Installation] chapter.
|
||||
|
||||
## Cargo subcommand
|
||||
|
||||
The easiest and most common way to run Clippy is through `cargo`. To do that,
|
||||
just run
|
||||
|
||||
```bash
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
### Lint configuration
|
||||
|
||||
The above command will run the default set of lints, which are included in the
|
||||
lint group `clippy::all`. You might want to use even more lints or you might not
|
||||
agree with every Clippy lint, and for that there are ways to configure lint
|
||||
levels.
|
||||
|
||||
> _Note:_ Clippy is meant to be used with a generous sprinkling of
|
||||
> `#[allow(..)]`s through your code. So if you disagree with a lint, don't feel
|
||||
> bad disabling them for parts of your code or the whole project.
|
||||
|
||||
#### Command line
|
||||
|
||||
You can configure lint levels on the command line by adding
|
||||
`-A/W/D clippy::lint_name` like this:
|
||||
|
||||
```bash
|
||||
cargo clippy -- -Aclippy::style -Wclippy::double_neg -Dclippy::perf
|
||||
```
|
||||
|
||||
For [CI] all warnings can be elevated to errors which will inturn fail
|
||||
the build and cause Clippy to exit with a code other than `0`.
|
||||
|
||||
```
|
||||
cargo clippy -- -Dwarnings
|
||||
```
|
||||
|
||||
> _Note:_ 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.).
|
||||
|
||||
For more information on configuring lint levels, see the [rustc documentation].
|
||||
|
||||
[rustc documentation]: https://doc.rust-lang.org/rustc/lints/levels.html#configuring-warning-levels
|
||||
|
||||
#### Even more lints
|
||||
|
||||
Clippy has lint groups which are allow-by-default. This means, that you will
|
||||
have to enable the lints in those groups manually.
|
||||
|
||||
For a full list of all lints with their description and examples, please refere
|
||||
to [Clippy's lint list]. The two most important allow-by-default groups are
|
||||
described below:
|
||||
|
||||
[Clippy's lint list]: https://rust-lang.github.io/rust-clippy/master/index.html
|
||||
|
||||
##### `clippy::pedantic`
|
||||
|
||||
The first group is the `pedantic` group. This group contains really opinionated
|
||||
lints, that may have some intentional false positives in order to prevent false
|
||||
negatives. So while this group is ready to be used in production, you can expect
|
||||
to sprinkle multiple `#[allow(..)]`s in your code. If you find any false
|
||||
positives, you're still welcome to report them to us for future improvements.
|
||||
|
||||
> FYI: Clippy uses the whole group to lint itself.
|
||||
|
||||
##### `clippy::restriction`
|
||||
|
||||
The second group is the `restriction` group. This group contains lints that
|
||||
"restrict" the language in some way. For example the `clippy::unwrap` lint from
|
||||
this group won't allow you to use `.unwrap()` in your code. You may want to look
|
||||
through the lints in this group and enable the ones that fit your need.
|
||||
|
||||
> _Note:_ You shouldn't enable the whole lint group, but cherry-pick lints from
|
||||
> this group. Some lints in this group will even contradict other Clippy lints!
|
||||
|
||||
#### Too many lints
|
||||
|
||||
The most opinionated warn-by-default group of Clippy is the `clippy::style`
|
||||
group. Some people prefer to disable this group completely and then cherry-pick
|
||||
some lints they like from this group. The same is of course possible with every
|
||||
other of Clippy's lint groups.
|
||||
|
||||
> _Note:_ We try to keep the warn-by-default groups free from false positives
|
||||
> (FP). If you find that a lint wrongly triggers, please report it in an issue
|
||||
> (if there isn't an issue for that FP already)
|
||||
|
||||
#### Source Code
|
||||
|
||||
You can configure lint levels in source code the same way you can configure
|
||||
`rustc` lints:
|
||||
|
||||
```rust
|
||||
#![allow(clippy::style)]
|
||||
|
||||
#[warn(clippy::double_neg)]
|
||||
fn main() {
|
||||
let x = 1;
|
||||
let y = --x;
|
||||
// ^^ warning: double negation
|
||||
}
|
||||
```
|
||||
|
||||
### Automatically applying Clippy suggestions
|
||||
|
||||
Clippy can automatically apply some lint suggestions, just like the compiler.
|
||||
|
||||
```terminal
|
||||
cargo clippy --fix
|
||||
```
|
||||
|
||||
### Workspaces
|
||||
|
||||
All the usual workspace options should work with Clippy. For example the
|
||||
following command will run Clippy on the `example` crate in your workspace:
|
||||
|
||||
```terminal
|
||||
cargo clippy -p example
|
||||
```
|
||||
|
||||
As with `cargo check`, this includes dependencies that are members of the
|
||||
workspace, like path dependencies. If you want to run Clippy **only** on the
|
||||
given crate, use the `--no-deps` option like this:
|
||||
|
||||
```terminal
|
||||
cargo clippy -p example -- --no-deps
|
||||
```
|
||||
|
||||
## Using Clippy without `cargo`: `clippy-driver`
|
||||
|
||||
Clippy can also be used in projects that do not use cargo. To do so, run
|
||||
`clippy-driver` with the same arguments you use for `rustc`. For example:
|
||||
|
||||
```terminal
|
||||
clippy-driver --edition 2018 -Cpanic=abort foo.rs
|
||||
```
|
||||
|
||||
> _Note:_ `clippy-driver` is designed for running Clippy and should not be used
|
||||
> as a general replacement for `rustc`. `clippy-driver` may produce artifacts
|
||||
> that are not optimized as expected, for example.
|
||||
|
||||
[Installation]: installation.md
|
||||
[CI]: continuous_integration
|
|
@ -13,7 +13,7 @@ fn exit_if_err(status: io::Result<ExitStatus>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a str>) {
|
||||
pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
|
||||
let is_file = match fs::metadata(path) {
|
||||
Ok(metadata) => metadata.is_file(),
|
||||
Err(e) => {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
|
||||
use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use indoc::indoc;
|
||||
fn main() {
|
||||
|
@ -10,15 +10,15 @@ fn main() {
|
|||
|
||||
match matches.subcommand() {
|
||||
Some(("bless", matches)) => {
|
||||
bless::bless(matches.is_present("ignore-timestamp"));
|
||||
bless::bless(matches.contains_id("ignore-timestamp"));
|
||||
},
|
||||
Some(("fmt", matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
|
||||
},
|
||||
Some(("update_lints", matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
if matches.contains_id("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
} else if matches.contains_id("check") {
|
||||
update_lints::update(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::update(update_lints::UpdateMode::Change);
|
||||
|
@ -26,10 +26,10 @@ fn main() {
|
|||
},
|
||||
Some(("new_lint", matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
matches.value_of("category"),
|
||||
matches.is_present("msrv"),
|
||||
matches.get_one::<String>("pass"),
|
||||
matches.get_one::<String>("name"),
|
||||
matches.get_one::<String>("category"),
|
||||
matches.contains_id("msrv"),
|
||||
) {
|
||||
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
|
@ -37,28 +37,28 @@ fn main() {
|
|||
},
|
||||
Some(("setup", sub_command)) => match sub_command.subcommand() {
|
||||
Some(("intellij", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
if matches.contains_id("remove") {
|
||||
setup::intellij::remove_rustc_src();
|
||||
} else {
|
||||
setup::intellij::setup_rustc_src(
|
||||
matches
|
||||
.value_of("rustc-repo-path")
|
||||
.get_one::<String>("rustc-repo-path")
|
||||
.expect("this field is mandatory and therefore always valid"),
|
||||
);
|
||||
}
|
||||
},
|
||||
Some(("git-hook", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
if matches.contains_id("remove") {
|
||||
setup::git_hook::remove_hook();
|
||||
} else {
|
||||
setup::git_hook::install_hook(matches.is_present("force-override"));
|
||||
setup::git_hook::install_hook(matches.contains_id("force-override"));
|
||||
}
|
||||
},
|
||||
Some(("vscode-tasks", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
if matches.contains_id("remove") {
|
||||
setup::vscode::remove_tasks();
|
||||
} else {
|
||||
setup::vscode::install_tasks(matches.is_present("force-override"));
|
||||
setup::vscode::install_tasks(matches.contains_id("force-override"));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -70,19 +70,19 @@ fn main() {
|
|||
_ => {},
|
||||
},
|
||||
Some(("serve", matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
let port = *matches.get_one::<u16>("port").unwrap();
|
||||
let lint = matches.get_one::<String>("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
Some(("lint", matches)) => {
|
||||
let path = matches.value_of("path").unwrap();
|
||||
let args = matches.values_of("args").into_iter().flatten();
|
||||
let path = matches.get_one::<String>("path").unwrap();
|
||||
let args = matches.get_many::<String>("args").into_iter().flatten();
|
||||
lint::run(path, args);
|
||||
},
|
||||
Some(("rename_lint", matches)) => {
|
||||
let old_name = matches.value_of("old_name").unwrap();
|
||||
let new_name = matches.value_of("new_name").unwrap_or(old_name);
|
||||
let uplift = matches.is_present("uplift");
|
||||
let old_name = matches.get_one::<String>("old_name").unwrap();
|
||||
let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
|
||||
let uplift = matches.contains_id("uplift");
|
||||
update_lints::rename(old_name, new_name, uplift);
|
||||
},
|
||||
_ => {},
|
||||
|
@ -92,98 +92,86 @@ fn main() {
|
|||
fn get_clap_config() -> ArgMatches {
|
||||
Command::new("Clippy developer tooling")
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(
|
||||
.subcommands([
|
||||
Command::new("bless").about("bless the test output changes").arg(
|
||||
Arg::new("ignore-timestamp")
|
||||
.long("ignore-timestamp")
|
||||
.help("Include files updated before clippy was built"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
.arg(Arg::new("check").long("check").help("Use the rustfmt --check option"))
|
||||
.arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")),
|
||||
)
|
||||
.subcommand(
|
||||
.args([
|
||||
Arg::new("check").long("check").help("Use the rustfmt --check option"),
|
||||
Arg::new("verbose").short('v').long("verbose").help("Echo commands run"),
|
||||
]),
|
||||
Command::new("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",
|
||||
* 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::new("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(
|
||||
.args([
|
||||
Arg::new("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::new("check")
|
||||
.long("check")
|
||||
.help("Checks that `cargo dev update_lints` has been run. Used on CI."),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
]),
|
||||
Command::new("new_lint")
|
||||
.about("Create new lint and run `cargo dev update_lints`")
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("pass")
|
||||
.short('p')
|
||||
.long("pass")
|
||||
.help("Specify whether the lint runs during the early or late pass")
|
||||
.takes_value(true)
|
||||
.possible_values(&["early", "late"])
|
||||
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("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::new("category")
|
||||
.short('c')
|
||||
.long("category")
|
||||
.help("What category the lint belongs to")
|
||||
.default_value("nursery")
|
||||
.possible_values(&[
|
||||
"style",
|
||||
"correctness",
|
||||
"suspicious",
|
||||
"complexity",
|
||||
"perf",
|
||||
"pedantic",
|
||||
"restriction",
|
||||
"cargo",
|
||||
"nursery",
|
||||
"internal",
|
||||
"internal_warn",
|
||||
.value_parser([
|
||||
PossibleValue::new("style"),
|
||||
PossibleValue::new("correctness"),
|
||||
PossibleValue::new("suspicious"),
|
||||
PossibleValue::new("complexity"),
|
||||
PossibleValue::new("perf"),
|
||||
PossibleValue::new("pedantic"),
|
||||
PossibleValue::new("restriction"),
|
||||
PossibleValue::new("cargo"),
|
||||
PossibleValue::new("nursery"),
|
||||
PossibleValue::new("internal"),
|
||||
PossibleValue::new("internal_warn"),
|
||||
])
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")),
|
||||
)
|
||||
.subcommand(
|
||||
Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
|
||||
]),
|
||||
Command::new("setup")
|
||||
.about("Support for setting up your personal development environment")
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(
|
||||
.subcommands([
|
||||
Command::new("intellij")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the dependencies added with 'cargo dev setup intellij'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short('r')
|
||||
|
@ -192,67 +180,53 @@ fn get_clap_config() -> ArgMatches {
|
|||
.value_name("path")
|
||||
.conflicts_with("remove")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
]),
|
||||
Command::new("git-hook")
|
||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short('f')
|
||||
.help("Forces the override of an existing git pre-commit hook")
|
||||
.required(false),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
]),
|
||||
Command::new("vscode-tasks")
|
||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short('f')
|
||||
.help("Forces the override of existing vscode tasks")
|
||||
.required(false),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
]),
|
||||
]),
|
||||
Command::new("remove")
|
||||
.about("Support for undoing changes done by the setup command")
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook"))
|
||||
.subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks"))
|
||||
.subcommand(
|
||||
.subcommands([
|
||||
Command::new("git-hook").about("Remove any existing pre-commit git hook"),
|
||||
Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
|
||||
Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
]),
|
||||
Command::new("serve")
|
||||
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("port")
|
||||
.long("port")
|
||||
.short('p')
|
||||
.help("Local port for the http server")
|
||||
.default_value("8000")
|
||||
.validator_os(serve::validate_port),
|
||||
)
|
||||
.arg(Arg::new("lint").help("Which lint's page to load initially (optional)")),
|
||||
)
|
||||
.subcommand(
|
||||
.value_parser(clap::value_parser!(u16)),
|
||||
Arg::new("lint").help("Which lint's page to load initially (optional)"),
|
||||
]),
|
||||
Command::new("lint")
|
||||
.about("Manually run clippy on a file or package")
|
||||
.after_help(indoc! {"
|
||||
|
@ -271,37 +245,27 @@ fn get_clap_config() -> ArgMatches {
|
|||
cargo dev lint file.rs -- -W clippy::pedantic
|
||||
cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||
"})
|
||||
.arg(
|
||||
.args([
|
||||
Arg::new("path")
|
||||
.required(true)
|
||||
.help("The path to a file or package directory to lint"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("args")
|
||||
.multiple_occurrences(true)
|
||||
.action(ArgAction::Append)
|
||||
.help("Pass extra arguments to cargo/clippy-driver"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("rename_lint")
|
||||
.about("Renames the given lint")
|
||||
.arg(
|
||||
Arg::new("old_name")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The name of the lint to rename"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("new_name")
|
||||
.index(2)
|
||||
.required_unless_present("uplift")
|
||||
.help("The new name of the lint"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("uplift")
|
||||
.long("uplift")
|
||||
.help("This lint will be uplifted into rustc"),
|
||||
),
|
||||
)
|
||||
]),
|
||||
Command::new("rename_lint").about("Renames the given lint").args([
|
||||
Arg::new("old_name")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The name of the lint to rename"),
|
||||
Arg::new("new_name")
|
||||
.index(2)
|
||||
.required_unless_present("uplift")
|
||||
.help("The new name of the lint"),
|
||||
Arg::new("uplift")
|
||||
.long("uplift")
|
||||
.help("This lint will be uplifted into rustc"),
|
||||
]),
|
||||
])
|
||||
.get_matches()
|
||||
}
|
||||
|
|
|
@ -34,7 +34,12 @@ impl<T> Context for io::Result<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// This function errors out if the files couldn't be created or written to.
|
||||
pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> {
|
||||
pub fn create(
|
||||
pass: Option<&String>,
|
||||
lint_name: Option<&String>,
|
||||
category: Option<&String>,
|
||||
msrv: bool,
|
||||
) -> io::Result<()> {
|
||||
let lint = LintData {
|
||||
pass: pass.expect("`pass` argument is validated by clap"),
|
||||
name: lint_name.expect("`name` argument is validated by clap"),
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::time::{Duration, SystemTime};
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if the python commands could not be spawned
|
||||
pub fn run(port: u16, lint: Option<&str>) -> ! {
|
||||
pub fn run(port: u16, lint: Option<&String>) -> ! {
|
||||
let mut url = Some(match lint {
|
||||
None => format!("http://localhost:{}", port),
|
||||
Some(lint) => format!("http://localhost:{}/#{}", port, lint),
|
||||
|
|
|
@ -58,6 +58,16 @@ fn generate_lint_files(
|
|||
},
|
||||
);
|
||||
|
||||
replace_region_in_file(
|
||||
update_mode,
|
||||
Path::new("book/src/README.md"),
|
||||
"[There are over ",
|
||||
" lints included in this crate!]",
|
||||
|res| {
|
||||
write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
|
||||
},
|
||||
);
|
||||
|
||||
replace_region_in_file(
|
||||
update_mode,
|
||||
Path::new("CHANGELOG.md"),
|
||||
|
|
|
@ -10,6 +10,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
cargo_metadata = "0.14"
|
||||
clippy_dev = { path = "../clippy_dev", optional = true }
|
||||
clippy_utils = { path = "../clippy_utils" }
|
||||
if_chain = "1.0"
|
||||
itertools = "0.10.1"
|
||||
|
@ -18,6 +19,7 @@ quine-mc_cluskey = "0.2"
|
|||
regex-syntax = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
toml = "0.5"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
|
@ -30,7 +32,7 @@ url = { version = "2.2", features = ["serde"] }
|
|||
[features]
|
||||
deny-warnings = ["clippy_utils/deny-warnings"]
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal = ["clippy_utils/internal", "serde_json"]
|
||||
internal = ["clippy_utils/internal", "serde_json", "tempfile", "clippy_dev"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This crate uses #[feature(rustc_private)]
|
||||
|
|
|
@ -14,9 +14,6 @@ declare_clippy_lint! {
|
|||
/// 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)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -24,6 +24,7 @@ declare_clippy_lint! {
|
|||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// async fn foo() {}
|
||||
|
@ -63,9 +64,10 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
|
|||
_ => None,
|
||||
};
|
||||
if let Some(return_expr_span) = return_expr_span {
|
||||
span_lint_and_then(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
ASYNC_YIELDS_ASYNC,
|
||||
body.value.hir_id,
|
||||
return_expr_span,
|
||||
"an async construct yields a type which is itself awaitable",
|
||||
|db| {
|
||||
|
|
|
@ -340,7 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
if is_word(lint, sym!(unused_imports))
|
||||
if is_word(lint, sym::unused_imports)
|
||||
|| is_word(lint, sym::deprecated)
|
||||
|| is_word(lint, sym!(unreachable_pub))
|
||||
|| is_word(lint, sym!(unused))
|
||||
|
@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
}
|
||||
},
|
||||
ItemKind::ExternCrate(..) => {
|
||||
if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
return;
|
||||
}
|
||||
if is_word(lint, sym!(unused_extern_crates)) {
|
||||
|
|
|
@ -140,8 +140,6 @@ declare_clippy_lint! {
|
|||
/// from a memory access perspective but will cause bugs at runtime if they
|
||||
/// are held in such a way.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
|
|
|
@ -17,11 +17,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// assert_eq!("a".is_empty(), false);
|
||||
/// assert_ne!("a".is_empty(), true);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// assert!(!"a".is_empty());
|
||||
/// ```
|
||||
#[clippy::version = "1.53.0"]
|
||||
|
|
|
@ -18,7 +18,7 @@ declare_clippy_lint! {
|
|||
/// Dereferencing and then borrowing a reference value has no effect in most cases.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// false negative on such code:
|
||||
/// False negative on such code:
|
||||
/// ```
|
||||
/// let x = &12;
|
||||
/// let addr_x = &x as *const _ as usize;
|
||||
|
@ -29,17 +29,20 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(_x: &str) {}
|
||||
///
|
||||
/// let s = &String::new();
|
||||
///
|
||||
/// // Bad
|
||||
/// let a: &String = &* s;
|
||||
/// foo(&*s);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(_x: &str) {}
|
||||
/// # let s = &String::new();
|
||||
/// let a: &String = s;
|
||||
/// foo(&**s);
|
||||
///
|
||||
/// fn foo(_: &str){ }
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub BORROW_DEREF_REF,
|
||||
|
|
|
@ -219,13 +219,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn fun() -> i32 { 1 }
|
||||
/// let a = fun as i64;
|
||||
/// let _ = fun as i64;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// fn fun2() -> i32 { 1 }
|
||||
/// let a = fun2 as usize;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn fun() -> i32 { 1 }
|
||||
/// let _ = fun as usize;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FN_TO_NUMERIC_CAST,
|
||||
|
@ -245,17 +246,19 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as i32;
|
||||
/// ```
|
||||
///
|
||||
/// // Better: Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn2() -> i16 {
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let fn_ptr = fn2 as usize;
|
||||
/// let fn_ptr = fn1 as usize;
|
||||
/// let fn_ptr_truncated = fn_ptr as i32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -277,19 +280,24 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad: fn1 is cast as `usize`
|
||||
/// // fn1 is cast as `usize`
|
||||
/// fn fn1() -> u16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as usize;
|
||||
/// ```
|
||||
///
|
||||
/// // Good: maybe you intended to call the function?
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // maybe you intended to call the function?
|
||||
/// fn fn2() -> u16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn2() as usize;
|
||||
///
|
||||
/// // Good: maybe you intended to cast it to a function type?
|
||||
/// // or
|
||||
///
|
||||
/// // maybe you intended to cast it to a function type?
|
||||
/// fn fn3() -> u16 {
|
||||
/// 1
|
||||
/// }
|
||||
|
@ -406,7 +414,7 @@ declare_clippy_lint! {
|
|||
/// enum E { X = 256 };
|
||||
/// let _ = E::X as u8;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_TRUNCATION,
|
||||
suspicious,
|
||||
"casts from an enum type to an integral type which will truncate the value"
|
||||
|
@ -451,7 +459,7 @@ declare_clippy_lint! {
|
|||
/// println!("{:?}", &*new_ptr);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_SLICE_DIFFERENT_SIZES,
|
||||
correctness,
|
||||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
|
||||
use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -22,18 +22,14 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let foo: u32 = 5;
|
||||
/// # let _ =
|
||||
/// foo <= i32::MAX as u32
|
||||
/// # ;
|
||||
/// foo <= i32::MAX as u32;
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let foo = 1;
|
||||
/// # let _ =
|
||||
/// i32::try_from(foo).is_ok()
|
||||
/// # ;
|
||||
/// # #[allow(unused)]
|
||||
/// i32::try_from(foo).is_ok();
|
||||
/// ```
|
||||
#[clippy::version = "1.37.0"]
|
||||
pub CHECKED_CONVERSIONS,
|
||||
|
@ -61,6 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
|||
}
|
||||
|
||||
let result = if_chain! {
|
||||
if !in_constant(cx, item.hir_id);
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if let ExprKind::Binary(op, left, right) = &item.kind;
|
||||
|
||||
|
|
|
@ -32,20 +32,20 @@ declare_clippy_lint! {
|
|||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// …
|
||||
/// // …
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
|
|
@ -35,7 +35,6 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use std::cmp::Ordering;
|
||||
/// # fn a() {}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
||||
use clippy_utils::{
|
||||
both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed,
|
||||
search_same, ContainsName, SpanlessEq, SpanlessHash,
|
||||
eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed,
|
||||
search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use core::iter;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{source_map::Span, symbol::Symbol, BytePos};
|
||||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -165,243 +165,315 @@ declare_lint_pass!(CopyAndPaste => [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() {
|
||||
if let ExprKind::If(_, _, _) = expr.kind {
|
||||
// skip ifs directly in else, it will be checked in the parent if
|
||||
if let Some(&Expr {
|
||||
kind: ExprKind::If(_, _, Some(else_expr)),
|
||||
..
|
||||
}) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if else_expr.hir_id == expr.hir_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (conds, blocks) = if_sequence(expr);
|
||||
// Conditions
|
||||
lint_same_cond(cx, &conds);
|
||||
lint_same_fns_in_if_cond(cx, &conds);
|
||||
// Block duplication
|
||||
lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr);
|
||||
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
||||
let (conds, blocks) = if_sequence(expr);
|
||||
lint_same_cond(cx, &conds);
|
||||
lint_same_fns_in_if_cond(cx, &conds);
|
||||
let all_same =
|
||||
!is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
|
||||
if !all_same && conds.len() != blocks.len() {
|
||||
lint_branches_sharing_code(cx, &conds, &blocks, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal.
|
||||
fn lint_same_then_else<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
conds: &[&'tcx Expr<'_>],
|
||||
blocks: &[&Block<'tcx>],
|
||||
has_conditional_else: bool,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
// We only lint ifs with multiple blocks
|
||||
if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if each block has shared code
|
||||
let has_expr = blocks[0].expr.is_some();
|
||||
|
||||
let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) {
|
||||
(block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// BRANCHES_SHARING_CODE prerequisites
|
||||
if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only the start is the same
|
||||
if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) {
|
||||
let block = blocks[0];
|
||||
let start_stmts = block.stmts.split_at(start_eq).0;
|
||||
|
||||
let mut start_walker = UsedValueFinderVisitor::new(cx);
|
||||
for stmt in start_stmts {
|
||||
intravisit::walk_stmt(&mut start_walker, stmt);
|
||||
}
|
||||
|
||||
emit_branches_sharing_code_lint(
|
||||
cx,
|
||||
start_eq,
|
||||
0,
|
||||
false,
|
||||
check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr),
|
||||
blocks,
|
||||
expr,
|
||||
);
|
||||
} else if end_eq != 0 || (has_expr && expr_eq) {
|
||||
let block = blocks[blocks.len() - 1];
|
||||
let (start_stmts, block_stmts) = block.stmts.split_at(start_eq);
|
||||
let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq);
|
||||
|
||||
// Scan start
|
||||
let mut start_walker = UsedValueFinderVisitor::new(cx);
|
||||
for stmt in start_stmts {
|
||||
intravisit::walk_stmt(&mut start_walker, stmt);
|
||||
}
|
||||
let mut moved_syms = start_walker.def_symbols;
|
||||
|
||||
// Scan block
|
||||
let mut block_walker = UsedValueFinderVisitor::new(cx);
|
||||
for stmt in block_stmts {
|
||||
intravisit::walk_stmt(&mut block_walker, stmt);
|
||||
}
|
||||
let mut block_defs = block_walker.defs;
|
||||
|
||||
// Scan moved stmts
|
||||
let mut moved_start: Option<usize> = None;
|
||||
let mut end_walker = UsedValueFinderVisitor::new(cx);
|
||||
for (index, stmt) in end_stmts.iter().enumerate() {
|
||||
intravisit::walk_stmt(&mut end_walker, stmt);
|
||||
|
||||
for value in &end_walker.uses {
|
||||
// Well we can't move this and all prev statements. So reset
|
||||
if block_defs.contains(value) {
|
||||
moved_start = Some(index + 1);
|
||||
end_walker.defs.drain().for_each(|x| {
|
||||
block_defs.insert(x);
|
||||
});
|
||||
|
||||
end_walker.def_symbols.clear();
|
||||
}
|
||||
}
|
||||
|
||||
end_walker.uses.clear();
|
||||
}
|
||||
|
||||
if let Some(moved_start) = moved_start {
|
||||
end_eq -= moved_start;
|
||||
}
|
||||
|
||||
let end_linable = block.expr.map_or_else(
|
||||
|| end_eq != 0,
|
||||
|expr| {
|
||||
intravisit::walk_expr(&mut end_walker, expr);
|
||||
end_walker.uses.iter().any(|x| !block_defs.contains(x))
|
||||
},
|
||||
);
|
||||
|
||||
if end_linable {
|
||||
end_walker.def_symbols.drain().for_each(|x| {
|
||||
moved_syms.insert(x);
|
||||
});
|
||||
}
|
||||
|
||||
emit_branches_sharing_code_lint(
|
||||
cx,
|
||||
start_eq,
|
||||
end_eq,
|
||||
end_linable,
|
||||
check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
|
||||
blocks,
|
||||
expr,
|
||||
);
|
||||
/// Checks if the given expression is a let chain.
|
||||
fn contains_let(e: &Expr<'_>) -> bool {
|
||||
match e.kind {
|
||||
ExprKind::Let(..) => true,
|
||||
ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => {
|
||||
matches!(lhs.kind, ExprKind::Let(..)) || contains_let(rhs)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockEqual {
|
||||
/// The amount statements that are equal from the start
|
||||
start_eq: usize,
|
||||
/// The amount statements that are equal from the end
|
||||
end_eq: usize,
|
||||
/// An indication if the block expressions are the same. This will also be true if both are
|
||||
/// `None`
|
||||
expr_eq: bool,
|
||||
}
|
||||
|
||||
/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
|
||||
/// abort any further processing and avoid duplicate lint triggers.
|
||||
fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option<BlockEqual> {
|
||||
let mut start_eq = usize::MAX;
|
||||
let mut end_eq = usize::MAX;
|
||||
let mut expr_eq = true;
|
||||
let mut iter = blocks.windows(2).enumerate();
|
||||
while let Some((i, &[block0, block1])) = iter.next() {
|
||||
let l_stmts = block0.stmts;
|
||||
let r_stmts = block1.stmts;
|
||||
|
||||
// `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
|
||||
// The comparison therefore needs to be done in a way that builds the correct context.
|
||||
let mut evaluator = SpanlessEq::new(cx);
|
||||
let mut evaluator = evaluator.inter_expr();
|
||||
|
||||
let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r));
|
||||
|
||||
let current_end_eq = {
|
||||
// We skip the middle statements which can't be equal
|
||||
let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq;
|
||||
let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count);
|
||||
let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count);
|
||||
it1.zip(it2)
|
||||
.fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
|
||||
};
|
||||
let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r));
|
||||
|
||||
// IF_SAME_THEN_ELSE
|
||||
if_chain! {
|
||||
if block_expr_eq;
|
||||
if l_stmts.len() == r_stmts.len();
|
||||
if l_stmts.len() == current_start_eq;
|
||||
// `conds` may have one last item than `blocks`.
|
||||
// Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration.
|
||||
if !matches!(conds[i].kind, ExprKind::Let(..));
|
||||
if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..)));
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id);
|
||||
if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id);
|
||||
then {
|
||||
fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool {
|
||||
let mut eq = SpanlessEq::new(cx);
|
||||
blocks
|
||||
.array_windows::<2>()
|
||||
.enumerate()
|
||||
.fold(true, |all_eq, (i, &[lhs, rhs])| {
|
||||
if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
IF_SAME_THEN_ELSE,
|
||||
block0.span,
|
||||
lhs.span,
|
||||
"this `if` has identical blocks",
|
||||
Some(block1.span),
|
||||
Some(rhs.span),
|
||||
"same as this",
|
||||
);
|
||||
|
||||
return None;
|
||||
all_eq
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
start_eq = start_eq.min(current_start_eq);
|
||||
end_eq = end_eq.min(current_end_eq);
|
||||
expr_eq &= block_expr_eq;
|
||||
}
|
||||
|
||||
if !expr_eq {
|
||||
end_eq = 0;
|
||||
}
|
||||
|
||||
// Check if the regions are overlapping. Set `end_eq` to prevent the overlap
|
||||
let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap();
|
||||
if (start_eq + end_eq) > min_block_size {
|
||||
end_eq = min_block_size - start_eq;
|
||||
}
|
||||
|
||||
Some(BlockEqual {
|
||||
start_eq,
|
||||
end_eq,
|
||||
expr_eq,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
|
||||
fn lint_branches_sharing_code<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
conds: &[&'tcx Expr<'_>],
|
||||
blocks: &[&Block<'tcx>],
|
||||
expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
// We only lint ifs with multiple blocks
|
||||
let &[first_block, ref blocks @ ..] = blocks else {
|
||||
return;
|
||||
};
|
||||
let &[.., last_block] = blocks else {
|
||||
return;
|
||||
};
|
||||
|
||||
let res = scan_block_for_eq(cx, conds, first_block, blocks);
|
||||
let sm = cx.tcx.sess.source_map();
|
||||
let start_suggestion = res.start_span(first_block, sm).map(|span| {
|
||||
let first_line_span = first_line_of_span(cx, expr.span);
|
||||
let replace_span = first_line_span.with_hi(span.hi());
|
||||
let cond_span = first_line_span.until(first_block.span);
|
||||
let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
|
||||
let cond_indent = indent_of(cx, cond_span);
|
||||
let moved_snippet = reindent_multiline(snippet(cx, span, "_"), true, None);
|
||||
let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
|
||||
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
|
||||
(replace_span, suggestion.to_string())
|
||||
});
|
||||
let end_suggestion = res.end_span(last_block, sm).map(|span| {
|
||||
let moved_snipped = reindent_multiline(snippet(cx, span, "_"), true, None);
|
||||
let indent = indent_of(cx, expr.span.shrink_to_hi());
|
||||
let suggestion = "}\n".to_string() + &moved_snipped;
|
||||
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
|
||||
|
||||
let span = span.with_hi(last_block.span.hi());
|
||||
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
||||
let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
|
||||
let span = if snippet_opt(cx, test_span).map_or(false, |snip| snip == " ") {
|
||||
span.with_lo(test_span.lo())
|
||||
} else {
|
||||
span
|
||||
};
|
||||
(span, suggestion.to_string())
|
||||
});
|
||||
|
||||
let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) {
|
||||
(&Some((span, _)), &Some((end_span, _))) => (
|
||||
span,
|
||||
"all if blocks contain the same code at both the start and the end",
|
||||
Some(end_span),
|
||||
),
|
||||
(&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None),
|
||||
(None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None),
|
||||
(None, None) => return,
|
||||
};
|
||||
span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| {
|
||||
if let Some(span) = end_span {
|
||||
diag.span_note(span, "this code is shared at the end");
|
||||
}
|
||||
if let Some((span, sugg)) = start_suggestion {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"consider moving these statements before the if",
|
||||
sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
if let Some((span, sugg)) = end_suggestion {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"consider moving these statements after the if",
|
||||
sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
if !cx.typeck_results().expr_ty(expr).is_unit() {
|
||||
diag.note("the end suggestion probably needs some adjustments to use the expression result correctly");
|
||||
}
|
||||
}
|
||||
if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) {
|
||||
diag.warn("some moved values might need to be renamed to avoid wrong references");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
struct BlockEq {
|
||||
/// The end of the range of equal stmts at the start.
|
||||
start_end_eq: usize,
|
||||
/// The start of the range of equal stmts at the end.
|
||||
end_begin_eq: Option<usize>,
|
||||
/// The name and id of every local which can be moved at the beginning and the end.
|
||||
moved_locals: Vec<(HirId, Symbol)>,
|
||||
}
|
||||
impl BlockEq {
|
||||
fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option<Span> {
|
||||
match &b.stmts[..self.start_end_eq] {
|
||||
[first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
|
||||
[s] => Some(sm.stmt_span(s.span, b.span)),
|
||||
[] => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option<Span> {
|
||||
match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) {
|
||||
([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
|
||||
([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))),
|
||||
([s], None) => Some(sm.stmt_span(s.span, b.span)),
|
||||
([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())),
|
||||
([], None) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If the statement is a local, checks if the bound names match the expected list of names.
|
||||
fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||
if let StmtKind::Local(l) = s.kind {
|
||||
let mut i = 0usize;
|
||||
let mut res = true;
|
||||
l.pat.each_binding_or_first(&mut |_, _, _, name| {
|
||||
if names.get(i).map_or(false, |&(_, n)| n == name.name) {
|
||||
i += 1;
|
||||
} else {
|
||||
res = false;
|
||||
}
|
||||
});
|
||||
res && i == names.len()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given statement should be considered equal to the statement in the same position
|
||||
/// for each block.
|
||||
fn eq_stmts(
|
||||
stmt: &Stmt<'_>,
|
||||
blocks: &[&Block<'_>],
|
||||
get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>,
|
||||
eq: &mut HirEqInterExpr<'_, '_, '_>,
|
||||
moved_bindings: &mut Vec<(HirId, Symbol)>,
|
||||
) -> bool {
|
||||
(if let StmtKind::Local(l) = stmt.kind {
|
||||
let old_count = moved_bindings.len();
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, name| {
|
||||
moved_bindings.push((id, name.name));
|
||||
});
|
||||
let new_bindings = &moved_bindings[old_count..];
|
||||
blocks
|
||||
.iter()
|
||||
.all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings)))
|
||||
} else {
|
||||
true
|
||||
}) && blocks
|
||||
.iter()
|
||||
.all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
|
||||
}
|
||||
|
||||
fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq {
|
||||
let mut eq = SpanlessEq::new(cx);
|
||||
let mut eq = eq.inter_expr();
|
||||
let mut moved_locals = Vec::new();
|
||||
|
||||
let start_end_eq = block
|
||||
.stmts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals))
|
||||
.map_or(block.stmts.len(), |(i, _)| i);
|
||||
|
||||
// Walk backwards through the final expression/statements so long as their hashes are equal. Note
|
||||
// `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
|
||||
// to match those in other blocks. e.g. If each block ends with the following the hash value will be
|
||||
// the same even though each `x` binding will have a different `HirId`:
|
||||
// let x = foo();
|
||||
// x + 50
|
||||
let expr_hash_eq = if let Some(e) = block.expr {
|
||||
let hash = hash_expr(cx, e);
|
||||
blocks
|
||||
.iter()
|
||||
.all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash))
|
||||
} else {
|
||||
blocks.iter().all(|b| b.expr.is_none())
|
||||
};
|
||||
if !expr_hash_eq {
|
||||
return BlockEq {
|
||||
start_end_eq,
|
||||
end_begin_eq: None,
|
||||
moved_locals,
|
||||
};
|
||||
}
|
||||
let end_search_start = block.stmts[start_end_eq..]
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|&(offset, stmt)| {
|
||||
let hash = hash_stmt(cx, stmt);
|
||||
blocks.iter().any(|b| {
|
||||
b.stmts
|
||||
// the bounds check will catch the underflow
|
||||
.get(b.stmts.len().wrapping_sub(offset + 1))
|
||||
.map_or(true, |s| hash != hash_stmt(cx, s))
|
||||
})
|
||||
})
|
||||
.map_or(block.stmts.len() - start_end_eq, |(i, _)| i);
|
||||
|
||||
let moved_locals_at_start = moved_locals.len();
|
||||
let mut i = end_search_start;
|
||||
let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..]
|
||||
.iter()
|
||||
.zip(iter::repeat_with(move || {
|
||||
let x = i;
|
||||
i -= 1;
|
||||
x
|
||||
}))
|
||||
.fold(end_search_start, |init, (stmt, offset)| {
|
||||
if eq_stmts(
|
||||
stmt,
|
||||
blocks,
|
||||
|b| b.stmts.get(b.stmts.len() - offset),
|
||||
&mut eq,
|
||||
&mut moved_locals,
|
||||
) {
|
||||
init
|
||||
} else {
|
||||
// Clear out all locals seen at the end so far. None of them can be moved.
|
||||
let stmts = &blocks[0].stmts;
|
||||
for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] {
|
||||
if let StmtKind::Local(l) = stmt.kind {
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||
eq.locals.remove(&id);
|
||||
});
|
||||
}
|
||||
}
|
||||
moved_locals.truncate(moved_locals_at_start);
|
||||
offset - 1
|
||||
}
|
||||
});
|
||||
if let Some(e) = block.expr {
|
||||
for block in blocks {
|
||||
if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) {
|
||||
moved_locals.truncate(moved_locals_at_start);
|
||||
return BlockEq {
|
||||
start_end_eq,
|
||||
end_begin_eq: None,
|
||||
moved_locals,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockEq {
|
||||
start_end_eq,
|
||||
end_begin_eq: Some(end_begin_eq),
|
||||
moved_locals,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
|
||||
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
|
||||
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
|
||||
|
||||
symbols
|
||||
.iter()
|
||||
.filter(|sym| !sym.as_str().starts_with('_'))
|
||||
.any(move |sym| {
|
||||
let mut walker = ContainsName {
|
||||
name: *sym,
|
||||
result: false,
|
||||
};
|
||||
.filter(|&&(_, name)| !name.as_str().starts_with('_'))
|
||||
.any(|&(_, name)| {
|
||||
let mut walker = ContainsName { name, result: false };
|
||||
|
||||
// Scan block
|
||||
block
|
||||
|
@ -419,194 +491,9 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symb
|
|||
})
|
||||
}
|
||||
|
||||
fn emit_branches_sharing_code_lint(
|
||||
cx: &LateContext<'_>,
|
||||
start_stmts: usize,
|
||||
end_stmts: usize,
|
||||
lint_end: bool,
|
||||
warn_about_moved_symbol: bool,
|
||||
blocks: &[&Block<'_>],
|
||||
if_expr: &Expr<'_>,
|
||||
) {
|
||||
if start_stmts == 0 && !lint_end {
|
||||
return;
|
||||
}
|
||||
|
||||
// (help, span, suggestion)
|
||||
let mut suggestions: Vec<(&str, Span, String)> = vec![];
|
||||
let mut add_expr_note = false;
|
||||
|
||||
// Construct suggestions
|
||||
let sm = cx.sess().source_map();
|
||||
if start_stmts > 0 {
|
||||
let block = blocks[0];
|
||||
let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo();
|
||||
let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span);
|
||||
|
||||
let cond_span = first_line_of_span(cx, if_expr.span).until(block.span);
|
||||
let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
|
||||
let cond_indent = indent_of(cx, cond_span);
|
||||
let moved_span = block.stmts[0].span.source_callsite().to(span_end);
|
||||
let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
|
||||
let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
|
||||
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
|
||||
|
||||
let span = span_start.to(span_end);
|
||||
suggestions.push(("start", span, suggestion.to_string()));
|
||||
}
|
||||
|
||||
if lint_end {
|
||||
let block = blocks[blocks.len() - 1];
|
||||
let span_end = block.span.shrink_to_hi();
|
||||
|
||||
let moved_start = if end_stmts == 0 && block.expr.is_some() {
|
||||
block.expr.unwrap().span.source_callsite()
|
||||
} else {
|
||||
sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span)
|
||||
};
|
||||
let moved_end = block.expr.map_or_else(
|
||||
|| sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span),
|
||||
|expr| expr.span.source_callsite(),
|
||||
);
|
||||
|
||||
let moved_span = moved_start.to(moved_end);
|
||||
let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
|
||||
let indent = indent_of(cx, if_expr.span.shrink_to_hi());
|
||||
let suggestion = "}\n".to_string() + &moved_snipped;
|
||||
let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
|
||||
|
||||
let mut span = moved_start.to(span_end);
|
||||
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
||||
let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
|
||||
if snippet_opt(cx, test_span)
|
||||
.map(|snip| snip == " ")
|
||||
.unwrap_or_default()
|
||||
{
|
||||
span = span.with_lo(test_span.lo());
|
||||
}
|
||||
|
||||
suggestions.push(("end", span, suggestion.to_string()));
|
||||
add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit();
|
||||
}
|
||||
|
||||
let add_optional_msgs = |diag: &mut Diagnostic| {
|
||||
if add_expr_note {
|
||||
diag.note("The end suggestion probably needs some adjustments to use the expression result correctly");
|
||||
}
|
||||
|
||||
if warn_about_moved_symbol {
|
||||
diag.warn("Some moved values might need to be renamed to avoid wrong references");
|
||||
}
|
||||
};
|
||||
|
||||
// Emit lint
|
||||
if suggestions.len() == 1 {
|
||||
let (place_str, span, sugg) = suggestions.pop().unwrap();
|
||||
let msg = format!("all if blocks contain the same code at the {}", place_str);
|
||||
let help = format!("consider moving the {} statements out like this", place_str);
|
||||
span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| {
|
||||
diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified);
|
||||
|
||||
add_optional_msgs(diag);
|
||||
});
|
||||
} else if suggestions.len() == 2 {
|
||||
let (_, end_span, end_sugg) = suggestions.pop().unwrap();
|
||||
let (_, start_span, start_sugg) = suggestions.pop().unwrap();
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
BRANCHES_SHARING_CODE,
|
||||
start_span,
|
||||
"all if blocks contain the same code at the start and the end. Here at the start",
|
||||
move |diag| {
|
||||
diag.span_note(end_span, "and here at the end");
|
||||
|
||||
diag.span_suggestion(
|
||||
start_span,
|
||||
"consider moving the start statements out like this",
|
||||
start_sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
end_span,
|
||||
"and consider moving the end statements out like this",
|
||||
end_sugg,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
||||
add_optional_msgs(diag);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values.
|
||||
struct UsedValueFinderVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
|
||||
/// The `HirId`s of defined values in the scanned statements
|
||||
defs: FxHashSet<HirId>,
|
||||
|
||||
/// The Symbols of the defined symbols in the scanned statements
|
||||
def_symbols: FxHashSet<Symbol>,
|
||||
|
||||
/// The `HirId`s of the used values
|
||||
uses: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>) -> Self {
|
||||
UsedValueFinderVisitor {
|
||||
cx,
|
||||
defs: FxHashSet::default(),
|
||||
def_symbols: FxHashSet::default(),
|
||||
uses: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) {
|
||||
let local_id = l.pat.hir_id;
|
||||
self.defs.insert(local_id);
|
||||
|
||||
if let Some(sym) = l.pat.simple_ident() {
|
||||
self.def_symbols.insert(sym.name);
|
||||
}
|
||||
|
||||
if let Some(expr) = l.init {
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
|
||||
if let rustc_hir::QPath::Resolved(_, path) = *qpath {
|
||||
if path.segments.len() == 1 {
|
||||
if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) {
|
||||
self.uses.insert(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `IFS_SAME_COND`.
|
||||
fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
||||
let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(expr);
|
||||
h.finish()
|
||||
};
|
||||
|
||||
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
|
||||
|
||||
for (i, j) in search_same(conds, hash, eq) {
|
||||
for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
IFS_SAME_COND,
|
||||
|
@ -620,12 +507,6 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
|||
|
||||
/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
|
||||
fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
||||
let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(expr);
|
||||
h.finish()
|
||||
};
|
||||
|
||||
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
|
||||
// Do not lint if any expr originates from a macro
|
||||
if lhs.span.from_expansion() || rhs.span.from_expansion() {
|
||||
|
@ -638,7 +519,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
|||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
};
|
||||
|
||||
for (i, j) in search_same(conds, hash, eq) {
|
||||
for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
|
|
|
@ -15,12 +15,12 @@ declare_clippy_lint! {
|
|||
/// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// std::fs::create_dir("foo");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// std::fs::create_dir_all("foo");
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
|
|
|
@ -18,10 +18,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// dbg!(true)
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// true
|
||||
/// ```
|
||||
#[clippy::version = "1.34.0"]
|
||||
|
|
|
@ -18,15 +18,16 @@ declare_clippy_lint! {
|
|||
/// Checks for literal calls to `Default::default()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's more clear to the reader to use the name of the type whose default is
|
||||
/// being gotten than the generic `Default`.
|
||||
/// It's easier for the reader if the name of the type is used, rather than the
|
||||
/// generic `Default`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let s: String = Default::default();
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let s = String::default();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -47,13 +48,13 @@ declare_clippy_lint! {
|
|||
/// Assignments to patterns that are of tuple type are not linted.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```
|
||||
/// # #[derive(Default)]
|
||||
/// # struct A { i: i32 }
|
||||
/// let mut a: A = Default::default();
|
||||
/// a.i = 42;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```
|
||||
/// # #[derive(Default)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::numeric_literal;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
|
@ -76,7 +76,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Check whether a passed literal has potential to cause fallback or not.
|
||||
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
|
||||
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
|
||||
if_chain! {
|
||||
if !in_external_macro(self.cx.sess(), lit.span);
|
||||
if let Some(ty_bound) = self.ty_bounds.last();
|
||||
|
@ -101,14 +101,15 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
}
|
||||
};
|
||||
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
|
||||
span_lint_and_sugg(
|
||||
span_lint_hir_and_then(
|
||||
self.cx,
|
||||
DEFAULT_NUMERIC_FALLBACK,
|
||||
emit_hir_id,
|
||||
lit.span,
|
||||
"default numeric fallback might occur",
|
||||
"consider adding suffix",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
|diag| {
|
||||
diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
|
||||
ExprKind::Lit(lit) => {
|
||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||
self.check_lit(lit, ty);
|
||||
self.check_lit(lit, ty, expr.hir_id);
|
||||
return;
|
||||
},
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::peel_mid_ty_refs;
|
||||
|
@ -30,13 +30,14 @@ declare_clippy_lint! {
|
|||
/// let a: &mut String = &mut String::from("foo");
|
||||
/// let b: &str = a.deref();
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a: &mut String = &mut String::from("foo");
|
||||
/// let b = &*a;
|
||||
/// ```
|
||||
///
|
||||
/// This lint excludes
|
||||
/// This lint excludes:
|
||||
/// ```rust,ignore
|
||||
/// let _ = d.unwrap().deref();
|
||||
/// ```
|
||||
|
@ -59,11 +60,13 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// fn fun(_a: &i32) {}
|
||||
///
|
||||
/// // Bad
|
||||
/// let x: &i32 = &&&&&&5;
|
||||
/// fun(&x);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn fun(_a: &i32) {}
|
||||
/// let x: &i32 = &5;
|
||||
/// fun(x);
|
||||
/// ```
|
||||
|
@ -82,13 +85,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = Some("");
|
||||
/// if let Some(ref x) = x {
|
||||
/// // use `x` here
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = Some("");
|
||||
/// if let Some(x) = x {
|
||||
/// // use `&x` here
|
||||
|
@ -131,6 +135,7 @@ pub struct Dereferencing {
|
|||
struct StateData {
|
||||
/// Span of the top level expression
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
}
|
||||
|
||||
enum State {
|
||||
|
@ -165,6 +170,8 @@ struct RefPat {
|
|||
app: Applicability,
|
||||
/// All the replacements which need to be made.
|
||||
replacements: Vec<(Span, String)>,
|
||||
/// The [`HirId`] that the lint should be emitted at.
|
||||
hir_id: HirId,
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
|
@ -218,7 +225,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
|
||||
target_mut,
|
||||
},
|
||||
StateData { span: expr.span },
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
},
|
||||
));
|
||||
},
|
||||
RefOp::AddrOf => {
|
||||
|
@ -290,7 +300,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
required_precedence,
|
||||
msg,
|
||||
},
|
||||
StateData { span: expr.span },
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
},
|
||||
));
|
||||
}
|
||||
},
|
||||
|
@ -383,6 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
spans: vec![pat.span],
|
||||
app,
|
||||
replacements: vec![(pat.span, snip.into())],
|
||||
hir_id: pat.hir_id
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -395,13 +409,15 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
||||
let replacements = pat.replacements;
|
||||
let app = pat.app;
|
||||
span_lint_and_then(
|
||||
let lint = if pat.always_deref {
|
||||
NEEDLESS_BORROW
|
||||
} else {
|
||||
REF_BINDING_TO_REFERENCE
|
||||
};
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
if pat.always_deref {
|
||||
NEEDLESS_BORROW
|
||||
} else {
|
||||
REF_BINDING_TO_REFERENCE
|
||||
},
|
||||
lint,
|
||||
pat.hir_id,
|
||||
pat.spans,
|
||||
"this pattern creates a reference to a reference",
|
||||
|diag| {
|
||||
|
@ -638,19 +654,14 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S
|
|||
} => {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_BORROW,
|
||||
data.span,
|
||||
msg,
|
||||
"change this to",
|
||||
if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
|
||||
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| {
|
||||
let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
|
||||
format!("({})", snip)
|
||||
} else {
|
||||
snip.into()
|
||||
},
|
||||
app,
|
||||
);
|
||||
};
|
||||
diag.span_suggestion(data.span, "change this to", sugg, app);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[derive(Default)]
|
||||
/// struct Foo {
|
||||
|
@ -45,7 +44,6 @@ declare_clippy_lint! {
|
|||
/// specialized than what derive will produce. This lint can't detect the manual `impl`
|
||||
/// has exactly equal bounds, and therefore this lint is disabled for types with
|
||||
/// generic parameters.
|
||||
///
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub DERIVABLE_IMPLS,
|
||||
complexity,
|
||||
|
|
|
@ -4,14 +4,19 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
|||
use clippy_utils::{is_lint_allowed, match_def_path};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety,
|
||||
self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
|
||||
Unsafety,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::subst::GenericArg;
|
||||
use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty};
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{
|
||||
self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
|
||||
Ty, TyCtxt, Visibility,
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
@ -459,50 +464,18 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
if_chain! {
|
||||
if let ty::Adt(adt, substs) = ty.kind();
|
||||
if cx.tcx.visibility(adt.did()) == Visibility::Public;
|
||||
if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
|
||||
if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq);
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
|
||||
// New `ParamEnv` replacing `T: PartialEq` with `T: Eq`
|
||||
let param_env = ParamEnv::new(
|
||||
cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| {
|
||||
let kind = p.kind();
|
||||
match kind.skip_binder() {
|
||||
PredicateKind::Trait(p)
|
||||
if p.trait_ref.def_id == peq_trait_def_id
|
||||
&& p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1)
|
||||
&& matches!(p.trait_ref.self_ty().kind(), ty::Param(_))
|
||||
&& p.constness == BoundConstness::NotConst
|
||||
&& p.polarity == ImplPolarity::Positive =>
|
||||
{
|
||||
cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate {
|
||||
trait_ref: TraitRef::new(
|
||||
eq_trait_def_id,
|
||||
cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()),
|
||||
),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
})))
|
||||
},
|
||||
_ => p,
|
||||
}
|
||||
})),
|
||||
cx.param_env.reveal(),
|
||||
cx.param_env.constness(),
|
||||
);
|
||||
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs);
|
||||
let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
|
||||
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
|
||||
// If all of our fields implement `Eq`, we can implement `Eq` too
|
||||
if adt
|
||||
.all_fields()
|
||||
.map(|f| f.ty(cx.tcx, substs))
|
||||
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
|
||||
then {
|
||||
// If all of our fields implement `Eq`, we can implement `Eq` too
|
||||
for variant in adt.variants() {
|
||||
for field in &variant.fields {
|
||||
let ty = field.ty(cx.tcx, substs);
|
||||
|
||||
if !implements_trait(cx, ty, eq_trait_def_id, substs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
|
@ -515,3 +488,41 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
|
||||
fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
|
||||
// Initial map from generic index to param def.
|
||||
// Vec<(param_def, needs_eq)>
|
||||
let mut params = tcx
|
||||
.generics_of(did)
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ty_predicates = tcx.predicates_of(did).predicates;
|
||||
for (p, _) in ty_predicates {
|
||||
if let PredicateKind::Trait(p) = p.kind().skip_binder()
|
||||
&& p.trait_ref.def_id == eq_trait_id
|
||||
&& let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
|
||||
&& p.constness == BoundConstness::NotConst
|
||||
{
|
||||
// Flag types which already have an `Eq` bound.
|
||||
params[self_ty.index as usize].1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
ParamEnv::new(
|
||||
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
|
||||
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
|
||||
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
|
||||
trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
})))
|
||||
}),
|
||||
)),
|
||||
Reveal::UserFacing,
|
||||
Constness::NotConst,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
#[clippy::version = "1.51.0"]
|
||||
pub MISSING_PANICS_DOC,
|
||||
pedantic,
|
||||
"`pub fn` may panic without `# Panics` in doc comment"
|
||||
|
@ -178,7 +178,7 @@ declare_clippy_lint! {
|
|||
/// if the `fn main()` is left implicit.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ``````rust
|
||||
/// ```rust
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// /// # Examples
|
||||
|
@ -191,7 +191,7 @@ declare_clippy_lint! {
|
|||
/// fn needless_main() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ``````
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub NEEDLESS_DOCTEST_MAIN,
|
||||
style,
|
||||
|
|
|
@ -60,7 +60,8 @@ declare_clippy_lint! {
|
|||
/// struct BlackForestCake;
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// mod cake {
|
||||
/// struct BlackForest;
|
||||
|
|
|
@ -52,15 +52,13 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// It is more idiomatic to dereference the other argument.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// None
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// ```rust,ignore
|
||||
/// &x == y
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// x == *y
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
|
|
@ -34,14 +34,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// xs.map(|x| foo(x))
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // where `foo(_)` is a plain function that takes the exact argument type of `x`.
|
||||
/// xs.map(foo)
|
||||
/// ```
|
||||
/// where `foo(_)` is a plain function that takes the exact argument type of
|
||||
/// `x`.
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub REDUNDANT_CLOSURE,
|
||||
style,
|
||||
|
|
|
@ -54,12 +54,11 @@ declare_clippy_lint! {
|
|||
/// API easier to use.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// fn f(is_round: bool, is_hot: bool) { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// enum Shape {
|
||||
/// Round,
|
||||
|
|
|
@ -45,10 +45,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let _: f32 = 16_777_217.0; // 16_777_216.0
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let _: f32 = 16_777_216.0;
|
||||
/// let _: f64 = 16_777_217.0;
|
||||
/// ```
|
||||
|
|
|
@ -76,12 +76,13 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// pub fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// pub unsafe fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
|
|
|
@ -20,13 +20,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.get(0);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.first();
|
||||
/// ```
|
||||
|
|
|
@ -16,17 +16,21 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let end: u32 = 10;
|
||||
/// let start: u32 = 5;
|
||||
///
|
||||
/// # let end: u32 = 10;
|
||||
/// # let start: u32 = 5;
|
||||
/// let mut i: u32 = end - start;
|
||||
///
|
||||
/// // Bad
|
||||
/// if i != 0 {
|
||||
/// i -= 1;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let end: u32 = 10;
|
||||
/// # let start: u32 = 5;
|
||||
/// let mut i: u32 = end - start;
|
||||
///
|
||||
/// // Good
|
||||
/// i = i.saturating_sub(1);
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
|
@ -48,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
// Check if the conditional expression is a binary operation
|
||||
if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
|
||||
|
||||
// Ensure that the binary operator is >, != and <
|
||||
// Ensure that the binary operator is >, !=, or <
|
||||
if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
|
||||
|
||||
// Check if assign operation is done
|
||||
|
|
|
@ -45,7 +45,7 @@ declare_clippy_lint! {
|
|||
/// println!("{}", first);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub INDEX_REFUTABLE_SLICE,
|
||||
nursery,
|
||||
"avoid indexing on slices which could be destructed"
|
||||
|
|
|
@ -17,19 +17,20 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// This will always panic at runtime.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Hopefully none.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// ```rust,no_run
|
||||
/// # #![allow(const_err)]
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = [1, 2, 3, 4];
|
||||
/// // Index within bounds
|
||||
///
|
||||
/// // Good
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
|
@ -49,42 +50,32 @@ declare_clippy_lint! {
|
|||
/// Indexing and slicing can panic at runtime and there are
|
||||
/// safe alternatives.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Hopefully none.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// // Vector
|
||||
/// let x = vec![0; 5];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[2];
|
||||
/// &x[2..100];
|
||||
/// &x[2..];
|
||||
/// &x[..100];
|
||||
///
|
||||
/// // Good
|
||||
/// x.get(2);
|
||||
/// x.get(2..100);
|
||||
/// x.get(2..);
|
||||
/// x.get(..100);
|
||||
///
|
||||
/// // Array
|
||||
/// let y = [0, 1, 2, 3];
|
||||
///
|
||||
/// // Bad
|
||||
/// &y[10..100];
|
||||
/// &y[10..];
|
||||
/// &y[..100];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # #![allow(unused)]
|
||||
///
|
||||
/// # let x = vec![0; 5];
|
||||
/// # let y = [0, 1, 2, 3];
|
||||
/// x.get(2);
|
||||
/// x.get(2..100);
|
||||
///
|
||||
/// // Good
|
||||
/// &y[2..];
|
||||
/// &y[..2];
|
||||
/// &y[0..3];
|
||||
/// y.get(10);
|
||||
/// y.get(10..100);
|
||||
/// y.get(10..);
|
||||
/// y.get(..100);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INDEXING_SLICING,
|
||||
|
|
|
@ -41,6 +41,7 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// let infinite_iter = 0..;
|
||||
/// # #[allow(unused)]
|
||||
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
|
|
@ -14,12 +14,8 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// None
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// pub struct A;
|
||||
///
|
||||
/// impl A {
|
||||
|
@ -29,8 +25,8 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
|
@ -54,12 +50,8 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// None
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
|
@ -77,8 +69,8 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
|
|
|
@ -21,8 +21,7 @@ declare_clippy_lint! {
|
|||
/// if x >= y + 1 {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 1;
|
||||
|
|
|
@ -15,11 +15,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = 3 / 2;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = 3f32 / 2f32;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
|
|
|
@ -17,7 +17,6 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
|
@ -31,8 +30,8 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
|
|
|
@ -20,10 +20,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// pub const a = [0u32; 1_000_000];
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust.ignore
|
||||
/// pub static a = [0u32; 1_000_000];
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
|
|
|
@ -38,12 +38,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// enum Test {
|
||||
/// A(i32),
|
||||
/// B([i32; 8000]),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Possibly better
|
||||
/// enum Test2 {
|
||||
/// A(i32),
|
||||
|
|
|
@ -22,10 +22,11 @@ declare_clippy_lint! {
|
|||
/// let included_bytes = include_bytes!("very_large_file.txt");
|
||||
/// ```
|
||||
///
|
||||
/// Instead, you can load the file at runtime:
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// use std::fs;
|
||||
///
|
||||
/// // You can load the file at runtime
|
||||
/// let string = fs::read_to_string("very_large_file.txt")?;
|
||||
/// let bytes = fs::read("very_large_file.txt")?;
|
||||
/// ```
|
||||
|
|
|
@ -45,13 +45,11 @@ declare_clippy_lint! {
|
|||
/// `std::mem::drop` conveys your intention better and is less error-prone.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// let _ = mutex.lock();
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _lock = mutex.lock();
|
||||
/// ```
|
||||
|
@ -75,24 +73,20 @@ declare_clippy_lint! {
|
|||
/// better and is less error-prone.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// struct Droppable;
|
||||
/// impl Drop for Droppable {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
/// ```rust
|
||||
/// # struct DroppableItem;
|
||||
/// {
|
||||
/// let _ = Droppable;
|
||||
/// // ^ dropped here
|
||||
/// let _ = DroppableItem;
|
||||
/// // ^ dropped here
|
||||
/// /* more code */
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct DroppableItem;
|
||||
/// {
|
||||
/// let _droppable = Droppable;
|
||||
/// let _droppable = DroppableItem;
|
||||
/// /* more code */
|
||||
/// // dropped at end of scope
|
||||
/// }
|
||||
|
|
|
@ -242,6 +242,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(needless_bool::NEEDLESS_BOOL),
|
||||
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
|
||||
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
|
||||
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
|
||||
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
|
||||
LintId::of(needless_update::NEEDLESS_UPDATE),
|
||||
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
|
@ -270,6 +271,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
LintId::of(redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
|
|
|
@ -55,6 +55,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
|||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
LintId::of(ptr::MUT_FROM_REF),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
|
|
|
@ -408,6 +408,7 @@ store.register_lints(&[
|
|||
needless_continue::NEEDLESS_CONTINUE,
|
||||
needless_for_each::NEEDLESS_FOR_EACH,
|
||||
needless_late_init::NEEDLESS_LATE_INIT,
|
||||
needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
|
||||
needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
|
||||
needless_question_mark::NEEDLESS_QUESTION_MARK,
|
||||
needless_update::NEEDLESS_UPDATE,
|
||||
|
@ -458,6 +459,7 @@ store.register_lints(&[
|
|||
ranges::RANGE_ZIP_WITH_LEN,
|
||||
ranges::REVERSED_EMPTY_RANGES,
|
||||
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
|
||||
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
|
||||
redundant_clone::REDUNDANT_CLONE,
|
||||
redundant_closure_call::REDUNDANT_CLOSURE_CALL,
|
||||
redundant_else::REDUNDANT_ELSE,
|
||||
|
|
|
@ -91,6 +91,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
|
||||
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
|
||||
LintId::of(neg_multiply::NEG_MULTIPLY),
|
||||
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
|
||||
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// error-pattern:cargo-clippy
|
||||
|
||||
#![feature(array_windows)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
#![feature(box_patterns)]
|
||||
|
@ -88,10 +86,11 @@ use rustc_session::Session;
|
|||
/// ///
|
||||
/// /// ### Example
|
||||
/// /// ```rust
|
||||
/// /// // Bad
|
||||
/// /// Insert a short example of code that triggers the lint
|
||||
/// /// ```
|
||||
/// ///
|
||||
/// /// // Good
|
||||
/// /// Use instead:
|
||||
/// /// ```rust
|
||||
/// /// Insert a short example of improved code that doesn't trigger the lint
|
||||
/// /// ```
|
||||
/// pub LINT_NAME,
|
||||
|
@ -315,6 +314,7 @@ mod needless_borrowed_ref;
|
|||
mod needless_continue;
|
||||
mod needless_for_each;
|
||||
mod needless_late_init;
|
||||
mod needless_parens_on_range_literals;
|
||||
mod needless_pass_by_value;
|
||||
mod needless_question_mark;
|
||||
mod needless_update;
|
||||
|
@ -348,6 +348,7 @@ mod pub_use;
|
|||
mod question_mark;
|
||||
mod ranges;
|
||||
mod rc_clone_in_vec_init;
|
||||
mod read_zero_byte_vec;
|
||||
mod redundant_clone;
|
||||
mod redundant_closure_call;
|
||||
mod redundant_else;
|
||||
|
@ -746,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
|
||||
store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
|
||||
store.register_early_pass(|| Box::new(precedence::Precedence));
|
||||
store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
|
||||
store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
|
||||
store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
|
||||
store.register_late_pass(|| Box::new(create_dir::CreateDir));
|
||||
|
@ -907,6 +909,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
|
||||
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -36,12 +36,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad: unnecessary lifetime annotations
|
||||
/// // Unnecessary lifetime annotations
|
||||
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn elided(x: &u8, y: u8) -> &u8 {
|
||||
/// x
|
||||
/// }
|
||||
|
@ -65,12 +67,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad: unnecessary lifetimes
|
||||
/// // unnecessary lifetimes
|
||||
/// fn unused_lifetime<'a>(x: u8) {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn no_lifetime(x: u8) {
|
||||
/// // ...
|
||||
/// }
|
||||
|
|
|
@ -22,11 +22,16 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: u64 = 61864918973511;
|
||||
/// # let _: u64 =
|
||||
/// 61864918973511
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let x: u64 = 61_864_918_973_511;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let _: u64 =
|
||||
/// 61_864_918_973_511
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNREADABLE_LITERAL,
|
||||
|
@ -46,6 +51,7 @@ declare_clippy_lint! {
|
|||
/// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// `2_32` => `2_i32`
|
||||
/// `250_8 => `250_u8`
|
||||
/// ```
|
||||
|
@ -66,11 +72,16 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: u64 = 618_64_9189_73_511;
|
||||
/// # let _: u64 =
|
||||
/// 618_64_9189_73_511
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let x: u64 = 61_864_918_973_511;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let _: u64 =
|
||||
/// 61_864_918_973_511
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INCONSISTENT_DIGIT_GROUPING,
|
||||
|
@ -125,9 +136,11 @@ declare_clippy_lint! {
|
|||
/// readable than a decimal representation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```text
|
||||
/// `255` => `0xFF`
|
||||
/// `65_535` => `0xFFFF`
|
||||
/// `4_042_322_160` => `0xF0F0_F0F0`
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DECIMAL_LITERAL_REPRESENTATION,
|
||||
restriction,
|
||||
|
|
|
@ -7,9 +7,22 @@ use rustc_lint::LateContext;
|
|||
use rustc_span::symbol::sym;
|
||||
|
||||
/// Checks for `for` loops over `Option`s and `Result`s.
|
||||
pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) {
|
||||
let ty = cx.typeck_results().expr_ty(arg);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
let help_string = if let Some(method_name) = method_name {
|
||||
format!(
|
||||
"consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
)
|
||||
};
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FOR_LOOPS_OVER_FALLIBLES,
|
||||
|
@ -20,13 +33,22 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
|||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
None,
|
||||
&format!(
|
||||
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
&help_string,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
|
||||
let help_string = if let Some(method_name) = method_name {
|
||||
format!(
|
||||
"consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
)
|
||||
};
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FOR_LOOPS_OVER_FALLIBLES,
|
||||
|
@ -37,11 +59,7 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
|||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
None,
|
||||
&format!(
|
||||
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
|
||||
snippet(cx, pat.span, "_"),
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
&help_string,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,8 @@ declare_clippy_lint! {
|
|||
/// dst[i + 64] = src[i];
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
|
@ -70,7 +71,8 @@ declare_clippy_lint! {
|
|||
/// println!("{}", vec[i]);
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let vec = vec!['a', 'b', 'c'];
|
||||
/// for i in vec {
|
||||
|
@ -103,7 +105,8 @@ declare_clippy_lint! {
|
|||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten to
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let y = vec![1];
|
||||
/// for x in &y {
|
||||
|
@ -188,6 +191,10 @@ declare_clippy_lint! {
|
|||
/// for x in &res {
|
||||
/// // ..
|
||||
/// }
|
||||
///
|
||||
/// for x in res.iter() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
|
@ -282,7 +289,8 @@ declare_clippy_lint! {
|
|||
/// i += 1;
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let v = vec![1];
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
|
@ -469,7 +477,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Why is this bad?
|
||||
/// This kind of operation can be expressed more succinctly with
|
||||
/// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
|
||||
/// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
|
||||
/// have better performance.
|
||||
///
|
||||
/// ### Example
|
||||
|
@ -484,7 +492,8 @@ declare_clippy_lint! {
|
|||
/// vec.push(item2);
|
||||
/// }
|
||||
/// ```
|
||||
/// could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let item1 = 2;
|
||||
/// let item2 = 3;
|
||||
|
@ -512,7 +521,8 @@ declare_clippy_lint! {
|
|||
/// println!("{}", item);
|
||||
/// }
|
||||
/// ```
|
||||
/// could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let item1 = 2;
|
||||
/// let item = &item1;
|
||||
|
@ -586,7 +596,7 @@ declare_clippy_lint! {
|
|||
/// std::hint::spin_loop()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub MISSING_SPIN_LOOP,
|
||||
perf,
|
||||
"An empty busy waiting loop"
|
||||
|
@ -695,10 +705,14 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
|||
let method_name = method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
|
||||
"iter" | "iter_mut" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
|
||||
},
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
|
||||
},
|
||||
"next" => {
|
||||
next_loop_linted = iter_next_loop::check(cx, arg);
|
||||
|
@ -708,6 +722,6 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
|||
}
|
||||
|
||||
if !next_loop_linted {
|
||||
for_loops_over_fallibles::check(cx, pat, arg);
|
||||
for_loops_over_fallibles::check(cx, pat, arg, None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,13 +117,20 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
|||
| ExprKind::Type(e, _)
|
||||
| ExprKind::Field(e, _)
|
||||
| ExprKind::AddrOf(_, _, e)
|
||||
| ExprKind::Struct(_, _, Some(e))
|
||||
| ExprKind::Repeat(e, _)
|
||||
| ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
|
||||
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
|
||||
ExprKind::Array(es) | ExprKind::MethodCall(_, es, _) | ExprKind::Tup(es) => {
|
||||
never_loop_expr_all(&mut es.iter(), main_loop_id)
|
||||
},
|
||||
ExprKind::Struct(_, fields, base) => {
|
||||
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
|
||||
if let Some(base) = base {
|
||||
combine_both(fields, never_loop_expr(base, main_loop_id))
|
||||
} else {
|
||||
fields
|
||||
}
|
||||
},
|
||||
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
|
||||
ExprKind::Binary(_, e1, e2)
|
||||
| ExprKind::Assign(e1, e2, _)
|
||||
|
@ -180,8 +187,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
|||
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
|
||||
})
|
||||
.fold(NeverLoopResult::Otherwise, combine_both),
|
||||
ExprKind::Struct(_, _, None)
|
||||
| ExprKind::Yield(_, _)
|
||||
ExprKind::Yield(_, _)
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::Path(_)
|
||||
| ExprKind::ConstBlock(_)
|
||||
|
|
|
@ -110,14 +110,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
|||
arm1.span,
|
||||
"this match arm has an identical body to the `_` wildcard arm",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
arm1.span,
|
||||
"try removing the arm",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(arm2.span, "`_` wildcard arm here");
|
||||
diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(arm2.span, "`_` wildcard arm here");
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -43,13 +43,16 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # fn bar(stool: &str) {}
|
||||
/// # let x = Some("abc");
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn bar(stool: &str) {}
|
||||
/// # let x = Some("abc");
|
||||
/// if let Some(ref foo) = x {
|
||||
/// bar(foo);
|
||||
/// }
|
||||
|
@ -114,14 +117,15 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// &A(ref y) => foo(y),
|
||||
/// &B => bar(),
|
||||
/// _ => frob(&x),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// match *x {
|
||||
/// A(ref y) => foo(y),
|
||||
/// B => bar(),
|
||||
|
@ -227,13 +231,16 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// let x: Option<()> = None;
|
||||
///
|
||||
/// // Bad
|
||||
/// let r: Option<&()> = match x {
|
||||
/// None => None,
|
||||
/// Some(ref v) => Some(v),
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x: Option<()> = None;
|
||||
///
|
||||
/// // Good
|
||||
/// let r: Option<&()> = x.as_ref();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -257,13 +264,16 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # enum Foo { A(usize), B(usize) }
|
||||
/// # let x = Foo::B(1);
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Foo::A(_) => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # enum Foo { A(usize), B(usize) }
|
||||
/// # let x = Foo::B(1);
|
||||
/// match x {
|
||||
/// Foo::A(_) => {},
|
||||
/// Foo::B(_) => {},
|
||||
|
@ -290,14 +300,17 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # enum Foo { A, B, C }
|
||||
/// # let x = Foo::B;
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Foo::A => {},
|
||||
/// Foo::B => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # enum Foo { A, B, C }
|
||||
/// # let x = Foo::B;
|
||||
/// match x {
|
||||
/// Foo::A => {},
|
||||
/// Foo::B => {},
|
||||
|
@ -320,14 +333,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// match "foo" {
|
||||
/// # let s = "foo";
|
||||
/// match s {
|
||||
/// "a" => {},
|
||||
/// "bar" | _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// match "foo" {
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let s = "foo";
|
||||
/// match s {
|
||||
/// "a" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
|
@ -389,15 +405,17 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # let a = 1;
|
||||
/// # let b = 2;
|
||||
///
|
||||
/// // Bad
|
||||
/// match (a, b) {
|
||||
/// (c, d) => {
|
||||
/// // useless match
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let a = 1;
|
||||
/// # let b = 2;
|
||||
/// let (c, d) = (a, b);
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
|
@ -419,13 +437,16 @@ declare_clippy_lint! {
|
|||
/// # struct A { a: i32 }
|
||||
/// let a = A { a: 5 };
|
||||
///
|
||||
/// // Bad
|
||||
/// match a {
|
||||
/// A { a: 5, .. } => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct A { a: i32 }
|
||||
/// # let a = A { a: 5 };
|
||||
/// match a {
|
||||
/// A { a: 5 } => {},
|
||||
/// _ => {},
|
||||
|
@ -509,7 +530,6 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// let x = Some(5);
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = match x {
|
||||
/// Some(0) => true,
|
||||
/// _ => false,
|
||||
|
@ -520,8 +540,11 @@ declare_clippy_lint! {
|
|||
/// } else {
|
||||
/// false
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = Some(5);
|
||||
/// let a = matches!(x, Some(0));
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
|
@ -695,19 +718,18 @@ declare_clippy_lint! {
|
|||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Bad
|
||||
/// match arr[idx] {
|
||||
/// 0 => println!("{}", 0),
|
||||
/// 1 => println!("{}", 3),
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust, no_run
|
||||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Good
|
||||
/// match arr.get(idx) {
|
||||
/// Some(0) => println!("{}", 0),
|
||||
/// Some(1) => println!("{}", 3),
|
||||
|
|
|
@ -6,7 +6,7 @@ use if_chain::if_chain;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
|
@ -155,7 +155,15 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
false
|
||||
};
|
||||
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
|
||||
|
||||
if match map_arg.kind {
|
||||
ExprKind::MethodCall(method, [original_arg], _) => {
|
||||
acceptable_methods(method)
|
||||
&& SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg)
|
||||
},
|
||||
_ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg)
|
||||
};
|
||||
|
||||
then {
|
||||
let span = filter_span.with_hi(expr.span.hi());
|
||||
let (filter_name, lint) = if is_find {
|
||||
|
@ -171,3 +179,18 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn acceptable_methods(method: &PathSegment<'_>) -> bool {
|
||||
let methods: [Symbol; 8] = [
|
||||
sym::clone,
|
||||
sym::as_ref,
|
||||
sym!(copied),
|
||||
sym!(cloned),
|
||||
sym!(as_deref),
|
||||
sym!(as_mut),
|
||||
sym!(as_deref_mut),
|
||||
sym!(to_owned),
|
||||
];
|
||||
|
||||
methods.contains(&method.ident.name)
|
||||
}
|
||||
|
|
|
@ -1,70 +1,59 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy};
|
||||
use itertools::Itertools;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{get_associated_type, implements_trait, is_copy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
use std::ops::Not;
|
||||
|
||||
use super::ITER_OVEREAGER_CLONED;
|
||||
use crate::redundant_clone::REDUNDANT_CLONE;
|
||||
|
||||
/// lint overeager use of `cloned()` for `Iterator`s
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
name: &str,
|
||||
map_arg: &[hir::Expr<'_>],
|
||||
expr: &'tcx Expr<'_>,
|
||||
cloned_call: &'tcx Expr<'_>,
|
||||
cloned_recv: &'tcx Expr<'_>,
|
||||
is_count: bool,
|
||||
needs_into_iter: bool,
|
||||
) {
|
||||
// Check if it's iterator and get type associated with `Item`.
|
||||
let inner_ty = if_chain! {
|
||||
if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
if implements_trait(cx, recv_ty, iterator_trait_id, &[]);
|
||||
if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv));
|
||||
then {
|
||||
inner_ty
|
||||
} else {
|
||||
let typeck = cx.typeck_results();
|
||||
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||
&& let Some(method_id) = typeck.type_dependent_def_id(expr.hir_id)
|
||||
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
|
||||
&& let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id)
|
||||
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
|
||||
&& let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv)
|
||||
&& let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item")
|
||||
&& matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty))
|
||||
{
|
||||
if needs_into_iter
|
||||
&& let Some(into_iter_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||
&& !implements_trait(cx, iter_assoc_ty, into_iter_id, &[])
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match inner_ty.kind() {
|
||||
ty::Ref(_, ty, _) if !is_copy(cx, *ty) => {},
|
||||
_ => return,
|
||||
};
|
||||
let (lint, msg, trailing_clone) = if is_count {
|
||||
(REDUNDANT_CLONE, "unneeded cloning of iterator items", "")
|
||||
} else {
|
||||
(ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()")
|
||||
};
|
||||
|
||||
let (lint, preserve_cloned) = match name {
|
||||
"count" => (REDUNDANT_CLONE, false),
|
||||
_ => (ITER_OVEREAGER_CLONED, true),
|
||||
};
|
||||
let wildcard_params = map_arg.is_empty().not().then(|| "...").unwrap_or_default();
|
||||
let msg = format!(
|
||||
"called `cloned().{}({})` on an `Iterator`. It may be more efficient to call `{}({}){}` instead",
|
||||
name,
|
||||
wildcard_params,
|
||||
name,
|
||||
wildcard_params,
|
||||
preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&msg,
|
||||
"try this",
|
||||
format!(
|
||||
"{}.{}({}){}",
|
||||
snippet(cx, recv.span, ".."),
|
||||
name,
|
||||
map_arg.iter().map(|a| snippet(cx, a.span, "..")).join(", "),
|
||||
preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
msg,
|
||||
|diag| {
|
||||
let method_span = expr.span.with_lo(cloned_call.span.hi());
|
||||
if let Some(mut snip) = snippet_opt(cx, method_span) {
|
||||
snip.push_str(trailing_clone);
|
||||
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
||||
diag.span_suggestion(replace_span, "try this", snip, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,28 +124,24 @@ declare_clippy_lint! {
|
|||
/// It's often inefficient to clone all elements of an iterator, when eventually, only some
|
||||
/// of them will be consumed.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let vec = vec!["string".to_string()];
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().cloned().take(10);
|
||||
///
|
||||
/// // Good
|
||||
/// vec.iter().take(10).cloned();
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().cloned().last();
|
||||
///
|
||||
/// // Good
|
||||
/// vec.iter().last().cloned();
|
||||
///
|
||||
/// ```
|
||||
/// ### Known Problems
|
||||
/// This `lint` removes the side of effect of cloning items in the iterator.
|
||||
/// A code that relies on that side-effect could fail.
|
||||
///
|
||||
#[clippy::version = "1.59.0"]
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let vec = vec!["string".to_string()];
|
||||
/// vec.iter().cloned().take(10);
|
||||
/// vec.iter().cloned().last();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let vec = vec!["string".to_string()];
|
||||
/// vec.iter().take(10).cloned();
|
||||
/// vec.iter().last().cloned();
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub ITER_OVEREAGER_CLONED,
|
||||
perf,
|
||||
"using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
|
||||
|
@ -342,11 +338,12 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = Ok::<_, ()>(());
|
||||
///
|
||||
/// // Bad
|
||||
/// x.ok().expect("why did I do this again?");
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = Ok::<_, ()>(());
|
||||
/// x.expect("why did I do this again?");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -390,12 +387,13 @@ declare_clippy_lint! {
|
|||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let x = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// x.unwrap_or_else(Default::default);
|
||||
/// x.unwrap_or_else(u32::default);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = Some(1);
|
||||
/// x.unwrap_or_default();
|
||||
/// ```
|
||||
#[clippy::version = "1.56.0"]
|
||||
|
@ -453,11 +451,12 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// opt.map_or(None, |a| Some(a + 1));
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
/// opt.and_then(|a| Some(a + 1));
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -475,13 +474,12 @@ declare_clippy_lint! {
|
|||
/// `_.ok()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// # let r: Result<u32, &str> = Ok(1);
|
||||
/// assert_eq!(Some(1), r.map_or(None, Some));
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let r: Result<u32, &str> = Ok(1);
|
||||
/// assert_eq!(Some(1), r.ok());
|
||||
|
@ -538,7 +536,8 @@ declare_clippy_lint! {
|
|||
/// # let vec = vec![1];
|
||||
/// vec.iter().filter(|x| **x == 0).next();
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().find(|x| **x == 0);
|
||||
|
@ -562,7 +561,8 @@ declare_clippy_lint! {
|
|||
/// # let vec = vec![1];
|
||||
/// vec.iter().skip_while(|x| **x == 0).next();
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().find(|x| **x != 0);
|
||||
|
@ -586,11 +586,14 @@ declare_clippy_lint! {
|
|||
/// let vec = vec![vec![1]];
|
||||
/// let opt = Some(5);
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().map(|x| x.iter()).flatten();
|
||||
/// opt.map(|x| Some(x * 2)).flatten();
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let vec = vec![vec![1]];
|
||||
/// # let opt = Some(5);
|
||||
/// vec.iter().flat_map(|x| x.iter());
|
||||
/// opt.and_then(|x| Some(x * 2));
|
||||
/// ```
|
||||
|
@ -610,15 +613,16 @@ declare_clippy_lint! {
|
|||
/// less performant.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// # #![allow(unused)]
|
||||
/// (0_i32..10)
|
||||
/// .filter(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # #[allow(unused)]
|
||||
/// (0_i32..10).filter_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
#[clippy::version = "1.51.0"]
|
||||
|
@ -637,14 +641,13 @@ declare_clippy_lint! {
|
|||
/// less performant.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// (0_i32..10)
|
||||
/// .find(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// (0_i32..10).find_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
|
@ -712,17 +715,20 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # #![allow(unused)]
|
||||
/// let vec = vec![1];
|
||||
/// vec.iter().find(|x| **x == 0).is_some();
|
||||
///
|
||||
/// let _ = "hello world".find("world").is_none();
|
||||
/// "hello world".find("world").is_none();
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let vec = vec![1];
|
||||
/// vec.iter().any(|x| *x == 0);
|
||||
///
|
||||
/// let _ = !"hello world".contains("world");
|
||||
/// # #[allow(unused)]
|
||||
/// !"hello world".contains("world");
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub SEARCH_IS_SOME,
|
||||
|
@ -744,7 +750,8 @@ declare_clippy_lint! {
|
|||
/// let name = "foo";
|
||||
/// if name.chars().next() == Some('_') {};
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let name = "foo";
|
||||
/// if name.starts_with('_') {};
|
||||
|
@ -899,10 +906,13 @@ declare_clippy_lint! {
|
|||
/// # use std::rc::Rc;
|
||||
/// let x = Rc::new(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// x.clone();
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::rc::Rc;
|
||||
/// # let x = Rc::new(1);
|
||||
/// Rc::clone(&x);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -1034,11 +1044,13 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// _.split("x");
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// _.split('x');
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub SINGLE_CHAR_PATTERN,
|
||||
perf,
|
||||
|
@ -1099,12 +1111,14 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::collections::HashSet;
|
||||
/// // Bad
|
||||
/// # let mut s = HashSet::new();
|
||||
/// # s.insert(1);
|
||||
/// let x = s.iter().nth(0);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::collections::HashSet;
|
||||
/// # let mut s = HashSet::new();
|
||||
/// # s.insert(1);
|
||||
/// let x = s.iter().next();
|
||||
|
@ -1210,11 +1224,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.get(x.len() - 1);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.last();
|
||||
/// ```
|
||||
|
@ -1273,10 +1288,14 @@ declare_clippy_lint! {
|
|||
/// let mut a = vec![1, 2, 3];
|
||||
/// let mut b = vec![4, 5, 6];
|
||||
///
|
||||
/// // Bad
|
||||
/// a.extend(b.drain(..));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut a = vec![1, 2, 3];
|
||||
/// let mut b = vec![4, 5, 6];
|
||||
///
|
||||
/// // Good
|
||||
/// a.append(&mut b);
|
||||
/// ```
|
||||
#[clippy::version = "1.55.0"]
|
||||
|
@ -1351,11 +1370,12 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let name = "_";
|
||||
///
|
||||
/// // Bad
|
||||
/// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let name = "_";
|
||||
/// name.ends_with('_') || name.ends_with('-');
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -1401,11 +1421,13 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
|
||||
/// # #[allow(unused)]
|
||||
/// (0..3).fold(false, |acc, x| acc || x > 2);
|
||||
/// ```
|
||||
/// This could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let _ = (0..3).any(|x| x > 2);
|
||||
/// (0..3).any(|x| x > 2);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_FOLD,
|
||||
|
@ -1485,11 +1507,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let _ = (&vec![3, 4, 5]).into_iter();
|
||||
/// # let vec = vec![3, 4, 5];
|
||||
/// (&vec).into_iter();
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let _ = (&vec![3, 4, 5]).iter();
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let vec = vec![3, 4, 5];
|
||||
/// (&vec).iter();
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub INTO_ITER_ON_REF,
|
||||
|
@ -1704,13 +1729,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut string = String::new();
|
||||
/// # let mut string = String::new();
|
||||
/// string.insert_str(0, "R");
|
||||
/// string.push_str("R");
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut string = String::new();
|
||||
/// # let mut string = String::new();
|
||||
/// string.insert(0, 'R');
|
||||
/// string.push('R');
|
||||
/// ```
|
||||
|
@ -1881,7 +1907,7 @@ declare_clippy_lint! {
|
|||
/// let x = [1, 2, 3];
|
||||
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub MAP_IDENTITY,
|
||||
complexity,
|
||||
"using iterator.map(|x| x)"
|
||||
|
@ -1897,11 +1923,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let _ = "Hello".bytes().nth(3);
|
||||
/// # #[allow(unused)]
|
||||
/// "Hello".bytes().nth(3);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let _ = "Hello".as_bytes().get(3);
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # #[allow(unused)]
|
||||
/// "Hello".as_bytes().get(3);
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub BYTES_NTH,
|
||||
|
@ -1945,15 +1974,19 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// # #![allow(unused)]
|
||||
/// let some_vec = vec![0, 1, 2, 3];
|
||||
/// let _ = some_vec.iter().count();
|
||||
/// let _ = &some_vec[..].iter().count();
|
||||
///
|
||||
/// // Good
|
||||
/// some_vec.iter().count();
|
||||
/// &some_vec[..].iter().count();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let some_vec = vec![0, 1, 2, 3];
|
||||
/// let _ = some_vec.len();
|
||||
/// let _ = &some_vec[..].len();
|
||||
///
|
||||
/// some_vec.len();
|
||||
/// &some_vec[..].len();
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub ITER_COUNT,
|
||||
|
@ -1973,16 +2006,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let s = "";
|
||||
/// # let s = "";
|
||||
/// for x in s.splitn(1, ":") {
|
||||
/// // use x
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let s = "";
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let s = "";
|
||||
/// for x in s.splitn(2, ":") {
|
||||
/// // use x
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.54.0"]
|
||||
|
@ -2000,10 +2034,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: String = std::iter::repeat('x').take(10).collect();
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x: String = "x".repeat(10);
|
||||
/// ```
|
||||
#[clippy::version = "1.54.0"]
|
||||
|
@ -2021,7 +2056,6 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let s = "key=value=add";
|
||||
/// let (key, value) = s.splitn(2, '=').next_tuple()?;
|
||||
/// let value = s.splitn(2, '=').nth(1)?;
|
||||
|
@ -2030,9 +2064,9 @@ declare_clippy_lint! {
|
|||
/// let key = parts.next()?;
|
||||
/// let value = parts.next()?;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // Good
|
||||
/// let s = "key=value=add";
|
||||
/// let (key, value) = s.split_once('=')?;
|
||||
/// let value = s.split_once('=')?.1;
|
||||
|
@ -2057,17 +2091,16 @@ declare_clippy_lint! {
|
|||
/// that both functions return a lazy iterator.
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let str = "key=value=add";
|
||||
/// let _ = str.splitn(3, '=').next().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// let str = "key=value=add";
|
||||
/// let _ = str.split('=').next().unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub NEEDLESS_SPLITN,
|
||||
complexity,
|
||||
"usages of `str::splitn` that can be replaced with `str::split`"
|
||||
|
@ -2098,7 +2131,7 @@ declare_clippy_lint! {
|
|||
/// foo(&path.to_string_lossy());
|
||||
/// fn foo(s: &str) {}
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub UNNECESSARY_TO_OWNED,
|
||||
perf,
|
||||
"unnecessary calls to `to_owned`-like functions"
|
||||
|
@ -2149,7 +2182,8 @@ declare_clippy_lint! {
|
|||
/// let a = Some(&1);
|
||||
/// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = Some(&1);
|
||||
/// let b = a;
|
||||
|
@ -2583,8 +2617,8 @@ impl Methods {
|
|||
},
|
||||
_ => {},
|
||||
},
|
||||
(name @ "count", args @ []) => match method_call(recv) {
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
|
||||
("count", []) => match method_call(recv) {
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
|
||||
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
|
||||
iter_count::check(cx, expr, recv2, name2);
|
||||
},
|
||||
|
@ -2614,9 +2648,9 @@ impl Methods {
|
|||
flat_map_identity::check(cx, expr, arg, span);
|
||||
flat_map_option::check(cx, expr, arg, span);
|
||||
},
|
||||
(name @ "flatten", args @ []) => match method_call(recv) {
|
||||
("flatten", []) => match method_call(recv) {
|
||||
Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
|
||||
_ => {},
|
||||
},
|
||||
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
|
||||
|
@ -2636,10 +2670,10 @@ impl Methods {
|
|||
unnecessary_join::check(cx, expr, recv, join_arg, span);
|
||||
}
|
||||
},
|
||||
("last", args @ []) | ("skip", args @ [_]) => {
|
||||
("last", []) | ("skip", [_]) => {
|
||||
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
|
||||
if let ("cloned", []) = (name2, args2) {
|
||||
iter_overeager_cloned::check(cx, expr, recv2, name, args);
|
||||
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2660,10 +2694,10 @@ impl Methods {
|
|||
map_identity::check(cx, expr, recv, m_arg, name, span);
|
||||
},
|
||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||
(name @ "next", args @ []) => {
|
||||
("next", []) => {
|
||||
if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
|
||||
match (name2, args2) {
|
||||
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
|
||||
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
|
||||
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
|
||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
|
||||
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
||||
|
@ -2673,9 +2707,9 @@ impl Methods {
|
|||
}
|
||||
}
|
||||
},
|
||||
("nth", args @ [n_arg]) => match method_call(recv) {
|
||||
("nth", [n_arg]) => match method_call(recv) {
|
||||
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
|
||||
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
||||
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
||||
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
||||
|
@ -2698,10 +2732,10 @@ impl Methods {
|
|||
}
|
||||
},
|
||||
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||
("take", args @ [_arg]) => {
|
||||
("take", [_arg]) => {
|
||||
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
|
||||
if let ("cloned", []) = (name2, args2) {
|
||||
iter_overeager_cloned::check(cx, expr, recv2, name, args);
|
||||
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -45,16 +45,13 @@ declare_clippy_lint! {
|
|||
/// dereferences, e.g., changing `*x` to `x` within the function.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// fn foo(ref x: u8) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```rust
|
||||
/// fn foo(ref _x: u8) {}
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// fn foo(x: &u8) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(_x: &u8) {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TOPLEVEL_REF_ARG,
|
||||
|
@ -73,11 +70,12 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = 1.0;
|
||||
///
|
||||
/// // Bad
|
||||
/// if x == f32::NAN { }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.0f32;
|
||||
/// if x.is_nan() { }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -139,7 +137,8 @@ declare_clippy_lint! {
|
|||
/// # let y = String::from("foo");
|
||||
/// if x.to_owned() == y {}
|
||||
/// ```
|
||||
/// Could be written as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
|
@ -232,10 +231,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let a = 0 as *const u32;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = std::ptr::null::<u32>();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
|
|
@ -34,13 +34,21 @@ declare_clippy_lint! {
|
|||
/// # }
|
||||
/// let f = Foo { a: 0, b: 0, c: 0 };
|
||||
///
|
||||
/// // Bad
|
||||
/// match f {
|
||||
/// Foo { a: _, b: 0, .. } => {},
|
||||
/// Foo { a: _, b: _, c: _ } => {},
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct Foo {
|
||||
/// # a: i32,
|
||||
/// # b: i32,
|
||||
/// # c: i32,
|
||||
/// # }
|
||||
/// let f = Foo { a: 0, b: 0, c: 0 };
|
||||
///
|
||||
/// // Good
|
||||
/// match f {
|
||||
/// Foo { b: 0, .. } => {},
|
||||
/// Foo { .. } => {},
|
||||
|
@ -62,10 +70,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn foo(a: i32, _a: i32) {}
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn bar(a: i32, _b: i32) {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -103,11 +112,16 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let y = 0x1a9BAcD;
|
||||
/// # let _ =
|
||||
/// 0x1a9BAcD
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let y = 0x1A9BACD;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let _ =
|
||||
/// 0x1A9BACD
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MIXED_CASE_HEX_LITERALS,
|
||||
|
@ -127,11 +141,16 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let y = 123832i32;
|
||||
/// # let _ =
|
||||
/// 123832i32
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let y = 123832_i32;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let _ =
|
||||
/// 123832_i32
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNSEPARATED_LITERAL_SUFFIX,
|
||||
|
@ -150,11 +169,16 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let y = 123832_i32;
|
||||
/// # let _ =
|
||||
/// 123832_i32
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let y = 123832i32;
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let _ =
|
||||
/// 123832i32
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub SEPARATED_LITERAL_SUFFIX,
|
||||
|
@ -234,14 +258,15 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let v = Some("abc");
|
||||
///
|
||||
/// // Bad
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y @ _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let v = Some("abc");
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y => (),
|
||||
|
@ -262,6 +287,7 @@ declare_clippy_lint! {
|
|||
/// means there are 0 or more elements left. This can make a difference
|
||||
/// when refactoring, but shouldn't result in errors in the refactored code,
|
||||
/// since the wildcard pattern isn't used anyway.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The wildcard pattern is unneeded as the rest pattern
|
||||
/// can match that element as well.
|
||||
|
@ -270,13 +296,16 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # struct TupleStruct(u32, u32, u32);
|
||||
/// # let t = TupleStruct(1, 2, 3);
|
||||
/// // Bad
|
||||
/// match t {
|
||||
/// TupleStruct(0, .., _) => (),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct TupleStruct(u32, u32, u32);
|
||||
/// # let t = TupleStruct(1, 2, 3);
|
||||
/// match t {
|
||||
/// TupleStruct(0, ..) => (),
|
||||
/// _ => (),
|
||||
|
|
|
@ -25,14 +25,16 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// let mut x = 0;
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
/// } + x;
|
||||
/// // Unclear whether a is 1 or 2.
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let mut x = 0;
|
||||
/// let tmp = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
|
|
|
@ -16,12 +16,17 @@ declare_clippy_lint! {
|
|||
/// the value. Also the code misleads about the intent of the call site.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// my_vec.push(&mut value)
|
||||
/// ```rust
|
||||
/// # let mut vec = Vec::new();
|
||||
/// # let mut value = 5;
|
||||
/// vec.push(&mut value);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// my_vec.push(&value)
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let mut vec = Vec::new();
|
||||
/// # let value = 5;
|
||||
/// vec.push(&value);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_MUT_PASSED,
|
||||
|
|
|
@ -27,12 +27,13 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let y = true;
|
||||
///
|
||||
/// // Bad
|
||||
/// # use std::sync::Mutex;
|
||||
/// let x = Mutex::new(&y);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let y = true;
|
||||
/// # use std::sync::atomic::AtomicBool;
|
||||
/// let x = AtomicBool::new(y);
|
||||
/// ```
|
||||
|
@ -60,8 +61,10 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
/// let x = Mutex::new(0usize);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::sync::atomic::AtomicUsize;
|
||||
/// let x = AtomicUsize::new(0usize);
|
||||
/// ```
|
||||
|
|
|
@ -30,16 +30,21 @@ declare_clippy_lint! {
|
|||
/// shorter code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # let x = true;
|
||||
/// if x {
|
||||
/// false
|
||||
/// } else {
|
||||
/// true
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
/// Could be written as
|
||||
/// ```rust,ignore
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = true;
|
||||
/// !x
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_BOOL,
|
||||
|
|
|
@ -27,16 +27,17 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
|
||||
/// # #[allow(unused)]
|
||||
/// v.iter_mut().filter(|&ref a| a.is_empty());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// let _ = v.iter_mut().filter(|a| a.is_empty());
|
||||
/// # #[allow(unused)]
|
||||
/// v.iter_mut().filter(|a| a.is_empty());
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_BORROWED_REFERENCE,
|
||||
|
|
|
@ -56,7 +56,7 @@ declare_clippy_lint! {
|
|||
/// -1
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub NEEDLESS_LATE_INIT,
|
||||
style,
|
||||
"late initializations that can be replaced by a `let` statement with an initializer"
|
||||
|
@ -185,14 +185,14 @@ fn assignment_suggestions<'tcx>(
|
|||
|
||||
let suggestions = assignments
|
||||
.iter()
|
||||
.map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new())))
|
||||
.chain(assignments.iter().map(|assignment| {
|
||||
Some((
|
||||
.flat_map(|assignment| {
|
||||
[
|
||||
assignment.span.until(assignment.rhs_span),
|
||||
assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
|
||||
String::new(),
|
||||
))
|
||||
}))
|
||||
.collect::<Option<Vec<(Span, String)>>>()?;
|
||||
]
|
||||
})
|
||||
.map(|span| (span, String::new()))
|
||||
.collect::<Vec<(Span, String)>>();
|
||||
|
||||
match suggestions.len() {
|
||||
// All of `exprs` are never types
|
||||
|
|
87
clippy_lints/src/needless_parens_on_range_literals.rs
Normal file
87
clippy_lints/src/needless_parens_on_range_literals.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_then,
|
||||
higher,
|
||||
source::{snippet, snippet_with_applicability},
|
||||
};
|
||||
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// The lint checks for parenthesis on literals in range statements that are
|
||||
/// superfluous.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Having superfluous parenthesis makes the code less readable
|
||||
/// overhead when reading.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// for i in (0)..10 {
|
||||
/// println!("{i}");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust
|
||||
/// for i in 0..10 {
|
||||
/// println!("{i}");
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub NEEDLESS_PARENS_ON_RANGE_LITERALS,
|
||||
style,
|
||||
"needless parenthesis on range literals can be removed"
|
||||
}
|
||||
|
||||
declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]);
|
||||
|
||||
fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
|
||||
snippet.starts_with('(') && snippet.ends_with(')')
|
||||
}
|
||||
|
||||
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
|
||||
if is_start &&
|
||||
let ExprKind::Lit(ref literal) = e.kind &&
|
||||
let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
|
||||
{
|
||||
// don't check floating point literals on the start expression of a range
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref literal) = e.kind;
|
||||
// the indicator that parenthesis surround the literal is that the span of the expression and the literal differ
|
||||
if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo);
|
||||
// inspect the source code of the expression for parenthesis
|
||||
if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, ""));
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span,
|
||||
"needless parenthesis on range literals can be removed",
|
||||
|diag| {
|
||||
let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability);
|
||||
diag.span_suggestion(e.span, "try", suggestion, applicability);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) {
|
||||
if let Some(start) = start {
|
||||
check_for_parens(cx, start, true);
|
||||
}
|
||||
if let Some(end) = end {
|
||||
check_for_parens(cx, end, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,16 +24,17 @@ declare_clippy_lint! {
|
|||
/// # z: i32,
|
||||
/// # }
|
||||
/// # let zero_point = Point { x: 0, y: 0, z: 0 };
|
||||
///
|
||||
/// // Bad
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 1,
|
||||
/// z: 1,
|
||||
/// ..zero_point
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// // Ok
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // Missing field `z`
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 1,
|
||||
|
|
|
@ -19,17 +19,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let a = 1.0;
|
||||
/// let b = f64::NAN;
|
||||
///
|
||||
/// let not_less_or_equal = !(a <= b);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = 1.0;
|
||||
/// let b = f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = !(a <= b);
|
||||
///
|
||||
/// // Good
|
||||
/// let a = 1.0;
|
||||
/// let b = f64::NAN;
|
||||
/// # let a = 1.0;
|
||||
/// # let b = f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = match a.partial_cmp(&b) {
|
||||
/// None | Some(Ordering::Greater) => true,
|
||||
|
|
|
@ -19,12 +19,13 @@ declare_clippy_lint! {
|
|||
/// This only catches integers (for now).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// ```rust,ignore
|
||||
/// let a = x * -1;
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// let b = -x;
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let a = -x;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEG_MULTIPLY,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue