Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
flip1995 2021-02-25 10:40:00 +01:00
commit c6408a47bd
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
111 changed files with 6361 additions and 3311 deletions

View file

@ -1,7 +1,7 @@
--- ---
name: Bug Report name: Bug Report
about: Create a bug report for Clippy about: Create a bug report for Clippy
labels: L-bug labels: C-bug
--- ---
<!-- <!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug, Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,

View file

@ -1,7 +1,7 @@
--- ---
name: Bug Report (False Negative) name: Bug Report (False Negative)
about: Create a bug report about missing warnings from a lint about: Create a bug report about missing warnings from a lint
labels: L-bug, L-false-negative labels: C-bug, I-false-negative
--- ---
<!-- <!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug, Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,

View file

@ -1,7 +1,7 @@
--- ---
name: Bug Report (False Positive) name: Bug Report (False Positive)
about: Create a bug report about a wrongly emitted lint warning about: Create a bug report about a wrongly emitted lint warning
labels: L-bug, L-false-positive labels: C-bug, I-false-positive
--- ---
<!-- <!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug, Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,

View file

@ -1,7 +1,7 @@
--- ---
name: Internal Compiler Error name: Internal Compiler Error
about: Create a report for an internal compiler error in Clippy. about: Create a report for an internal compiler error in Clippy.
labels: L-bug, L-crash labels: C-bug, I-ICE
--- ---
<!-- <!--
Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide

View file

@ -1,7 +1,7 @@
--- ---
name: New lint suggestion name: New lint suggestion
about: Suggest a new Clippy lint. about: Suggest a new Clippy lint.
labels: L-lint labels: A-lint
--- ---
### What it does ### What it does

View file

@ -53,16 +53,8 @@ jobs:
- name: Test "--fix -Zunstable-options" - name: Test "--fix -Zunstable-options"
run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options
- name: Test - name: Test Workspace
run: cargo test --features deny-warnings,internal-lints run: cargo test --all --features deny-warnings,internal-lints
- name: Test clippy_lints
run: cargo test --features deny-warnings,internal-lints
working-directory: clippy_lints
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
working-directory: rustc_tools_util
- name: Test clippy_dev - name: Test clippy_dev
run: cargo test --features deny-warnings run: cargo test --features deny-warnings

View file

@ -112,16 +112,8 @@ jobs:
- name: Build - name: Build
run: cargo build --features deny-warnings,internal-lints run: cargo build --features deny-warnings,internal-lints
- name: Test - name: Test Workspace
run: cargo test --features deny-warnings,internal-lints run: cargo test --all --features deny-warnings,internal-lints
- name: Test clippy_lints
run: cargo test --features deny-warnings,internal-lints
working-directory: clippy_lints
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
working-directory: rustc_tools_util
- name: Test clippy_dev - name: Test clippy_dev
run: cargo test --features deny-warnings run: cargo test --features deny-warnings

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ out
*Cargo.lock *Cargo.lock
/target /target
/clippy_lints/target /clippy_lints/target
/clippy_utils/target
/clippy_workspace_tests/target /clippy_workspace_tests/target
/clippy_dev/target /clippy_dev/target
/rustc_tools_util/target /rustc_tools_util/target

View file

@ -6,13 +6,128 @@ document.
## Unreleased / In Rust Nightly ## Unreleased / In Rust Nightly
[4911ab1...master](https://github.com/rust-lang/rust-clippy/compare/4911ab1...master) [3e41797...master](https://github.com/rust-lang/rust-clippy/compare/3e41797...master)
## Rust 1.51
Current beta, release 2021-03-25
[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
### New Lints
* [`upper_case_acronyms`]
[#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
* [`case_sensitive_file_extension_comparisons`]
[#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
* [`needless_question_mark`]
[#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
* [`missing_panics_doc`]
[#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
* [`redundant_slicing`]
[#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
* [`vec_init_then_push`]
[#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
* [`collapsible_else_if`] (split out from `collapsible_if`)
[#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
* [`manual_filter_map`]
[#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
* [`exhaustive_enums`]
[#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
* [`exhaustive_structs`]
[#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
### Moves and Deprecations
* Replace [`find_map`] with [`manual_find_map`]
[#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
[#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
### Enhancements
* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
[#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
[#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
### False Positive Fixes
* [`similar_names`] Ignore underscore prefixed names
[#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
* [`print_literal`] and [`write_literal`] No longer lint numeric literals
[#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
* [`large_enum_variant`] No longer lints in external macros
[#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
* [`empty_enum`] Only lint if `never_type` feature is enabled
[#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
* [`field_reassign_with_default`] No longer lints in macros
[#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
* [`size_of_in_element_count`] No longer lints when dividing by element size
[#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
* [`needless_return`] No longer lints in macros
[#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
* [`match_overlapping_arm`] No longer lint when first arm is completely included
in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
### Suggestion Fixes/Improvements
* [`field_reassign_with_default`] Don't expand macro in lint suggestion
[#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
* [`match_like_matches_macro`] Strip references in suggestion
[#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
* [`single_match`] Suggest `if` over `if let` when possible
[#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
* [`ref_in_deref`] Use parentheses correctly in suggestion
[#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
* [`stable_sort_primitive`] Clarify error message
[#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
### ICE Fixes
* [`zero_sized_map_values`]
[#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
### Documentation Improvements
* Improve search performance on the Clippy website and make it possible to
directly search for lints on the GitHub issue tracker
[#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
* Clean up `README.md` by removing outdated paragraph
[#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
* [`await_holding_refcell_ref`] and [`await_holding_lock`]
[#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
### Others
* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
project, take a look at the [Roadmap project page]. All issues listed there
are actively mentored
[#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
* The Clippy version number now corresponds to the Rust version number
[#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
* Fix oversight which caused Clippy to lint deps in some environments, where
`CLIPPY_TESTS=true` was set somewhere
[#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
[#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
## Rust 1.50 ## Rust 1.50
Current beta, release 2021-02-11 Current stable, released 2021-02-11
[b20d4c1...4911ab1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4911ab1) [b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
### New Lints ### New Lints
@ -90,6 +205,8 @@ Current beta, release 2021-02-11
* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: * [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
Both now ignore enums with frozen variants Both now ignore enums with frozen variants
[#6110](https://github.com/rust-lang/rust-clippy/pull/6110) [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
* [`field_reassign_with_default`] No longer lint for private fields
[#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
### Suggestion Fixes/Improvements ### Suggestion Fixes/Improvements
@ -137,7 +254,7 @@ Current beta, release 2021-02-11
## Rust 1.49 ## Rust 1.49
Current stable, released 2020-12-31 Released 2020-12-31
[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1) [e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
@ -1910,6 +2027,7 @@ Released 2018-09-13
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
@ -1975,6 +2093,7 @@ Released 2018-09-13
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
@ -1990,6 +2109,7 @@ Released 2018-09-13
[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
@ -2042,6 +2162,7 @@ Released 2018-09-13
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or

View file

@ -18,6 +18,9 @@ build = "build.rs"
edition = "2018" edition = "2018"
publish = false publish = false
[workspace]
exclude = ["clippy_dev", "mini-macro", "target/lintcheck/crates"]
[[bin]] [[bin]]
name = "cargo-clippy" name = "cargo-clippy"
test = false test = false
@ -37,8 +40,8 @@ tempfile = { version = "3.1.0", optional = true }
[dev-dependencies] [dev-dependencies]
cargo_metadata = "0.12" cargo_metadata = "0.12"
compiletest_rs = { version = "0.5.0", features = ["tmp"] } compiletest_rs = { version = "0.6.0", features = ["tmp"] }
tester = "0.7" tester = "0.9"
clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
derive-new = "0.5" derive-new = "0.5"

View file

@ -98,6 +98,23 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
cargo clippy -p example -- --no-deps cargo clippy -p example -- --no-deps
``` ```
### As a rustc replacement (`clippy-driver`)
Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
```terminal
rustc --edition 2018 -Cpanic=abort foo.rs
```
Then, to enable Clippy, you will need to call:
```terminal
clippy-driver --edition 2018 -Cpanic=abort foo.rs
```
Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
### Travis CI ### Travis CI
You can add Clippy to Travis CI in the same way you use it locally: You can add Clippy to Travis CI in the same way you use it locally:

View file

@ -19,8 +19,9 @@ shell-escape = "0.1"
tar = { version = "0.4.30", optional = true } tar = { version = "0.4.30", optional = true }
toml = { version = "0.5", optional = true } toml = { version = "0.5", optional = true }
ureq = { version = "2.0.0-rc3", optional = true } ureq = { version = "2.0.0-rc3", optional = true }
rayon = { version = "1.5.0", optional = true }
walkdir = "2" walkdir = "2"
[features] [features]
lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra"] lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra", "rayon"]
deny-warnings = [] deny-warnings = []

View file

@ -1,28 +1,77 @@
# Clippy Dev Tool # Clippy Dev Tool
The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s `x.py`. The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s
`x.py`.
Functionalities (incomplete): Functionalities (incomplete):
## `lintcheck` ## `lintcheck`
Runs clippy on a fixed set of crates read from `clippy_dev/lintcheck_crates.toml`
and saves logs of the lint warnings into the repo. Runs clippy on a fixed set of crates read from
We can then check the diff and spot new or disappearing warnings. `clippy_dev/lintcheck_crates.toml` and saves logs of the lint warnings into the
repo. We can then check the diff and spot new or disappearing warnings.
From the repo root, run: From the repo root, run:
````
```
cargo run --target-dir clippy_dev/target --package clippy_dev \ cargo run --target-dir clippy_dev/target --package clippy_dev \
--bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck
```` ```
or or
````
```
cargo dev-lintcheck cargo dev-lintcheck
```` ```
By default the logs will be saved into `lintcheck-logs/lintcheck_crates_logs.txt`. By default the logs will be saved into
`lintcheck-logs/lintcheck_crates_logs.txt`.
You can set a custom sources.toml by adding `--crates-toml custom.toml` You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
where `custom.toml` must be a relative path from the repo root. `LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from
the repo root.
The results will then be saved to `lintcheck-logs/custom_logs.toml`. The results will then be saved to `lintcheck-logs/custom_logs.toml`.
### Configuring the Crate Sources
The sources to check are saved in a `toml` file. There are three types of
sources.
1. Crates-io Source
```toml
bitflags = {name = "bitflags", versions = ['1.2.1']}
```
Requires a "name" and one or multiple "versions" to be checked.
2. `git` Source
````toml
puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
````
Requires a name, the url to the repo and unique identifier of a commit,
branch or tag which is checked out before linting. There is no way to always
check `HEAD` because that would lead to changing lint-results as the repo
would get updated. If `git_url` or `git_hash` is missing, an error will be
thrown.
3. Local Dependency
```toml
clippy = {name = "clippy", path = "/home/user/clippy"}
```
For when you want to add a repository that is not published yet.
#### Command Line Options (optional)
```toml
bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
```
It is possible to specify command line options for each crate. This makes it
possible to only check a crate for certain lint groups. If no options are
specified, the lint groups `clippy::all`, `clippy::pedantic`, and
`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
is checked.
**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
is explicitly specified in the options.

View file

@ -42,9 +42,10 @@ pub fn bless(ignore_timestamp: bool) {
.for_each(|f| { .for_each(|f| {
let test_name = f.path().strip_prefix(test_suite_dir).unwrap(); let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
for &ext in &["stdout", "stderr", "fixed"] { for &ext in &["stdout", "stderr", "fixed"] {
let test_name_ext = format!("stage-id.{}", ext);
update_reference_file( update_reference_file(
f.path().with_extension(ext), f.path().with_extension(ext),
test_name.with_extension(ext), test_name.with_extension(test_name_ext),
ignore_timestamp, ignore_timestamp,
); );
} }

View file

@ -11,20 +11,22 @@ use crate::clippy_project_root;
use std::collections::HashMap; use std::collections::HashMap;
use std::process::Command; use std::process::Command;
use std::{fmt, fs::write, path::PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::{env, fmt, fs::write, path::PathBuf};
use clap::ArgMatches; use clap::ArgMatches;
use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
// use this to store the crates when interacting with the crates.toml file /// List of sources to check, loaded from a .toml file
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct CrateList { struct SourceList {
crates: HashMap<String, TomlCrate>, crates: HashMap<String, TomlCrate>,
} }
// crate data we stored in the toml, can have multiple versions per crate /// A crate source stored inside the .toml
// A single TomlCrate is laster mapped to several CrateSources in that case /// will be translated into on one of the `CrateSource` variants
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct TomlCrate { struct TomlCrate {
name: String, name: String,
@ -32,27 +34,42 @@ struct TomlCrate {
git_url: Option<String>, git_url: Option<String>,
git_hash: Option<String>, git_hash: Option<String>,
path: Option<String>, path: Option<String>,
options: Option<Vec<String>>,
} }
// represents an archive we download from crates.io, or a git repo, or a local repo /// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)] /// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
enum CrateSource { enum CrateSource {
CratesIo { name: String, version: String }, CratesIo {
Git { name: String, url: String, commit: String }, name: String,
Path { name: String, path: PathBuf }, version: String,
options: Option<Vec<String>>,
},
Git {
name: String,
url: String,
commit: String,
options: Option<Vec<String>>,
},
Path {
name: String,
path: PathBuf,
options: Option<Vec<String>>,
},
} }
// represents the extracted sourcecode of a crate /// Represents the actual source code of a crate that we ran "cargo clippy" on
// we actually don't need to special-case git repos here because it does not matter for clippy, yay!
// (clippy only needs a simple path)
#[derive(Debug)] #[derive(Debug)]
struct Crate { struct Crate {
version: String, version: String,
name: String, name: String,
// path to the extracted sources that clippy can check // path to the extracted sources that clippy can check
path: PathBuf, path: PathBuf,
options: Option<Vec<String>>,
} }
/// A single warning that clippy issued while checking a `Crate`
#[derive(Debug)] #[derive(Debug)]
struct ClippyWarning { struct ClippyWarning {
crate_name: String, crate_name: String,
@ -62,7 +79,7 @@ struct ClippyWarning {
column: String, column: String,
linttype: String, linttype: String,
message: String, message: String,
ice: bool, is_ice: bool,
} }
impl std::fmt::Display for ClippyWarning { impl std::fmt::Display for ClippyWarning {
@ -76,9 +93,12 @@ impl std::fmt::Display for ClippyWarning {
} }
impl CrateSource { impl CrateSource {
/// Makes the sources available on the disk for clippy to check.
/// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
/// copies a local folder
fn download_and_extract(&self) -> Crate { fn download_and_extract(&self) -> Crate {
match self { match self {
CrateSource::CratesIo { name, version } => { CrateSource::CratesIo { name, version, options } => {
let extract_dir = PathBuf::from("target/lintcheck/crates"); let extract_dir = PathBuf::from("target/lintcheck/crates");
let krate_download_dir = PathBuf::from("target/lintcheck/downloads"); let krate_download_dir = PathBuf::from("target/lintcheck/downloads");
@ -110,9 +130,15 @@ impl CrateSource {
version: version.clone(), version: version.clone(),
name: name.clone(), name: name.clone(),
path: extract_dir.join(format!("{}-{}/", name, version)), path: extract_dir.join(format!("{}-{}/", name, version)),
options: options.clone(),
} }
}, },
CrateSource::Git { name, url, commit } => { CrateSource::Git {
name,
url,
commit,
options,
} => {
let repo_path = { let repo_path = {
let mut repo_path = PathBuf::from("target/lintcheck/crates"); let mut repo_path = PathBuf::from("target/lintcheck/crates");
// add a -git suffix in case we have the same crate from crates.io and a git repo // add a -git suffix in case we have the same crate from crates.io and a git repo
@ -122,27 +148,37 @@ impl CrateSource {
// clone the repo if we have not done so // clone the repo if we have not done so
if !repo_path.is_dir() { if !repo_path.is_dir() {
println!("Cloning {} and checking out {}", url, commit); println!("Cloning {} and checking out {}", url, commit);
Command::new("git") if !Command::new("git")
.arg("clone") .arg("clone")
.arg(url) .arg(url)
.arg(&repo_path) .arg(&repo_path)
.output() .status()
.expect("Failed to clone git repo!"); .expect("Failed to clone git repo!")
.success()
{
eprintln!("Failed to clone {} into {}", url, repo_path.display())
}
} }
// check out the commit/branch/whatever // check out the commit/branch/whatever
Command::new("git") if !Command::new("git")
.arg("checkout") .arg("checkout")
.arg(commit) .arg(commit)
.output() .current_dir(&repo_path)
.expect("Failed to check out commit"); .status()
.expect("Failed to check out commit")
.success()
{
eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
}
Crate { Crate {
version: commit.clone(), version: commit.clone(),
name: name.clone(), name: name.clone(),
path: repo_path, path: repo_path,
options: options.clone(),
} }
}, },
CrateSource::Path { name, path } => { CrateSource::Path { name, path, options } => {
use fs_extra::dir; use fs_extra::dir;
// simply copy the entire directory into our target dir // simply copy the entire directory into our target dir
@ -171,6 +207,7 @@ impl CrateSource {
version: String::from("local"), version: String::from("local"),
name: name.clone(), name: name.clone(),
path: crate_root, path: crate_root,
options: options.clone(),
} }
}, },
} }
@ -178,24 +215,56 @@ impl CrateSource {
} }
impl Crate { impl Crate {
fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
println!("Linting {} {}...", &self.name, &self.version); /// issued
fn run_clippy_lints(
&self,
cargo_clippy_path: &PathBuf,
target_dir_index: &AtomicUsize,
thread_limit: usize,
total_crates_to_lint: usize,
) -> Vec<ClippyWarning> {
// advance the atomic index by one
let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
// "loop" the index within 0..thread_limit
let target_dir_index = index % thread_limit;
let perc = ((index * 100) as f32 / total_crates_to_lint as f32) as u8;
if thread_limit == 1 {
println!(
"{}/{} {}% Linting {} {}",
index, total_crates_to_lint, perc, &self.name, &self.version
);
} else {
println!(
"{}/{} {}% Linting {} {} in target dir {:?}",
index, total_crates_to_lint, perc, &self.name, &self.version, target_dir_index
);
}
let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap(); let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir/"); let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
let mut args = vec!["--", "--message-format=json", "--", "--cap-lints=warn"];
if let Some(options) = &self.options {
for opt in options {
args.push(opt);
}
} else {
args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
}
let all_output = std::process::Command::new(&cargo_clippy_path) let all_output = std::process::Command::new(&cargo_clippy_path)
.env("CARGO_TARGET_DIR", shared_target_dir) // use the looping index to create individual target dirs
.env(
"CARGO_TARGET_DIR",
shared_target_dir.join(format!("_{:?}", target_dir_index)),
)
// lint warnings will look like this: // lint warnings will look like this:
// src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
.args(&[ .args(&args)
"--",
"--message-format=json",
"--",
"--cap-lints=warn",
"-Wclippy::pedantic",
"-Wclippy::cargo",
])
.current_dir(&self.path) .current_dir(&self.path)
.output() .output()
.unwrap_or_else(|error| { .unwrap_or_else(|error| {
@ -211,28 +280,69 @@ impl Crate {
let warnings: Vec<ClippyWarning> = output_lines let warnings: Vec<ClippyWarning> = output_lines
.into_iter() .into_iter()
// get all clippy warnings and ICEs // get all clippy warnings and ICEs
.filter(|line| line.contains("clippy::") || line.contains("internal compiler error: ")) .filter(|line| filter_clippy_warnings(&line))
.map(|json_msg| parse_json_message(json_msg, &self)) .map(|json_msg| parse_json_message(json_msg, &self))
.collect(); .collect();
warnings warnings
} }
} }
fn build_clippy() { /// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
Command::new("cargo") /// or false (we aren't)
.arg("build") fn filter_clippy_warnings(line: &str) -> bool {
.output() // we want to collect ICEs because clippy might have crashed.
.expect("Failed to build clippy!"); // these are summarized later
if line.contains("internal compiler error: ") {
return true;
}
// in general, we want all clippy warnings
// however due to some kind of bug, sometimes there are absolute paths
// to libcore files inside the message
// or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
// filter out these message to avoid unnecessary noise in the logs
if line.contains("clippy::")
&& !(line.contains("could not read cargo metadata")
|| (line.contains(".rustup") && line.contains("toolchains")))
{
return true;
}
false
} }
// get a list of CrateSources we want to check from a "lintcheck_crates.toml" file. /// get the path to lintchecks crate sources .toml file, check LINTCHECK_TOML first but if it's
/// empty use the default path
fn lintcheck_config_toml(toml_path: Option<&str>) -> PathBuf {
PathBuf::from(
env::var("LINTCHECK_TOML").unwrap_or(
toml_path
.clone()
.unwrap_or("clippy_dev/lintcheck_crates.toml")
.to_string(),
),
)
}
/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
fn build_clippy() {
let status = Command::new("cargo")
.arg("build")
.status()
.expect("Failed to build clippy!");
if !status.success() {
eprintln!("Error: Failed to compile Clippy!");
std::process::exit(1);
}
}
/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) { fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
let toml_path = PathBuf::from(toml_path.unwrap_or("clippy_dev/lintcheck_crates.toml")); let toml_path = lintcheck_config_toml(toml_path);
// save it so that we can use the name of the sources.toml as name for the logfile later. // save it so that we can use the name of the sources.toml as name for the logfile later.
let toml_filename = toml_path.file_stem().unwrap().to_str().unwrap().to_string(); let toml_filename = toml_path.file_stem().unwrap().to_str().unwrap().to_string();
let toml_content: String = let toml_content: String =
std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
let crate_list: CrateList = let crate_list: SourceList =
toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
// parse the hashmap of the toml file into a list of crates // parse the hashmap of the toml file into a list of crates
let tomlcrates: Vec<TomlCrate> = crate_list let tomlcrates: Vec<TomlCrate> = crate_list
@ -249,6 +359,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
crate_sources.push(CrateSource::Path { crate_sources.push(CrateSource::Path {
name: tk.name.clone(), name: tk.name.clone(),
path: PathBuf::from(path), path: PathBuf::from(path),
options: tk.options.clone(),
}); });
} }
@ -258,6 +369,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
crate_sources.push(CrateSource::CratesIo { crate_sources.push(CrateSource::CratesIo {
name: tk.name.clone(), name: tk.name.clone(),
version: ver.to_string(), version: ver.to_string(),
options: tk.options.clone(),
}); });
}) })
} }
@ -267,6 +379,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
name: tk.name.clone(), name: tk.name.clone(),
url: tk.git_url.clone().unwrap(), url: tk.git_url.clone().unwrap(),
commit: tk.git_hash.clone().unwrap(), commit: tk.git_hash.clone().unwrap(),
options: tk.options.clone(),
}); });
} }
// if we have a version as well as a git data OR only one git data, something is funky // if we have a version as well as a git data OR only one git data, something is funky
@ -283,10 +396,13 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
unreachable!("Failed to translate TomlCrate into CrateSource!"); unreachable!("Failed to translate TomlCrate into CrateSource!");
} }
}); });
// sort the crates
crate_sources.sort();
(toml_filename, crate_sources) (toml_filename, crate_sources)
} }
// extract interesting data from a json lint message /// Parse the json output of clippy and return a `ClippyWarning`
fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
@ -307,18 +423,84 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
.into(), .into(),
linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
message: jmsg["message"]["message"].to_string().trim_matches('"').into(), message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
ice: json_message.contains("internal compiler error: "), is_ice: json_message.contains("internal compiler error: "),
} }
} }
// the main fn /// Generate a short list of occuring lints-types and their count
pub fn run(clap_config: &ArgMatches) { fn gather_stats(clippy_warnings: &[ClippyWarning]) -> String {
let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy"); // count lint type occurrences
let mut counter: HashMap<&String, usize> = HashMap::new();
clippy_warnings
.iter()
.for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
// collect into a tupled list for sorting
let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
// sort by "000{count} {clippy::lintname}"
// to not have a lint with 200 and 2 warnings take the same spot
stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
stats
.iter()
.map(|(lint, count)| format!("{} {}\n", lint, count))
.collect::<String>()
}
/// check if the latest modification of the logfile is older than the modification date of the
/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
fn lintcheck_needs_rerun(toml_path: Option<&str>) -> bool {
let clippy_modified: std::time::SystemTime = {
let mut times = ["target/debug/clippy-driver", "target/debug/cargo-clippy"]
.iter()
.map(|p| {
std::fs::metadata(p)
.expect("failed to get metadata of file")
.modified()
.expect("failed to get modification date")
});
// the lates modification of either of the binaries
std::cmp::max(times.next().unwrap(), times.next().unwrap())
};
let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_config_toml(toml_path))
.expect("failed to get metadata of file")
.modified()
.expect("failed to get modification date");
// if clippys modification time is bigger (older) than the logs mod time, we need to rerun lintcheck
clippy_modified > logs_modified
}
/// lintchecks `main()` function
pub fn run(clap_config: &ArgMatches) {
println!("Compiling clippy..."); println!("Compiling clippy...");
build_clippy(); build_clippy();
println!("Done compiling"); println!("Done compiling");
let clap_toml_path = clap_config.value_of("crates-toml");
// if the clippy bin is newer than our logs, throw away target dirs to force clippy to
// refresh the logs
if lintcheck_needs_rerun(clap_toml_path) {
let shared_target_dir = "target/lintcheck/shared_target_dir";
match std::fs::metadata(&shared_target_dir) {
Ok(metadata) => {
if metadata.is_dir() {
println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
std::fs::remove_dir_all(&shared_target_dir)
.expect("failed to remove target/lintcheck/shared_target_dir");
}
},
Err(_) => { // dir probably does not exist, don't remove anything
},
}
}
let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy")
.canonicalize()
.expect("failed to canonicalize path to clippy binary");
// assert that clippy is found // assert that clippy is found
assert!( assert!(
cargo_clippy_path.is_file(), cargo_clippy_path.is_file(),
@ -335,7 +517,7 @@ pub fn run(clap_config: &ArgMatches) {
// download and extract the crates, then run clippy on them and collect clippys warnings // download and extract the crates, then run clippy on them and collect clippys warnings
// flatten into one big list of warnings // flatten into one big list of warnings
let (filename, crates) = read_crates(clap_config.value_of("crates-toml")); let (filename, crates) = read_crates(clap_toml_path);
let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") { let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
// if we don't have the specified crate in the .toml, throw an error // if we don't have the specified crate in the .toml, throw an error
@ -359,45 +541,60 @@ pub fn run(clap_config: &ArgMatches) {
.into_iter() .into_iter()
.map(|krate| krate.download_and_extract()) .map(|krate| krate.download_and_extract())
.filter(|krate| krate.name == only_one_crate) .filter(|krate| krate.name == only_one_crate)
.map(|krate| krate.run_clippy_lints(&cargo_clippy_path)) .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1))
.flatten() .flatten()
.collect() .collect()
} else { } else {
let counter = std::sync::atomic::AtomicUsize::new(0);
// Ask rayon for thread count. Assume that half of that is the number of physical cores
// Use one target dir for each core so that we can run N clippys in parallel.
// We need to use different target dirs because cargo would lock them for a single build otherwise,
// killing the parallelism. However this also means that deps will only be reused half/a
// quarter of the time which might result in a longer wall clock runtime
// This helps when we check many small crates with dep-trees that don't have a lot of branches in
// order to achive some kind of parallelism
// by default, use a single thread
let num_cpus = match clap_config.value_of("threads") {
Some(threads) => {
let threads: usize = threads
.parse()
.expect(&format!("Failed to parse '{}' to a digit", threads));
if threads == 0 {
// automatic choice
// Rayon seems to return thread count so half that for core count
(rayon::current_num_threads() / 2) as usize
} else {
threads
}
},
// no -j passed, use a single thread
None => 1,
};
let num_crates = crates.len();
// check all crates (default) // check all crates (default)
crates crates
.into_iter() .into_par_iter()
.map(|krate| krate.download_and_extract()) .map(|krate| krate.download_and_extract())
.map(|krate| krate.run_clippy_lints(&cargo_clippy_path)) .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates))
.flatten() .flatten()
.collect() .collect()
}; };
// generate some stats: // generate some stats
let stats_formatted = gather_stats(&clippy_warnings);
// grab crashes/ICEs, save the crate name and the ice message // grab crashes/ICEs, save the crate name and the ice message
let ices: Vec<(&String, &String)> = clippy_warnings let ices: Vec<(&String, &String)> = clippy_warnings
.iter() .iter()
.filter(|warning| warning.ice) .filter(|warning| warning.is_ice)
.map(|w| (&w.crate_name, &w.message)) .map(|w| (&w.crate_name, &w.message))
.collect(); .collect();
// count lint type occurrences
let mut counter: HashMap<&String, usize> = HashMap::new();
clippy_warnings
.iter()
.for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
// collect into a tupled list for sorting
let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
// sort by "000{count} {clippy::lintname}"
// to not have a lint with 200 and 2 warnings take the same spot
stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
let stats_formatted: String = stats
.iter()
.map(|(lint, count)| format!("{} {}\n", lint, count))
.collect::<String>();
let mut all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect(); let mut all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect();
all_msgs.sort(); all_msgs.sort();
all_msgs.push("\n\n\n\nStats\n\n".into()); all_msgs.push("\n\n\n\nStats\n\n".into());
@ -411,5 +608,6 @@ pub fn run(clap_config: &ArgMatches) {
.for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
let file = format!("lintcheck-logs/{}_logs.txt", filename); let file = format!("lintcheck-logs/{}_logs.txt", filename);
println!("Writing logs to {}", file);
write(file, text).unwrap(); write(file, text).unwrap();
} }

View file

@ -69,6 +69,14 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.value_name("CRATES-SOURCES-TOML-PATH") .value_name("CRATES-SOURCES-TOML-PATH")
.long("crates-toml") .long("crates-toml")
.help("set the path for a crates.toml where lintcheck should read the sources from"), .help("set the path for a crates.toml where lintcheck should read the sources from"),
)
.arg(
Arg::with_name("threads")
.takes_value(true)
.value_name("N")
.short("j")
.long("jobs")
.help("number of threads to use, 0 automatic choice"),
); );
let app = App::new("Clippy developer tooling") let app = App::new("Clippy developer tooling")

View file

@ -18,6 +18,7 @@ edition = "2018"
[dependencies] [dependencies]
cargo_metadata = "0.12" cargo_metadata = "0.12"
clippy_utils = { path = "../clippy_utils" }
if_chain = "1.0.0" if_chain = "1.0.0"
itertools = "0.9" itertools = "0.9"
pulldown-cmark = { version = "0.8", default-features = false } pulldown-cmark = { version = "0.8", default-features = false }
@ -38,4 +39,4 @@ syn = { version = "1", features = ["full"] }
[features] [features]
deny-warnings = [] deny-warnings = []
# build clippy with internal lints enabled, off by default # build clippy with internal lints enabled, off by default
internal-lints = [] internal-lints = ["clippy_utils/internal-lints"]

View file

@ -1,4 +1,8 @@
use crate::utils::{differing_macro_contexts, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; use crate::utils::{
differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths,
snippet_block_with_applicability, span_lint, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_hir::{BlockCheckMode, Expr, ExprKind};
@ -52,6 +56,18 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
// do not lint if the closure is called using an iterator (see #1141)
if_chain! {
if let Some(parent) = get_parent_expr(self.cx, expr);
if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
let caller = self.cx.typeck_results().expr_ty(&args[0]);
if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR);
if implements_trait(self.cx, caller, iter_id, &[]);
then {
return;
}
}
let body = self.cx.tcx.hir().body(eid); let body = self.cx.tcx.hir().body(eid);
let ex = &body.value; let ex = &body.value;
if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {

View file

@ -122,6 +122,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
if let ast::ExprKind::Block(ref block, _) = else_.kind; if let ast::ExprKind::Block(ref block, _) = else_.kind;
if !block_starts_with_comment(cx, block); if !block_starts_with_comment(cx, block);
if let Some(else_) = expr_block(block); if let Some(else_) = expr_block(block);
if else_.attrs.is_empty();
if !else_.span.from_expansion(); if !else_.span.from_expansion();
if let ast::ExprKind::If(..) = else_.kind; if let ast::ExprKind::If(..) = else_.kind;
then { then {
@ -143,16 +144,12 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
if_chain! { if_chain! {
if !block_starts_with_comment(cx, then); if !block_starts_with_comment(cx, then);
if let Some(inner) = expr_block(then); if let Some(inner) = expr_block(then);
if inner.attrs.is_empty();
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
// Prevent triggering on `if c { if let a = b { .. } }`.
if !matches!(check_inner.kind, ast::ExprKind::Let(..));
if expr.span.ctxt() == inner.span.ctxt();
then { then {
if let ast::ExprKind::Let(..) = check_inner.kind {
// Prevent triggering on `if c { if let a = b { .. } }`.
return;
}
if expr.span.ctxt() != inner.span.ctxt() {
return;
}
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
let lhs = Sugg::ast(cx, check, ".."); let lhs = Sugg::ast(cx, check, "..");
let rhs = Sugg::ast(cx, check_inner, ".."); let rhs = Sugg::ast(cx, check_inner, "..");

View file

@ -96,12 +96,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
cx, cx,
COLLAPSIBLE_MATCH, COLLAPSIBLE_MATCH,
expr.span, expr.span,
"Unnecessary nested match", "unnecessary nested match",
|diag| { |diag| {
let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]);
help_span.push_span_label(binding_span, "Replace this binding".into()); help_span.push_span_label(binding_span, "replace this binding".into());
help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into());
diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
}, },
); );
} }

View file

@ -1,574 +1 @@
#![allow(clippy::float_cmp)] pub use clippy_utils::consts::*;
use crate::utils::{clip, sext, unsext};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Symbol;
use std::cmp::Ordering::{self, Equal};
use std::convert::TryInto;
use std::hash::{Hash, Hasher};
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
pub enum Constant {
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
Binary(Lrc<[u8]>),
/// A single `char` (e.g., `'a'`).
Char(char),
/// An integer's bit representation.
Int(u128),
/// An `f32`.
F32(f32),
/// An `f64`.
F64(f64),
/// `true` or `false`.
Bool(bool),
/// An array of constants.
Vec(Vec<Constant>),
/// Also an array, but with only one constant, repeated N times.
Repeat(Box<Constant>, u64),
/// A tuple of constants.
Tuple(Vec<Constant>),
/// A raw pointer.
RawPtr(u128),
/// A reference
Ref(Box<Constant>),
/// A literal with syntax error.
Err(Symbol),
}
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
(&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
(&Self::Char(l), &Self::Char(r)) => l == r,
(&Self::Int(l), &Self::Int(r)) => l == r,
(&Self::F64(l), &Self::F64(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
l.to_bits() == r.to_bits()
},
(&Self::F32(l), &Self::F32(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
f64::from(l).to_bits() == f64::from(r).to_bits()
},
(&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
(&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
// TODO: are there inter-type equalities?
_ => false,
}
}
}
impl Hash for Constant {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
std::mem::discriminant(self).hash(state);
match *self {
Self::Str(ref s) => {
s.hash(state);
},
Self::Binary(ref b) => {
b.hash(state);
},
Self::Char(c) => {
c.hash(state);
},
Self::Int(i) => {
i.hash(state);
},
Self::F32(f) => {
f64::from(f).to_bits().hash(state);
},
Self::F64(f) => {
f.to_bits().hash(state);
},
Self::Bool(b) => {
b.hash(state);
},
Self::Vec(ref v) | Self::Tuple(ref v) => {
v.hash(state);
},
Self::Repeat(ref c, l) => {
c.hash(state);
l.hash(state);
},
Self::RawPtr(u) => {
u.hash(state);
},
Self::Ref(ref r) => {
r.hash(state);
},
Self::Err(ref s) => {
s.hash(state);
},
}
}
}
impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
(&Self::Int(l), &Self::Int(r)) => {
if let ty::Int(int_ty) = *cmp_type.kind() {
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
} else {
Some(l.cmp(&r))
}
},
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
(&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
(&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
.iter()
.zip(r.iter())
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
.find(|r| r.map_or(true, |o| o != Ordering::Equal))
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
match Self::partial_cmp(tcx, cmp_type, lv, rv) {
Some(Equal) => Some(ls.cmp(rs)),
x => x,
}
},
(&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
// TODO: are there any useful inter-type orderings?
_ => None,
}
}
}
/// Parses a `LitKind` to a `Constant`.
pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, _) => Constant::Int(n),
LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
},
LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
_ => bug!(),
},
LitKind::Bool(b) => Constant::Bool(b),
LitKind::Err(s) => Constant::Err(s),
}
}
pub fn constant<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext {
lcx,
typeck_results,
param_env: lcx.param_env,
needed_resolution: false,
substs: lcx.tcx.intern_substs(&[]),
};
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
}
pub fn constant_simple<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<Constant> {
constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
}
/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
pub fn constant_context<'a, 'tcx>(
lcx: &'a LateContext<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> ConstEvalLateContext<'a, 'tcx> {
ConstEvalLateContext {
lcx,
typeck_results,
param_env: lcx.param_env,
needed_resolution: false,
substs: lcx.tcx.intern_substs(&[]),
}
}
pub struct ConstEvalLateContext<'a, 'tcx> {
lcx: &'a LateContext<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>,
needed_resolution: bool,
substs: SubstsRef<'tcx>,
}
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Simple constant folding: Insert an expression, get a constant or none.
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
match e.kind {
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(ref block, _) => self.block(block),
ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(ref value, _) => {
let n = match self.typeck_results.expr_ty(e).kind() {
ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
_ => span_bug!(e.span, "typeck error"),
};
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
},
ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
}),
ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
ExprKind::Call(ref callee, ref args) => {
// We only handle a few const functions for now.
if_chain! {
if args.is_empty();
if let ExprKind::Path(qpath) = &callee.kind;
let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
if let Some(def_id) = res.opt_def_id();
let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
if let ["core", "num", int_impl, "max_value"] = *def_path;
then {
let value = match int_impl {
"<impl i8>" => i8::MAX as u128,
"<impl i16>" => i16::MAX as u128,
"<impl i32>" => i32::MAX as u128,
"<impl i64>" => i64::MAX as u128,
"<impl i128>" => i128::MAX as u128,
_ => return None,
};
Some(Constant::Int(value))
}
else {
None
}
}
},
ExprKind::Index(ref arr, ref index) => self.index(arr, index),
ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
// TODO: add other expressions.
_ => None,
}
}
#[allow(clippy::cast_possible_wrap)]
fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{Bool, Int};
match *o {
Bool(b) => Some(Bool(!b)),
Int(value) => {
let value = !value;
match *ty.kind() {
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
_ => None,
}
},
_ => None,
}
}
fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
let ity = match *ty.kind() {
ty::Int(ity) => ity,
_ => return None,
};
// sign extend
let value = sext(self.lcx.tcx, value, ity);
let value = value.checked_neg()?;
// clear unused bits
Some(Int(unsext(self.lcx.tcx, value, ity)))
},
F32(f) => Some(F32(-f)),
F64(f) => Some(F64(-f)),
_ => None,
}
}
/// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part.
fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
}
/// Lookup a possibly constant expression from a `ExprKind::Path`.
fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
let res = self.typeck_results.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
let substs = self.typeck_results.node_substs(id);
let substs = if self.substs.is_empty() {
substs
} else {
substs.subst(self.lcx.tcx, self.substs)
};
let result = self
.lcx
.tcx
.const_eval_resolve(
self.param_env,
ty::WithOptConstParam::unknown(def_id),
substs,
None,
None,
)
.ok()
.map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
let result = miri_to_const(&result);
if result.is_some() {
self.needed_resolution = true;
}
result
},
// FIXME: cover all usable cases.
_ => None,
}
}
fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
let lhs = self.expr(lhs);
let index = self.expr(index);
match (lhs, index) {
(Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
Some(Constant::F32(x)) => Some(Constant::F32(*x)),
Some(Constant::F64(x)) => Some(Constant::F64(*x)),
_ => None,
},
(Some(Constant::Vec(vec)), _) => {
if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
match vec.get(0) {
Some(Constant::F32(x)) => Some(Constant::F32(*x)),
Some(Constant::F64(x)) => Some(Constant::F64(*x)),
_ => None,
}
} else {
None
}
},
_ => None,
}
}
/// A block can only yield a constant if it only has one constant expression.
fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
if block.stmts.is_empty() {
block.expr.as_ref().and_then(|b| self.expr(b))
} else {
None
}
}
fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
if let Some(Constant::Bool(b)) = self.expr(cond) {
if b {
self.expr(&*then)
} else {
otherwise.as_ref().and_then(|expr| self.expr(expr))
}
} else {
None
}
}
fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
(Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
ty::Int(ity) => {
let l = sext(self.lcx.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity);
let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
match op.node {
BinOpKind::Add => l.checked_add(r).map(zext),
BinOpKind::Sub => l.checked_sub(r).map(zext),
BinOpKind::Mul => l.checked_mul(r).map(zext),
BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
BinOpKind::BitXor => Some(zext(l ^ r)),
BinOpKind::BitOr => Some(zext(l | r)),
BinOpKind::BitAnd => Some(zext(l & r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
}
},
ty::Uint(_) => match op.node {
BinOpKind::Add => l.checked_add(r).map(Constant::Int),
BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
BinOpKind::Div => l.checked_div(r).map(Constant::Int),
BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
BinOpKind::BitOr => Some(Constant::Int(l | r)),
BinOpKind::BitAnd => Some(Constant::Int(l & r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
_ => None,
},
(Constant::F32(l), Some(Constant::F32(r))) => match op.node {
BinOpKind::Add => Some(Constant::F32(l + r)),
BinOpKind::Sub => Some(Constant::F32(l - r)),
BinOpKind::Mul => Some(Constant::F32(l * r)),
BinOpKind::Div => Some(Constant::F32(l / r)),
BinOpKind::Rem => Some(Constant::F32(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F64(l), Some(Constant::F64(r))) => match op.node {
BinOpKind::Add => Some(Constant::F64(l + r)),
BinOpKind::Sub => Some(Constant::F64(l - r)),
BinOpKind::Mul => Some(Constant::F64(l * r)),
BinOpKind::Div => Some(Constant::F64(l / r)),
BinOpKind::Rem => Some(Constant::F64(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(l, r) => match (op.node, l, r) {
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
(BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
Some(r)
},
(BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
(BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
(BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
_ => None,
},
}
}
}
pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
use rustc_middle::mir::interpret::ConstValue;
match result.val {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
match result.ty.kind() {
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
int.try_into().expect("invalid f32 bit representation"),
))),
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
int.try_into().expect("invalid f64 bit representation"),
))),
ty::RawPtr(type_and_mut) => {
if let ty::Uint(_) = type_and_mut.ty.kind() {
return Some(Constant::RawPtr(int.assert_bits(int.size())));
}
None
},
// FIXME: implement other conversions.
_ => None,
}
},
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8(
data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
.to_owned(),
)
.ok()
.map(Constant::Str),
_ => None,
},
_ => None,
},
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match miri_to_const(len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
.to_owned()
.chunks(4)
.map(|chunk| {
Some(Constant::F32(f32::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match miri_to_const(len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
.to_owned()
.chunks(8)
.map(|chunk| {
Some(Constant::F64(f64::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,
},
// FIXME: implement other array type conversions.
_ => None,
},
_ => None,
},
// FIXME: implement other conversions.
_ => None,
}
}

View file

@ -0,0 +1,237 @@
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::{
hir::map::Map,
ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use if_chain::if_chain;
use crate::utils::{snippet, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
/// inference.
///
/// Default numeric fallback means that if numeric types have not yet been bound to concrete
/// types at the end of type inference, then integer type is bound to `i32`, and similarly
/// floating type is bound to `f64`.
///
/// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
///
/// **Why is this bad?** For those who are very careful about types, default numeric fallback
/// can be a pitfall that cause unexpected runtime behavior.
///
/// **Known problems:** This lint can only be allowed at the function level or above.
///
/// **Example:**
/// ```rust
/// let i = 10;
/// let f = 1.23;
/// ```
///
/// Use instead:
/// ```rust
/// let i = 10i32;
/// let f = 1.23f64;
/// ```
pub DEFAULT_NUMERIC_FALLBACK,
restriction,
"usage of unconstrained numeric literals which may cause default numeric fallback."
}
declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
impl LateLintPass<'_> for DefaultNumericFallback {
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let mut visitor = NumericFallbackVisitor::new(cx);
visitor.visit_body(body);
}
}
struct NumericFallbackVisitor<'a, 'tcx> {
/// Stack manages type bound of exprs. The top element holds current expr type.
ty_bounds: Vec<TyBound<'tcx>>,
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
ty_bounds: vec![TyBound::Nothing],
cx,
}
}
/// Check whether a passed literal has potential to cause fallback or not.
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
if_chain! {
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
if !ty_bound.is_integral();
then {
let suffix = match lit_ty.kind() {
ty::Int(IntTy::I32) => "i32",
ty::Float(FloatTy::F64) => "f64",
// Default numeric fallback never results in other types.
_ => return,
};
let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
span_lint_and_sugg(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
lit.span,
"default numeric fallback might occur",
"consider adding suffix",
sugg,
Applicability::MaybeIncorrect,
);
}
}
}
}
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
type Map = Map<'tcx>;
#[allow(clippy::too_many_lines)]
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
match &expr.kind {
ExprKind::Call(func, args) => {
if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
for (expr, bound) in args.iter().zip(fn_sig.skip_binder().inputs().iter()) {
// Push found arg type, then visit arg.
self.ty_bounds.push(TyBound::Ty(bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
return;
}
},
ExprKind::MethodCall(_, _, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
self.ty_bounds.push(TyBound::Ty(bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
return;
}
},
ExprKind::Struct(qpath, fields, base) => {
if_chain! {
if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id();
let ty = self.cx.tcx.type_of(def_id);
if let Some(adt_def) = ty.ty_adt_def();
if adt_def.is_struct();
if let Some(variant) = adt_def.variants.iter().next();
then {
let fields_def = &variant.fields;
// Push field type then visit each field expr.
for field in fields.iter() {
let bound =
fields_def
.iter()
.find_map(|f_def| {
if f_def.ident == field.ident
{ Some(self.cx.tcx.type_of(f_def.did)) }
else { None }
});
self.ty_bounds.push(bound.into());
self.visit_expr(field.expr);
self.ty_bounds.pop();
}
// Visit base with no bound.
if let Some(base) = base {
self.ty_bounds.push(TyBound::Nothing);
self.visit_expr(base);
self.ty_bounds.pop();
}
return;
}
}
},
ExprKind::Lit(lit) => {
let ty = self.cx.typeck_results().expr_ty(expr);
self.check_lit(lit, ty);
return;
},
_ => {},
}
walk_expr(self, expr);
}
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
match stmt.kind {
StmtKind::Local(local) => {
if local.ty.is_some() {
self.ty_bounds.push(TyBound::Any)
} else {
self.ty_bounds.push(TyBound::Nothing)
}
},
_ => self.ty_bounds.push(TyBound::Nothing),
}
walk_stmt(self, stmt);
self.ty_bounds.pop();
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
// We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs.
match node_ty.kind() {
ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
ty::FnPtr(fn_sig) => Some(*fn_sig),
_ => None,
}
}
#[derive(Debug, Clone, Copy)]
enum TyBound<'tcx> {
Any,
Ty(Ty<'tcx>),
Nothing,
}
impl<'tcx> TyBound<'tcx> {
fn is_integral(self) -> bool {
match self {
TyBound::Any => true,
TyBound::Ty(t) => t.is_integral(),
TyBound::Nothing => false,
}
}
}
impl<'tcx> From<Option<Ty<'tcx>>> for TyBound<'tcx> {
fn from(v: Option<Ty<'tcx>>) -> Self {
match v {
Some(t) => TyBound::Ty(t),
None => TyBound::Nothing,
}
}
}

View file

@ -1,6 +1,6 @@
use crate::utils::{ use crate::utils::{
implements_trait, is_entrypoint_fn, is_type_diagnostic_item, match_panic_def_id, method_chain_args, return_ty, implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args,
span_lint, span_lint_and_note, return_ty, span_lint, span_lint_and_note,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use itertools::Itertools; use itertools::Itertools;
@ -216,9 +216,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
let headers = check_attrs(cx, &self.valid_idents, &item.attrs); let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
match item.kind { match item.kind {
hir::ItemKind::Fn(ref sig, _, body_id) => { hir::ItemKind::Fn(ref sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|| in_external_macro(cx.tcx.sess, item.span))
{
let body = cx.tcx.hir().body(body_id); let body = cx.tcx.hir().body(body_id);
let mut fpu = FindPanicUnwrap { let mut fpu = FindPanicUnwrap {
cx, cx,
@ -226,7 +224,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(&body.value); fpu.visit_expr(&body.value);
lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, Some(body_id), fpu.panic_span); lint_for_missing_headers(
cx,
item.hir_id(),
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
}, },
hir::ItemKind::Impl(ref impl_) => { hir::ItemKind::Impl(ref impl_) => {
@ -264,7 +270,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
panic_span: None, panic_span: None,
}; };
fpu.visit_expr(&body.value); fpu.visit_expr(&body.value);
lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, Some(body_id), fpu.panic_span); lint_for_missing_headers(
cx,
item.hir_id(),
item.span,
sig,
headers,
Some(body_id),
fpu.panic_span,
);
} }
} }
} }
@ -561,9 +575,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
| ItemKind::ExternCrate(..) | ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..) => return false, | ItemKind::ForeignMod(..) => return false,
// We found a main function ... // We found a main function ...
ItemKind::Fn(box FnKind(_, sig, _, Some(block))) ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => {
if item.ident.name == sym::main =>
{
let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
let returns_nothing = match &sig.decl.output { let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true, FnRetTy::Default(..) => true,
@ -699,6 +711,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind;
if let Some(path_def_id) = path.res.opt_def_id(); if let Some(path_def_id) = path.res.opt_def_id();
if match_panic_def_id(self.cx, path_def_id); if match_panic_def_id(self.cx, path_def_id);
if is_expn_of(expr.span, "unreachable").is_none();
then { then {
self.panic_span = Some(expr.span); self.panic_span = Some(expr.span);
} }

View file

@ -1,7 +1,5 @@
use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help}; use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
use rustc_ast::ast::{ use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind,
};
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span; use rustc_span::Span;

View file

@ -0,0 +1,101 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
use crate::utils::is_type_diagnostic_item;
use crate::utils::span_lint_and_sugg;
use crate::utils::sugg::Sugg;
declare_clippy_lint! {
/// **What it does:**
/// Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
///
/// **Why is this bad?**
/// This specific common use case can be rewritten as `s.parse::<primitive>()`
/// (and in most cases, the turbofish can be removed), which reduces code length
/// and complexity.
///
/// **Known problems:**
/// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
/// in some cases, which is correct but adds unnecessary complexity to the code.
///
/// **Example:**
///
/// ```ignore
/// let input: &str = get_input();
/// let num = u16::from_str_radix(input, 10)?;
/// ```
/// Use instead:
/// ```ignore
/// let input: &str = get_input();
/// let num: u16 = input.parse()?;
/// ```
pub FROM_STR_RADIX_10,
style,
"from_str_radix with radix 10"
}
declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
impl LateLintPass<'tcx> for FromStrRadix10 {
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
if_chain! {
if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
// check if the first part of the path is some integer primitive
if let TyKind::Path(ty_qpath) = &ty.kind;
let ty_res = cx.qpath_res(ty_qpath, ty.hir_id);
if let def::Res::PrimTy(prim_ty) = ty_res;
if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_));
// check if the second part of the path indeed calls the associated
// function `from_str_radix`
if pathseg.ident.name.as_str() == "from_str_radix";
// check if the second argument is a primitive `10`
if arguments.len() == 2;
if let ExprKind::Lit(lit) = &arguments[1].kind;
if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
then {
let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind {
let ty = cx.typeck_results().expr_ty(expr);
if is_ty_stringish(cx, ty) {
expr
} else {
&arguments[0]
}
} else {
&arguments[0]
};
let sugg = Sugg::hir_with_applicability(
cx,
expr,
"<string>",
&mut Applicability::MachineApplicable
).maybe_par();
span_lint_and_sugg(
cx,
FROM_STR_RADIX_10,
exp.span,
"this call to `from_str_radix` can be replaced with a call to `str::parse`",
"try",
format!("{}.parse::<{}>()", sugg, prim_ty.name_str()),
Applicability::MaybeIncorrect
);
}
}
}
}
/// Checks if a Ty is `String` or `&str`
fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
is_type_diagnostic_item(cx, ty, sym::string_type) || is_type_diagnostic_item(cx, ty, sym::str)
}

View file

@ -1,7 +1,7 @@
use crate::utils::{ use crate::utils::{
attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
last_path_segment, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::Attribute; use rustc_ast::ast::Attribute;
@ -470,12 +470,11 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span
if_chain! { if_chain! {
if !in_external_macro(cx.sess(), item_span); if !in_external_macro(cx.sess(), item_span);
if let hir::FnRetTy::Return(ref ty) = decl.output; if let hir::FnRetTy::Return(ref ty) = decl.output;
if let hir::TyKind::Path(ref qpath) = ty.kind; let ty = hir_ty_to_ty(cx.tcx, ty);
if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type); if is_type_diagnostic_item(cx, ty, sym::result_type);
if let Some(ref args) = last_path_segment(qpath).args; if let ty::Adt(_, substs) = ty.kind();
if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; let err_ty = substs.type_at(1);
if let hir::TyKind::Tup(t) = err_ty.kind; if err_ty.is_unit();
if t.is_empty();
then { then {
span_lint_and_help( span_lint_and_help(
cx, cx,

View file

@ -0,0 +1,134 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
use if_chain::if_chain;
use crate::utils::{snippet, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for struct constructors where the order of the field init
/// shorthand in the constructor is inconsistent with the order in the struct definition.
///
/// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
/// resulted instance as the below example indicates,
///
/// ```rust
/// #[derive(Debug, PartialEq, Eq)]
/// struct Foo {
/// x: i32,
/// y: i32,
/// }
/// let x = 1;
/// let y = 2;
///
/// // This assertion never fails.
/// assert_eq!(Foo { x, y }, Foo { y, x });
/// ```
///
/// inconsistent order means nothing and just decreases readability and consistency.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// struct Foo {
/// x: i32,
/// y: i32,
/// }
/// let x = 1;
/// let y = 2;
/// Foo { y, x };
/// ```
///
/// Use instead:
/// ```rust
/// # struct Foo {
/// # x: i32,
/// # y: i32,
/// # }
/// # let x = 1;
/// # let y = 2;
/// Foo { x, y };
/// ```
pub INCONSISTENT_STRUCT_CONSTRUCTOR,
style,
"the order of the field init shorthand is inconsistent with the order in the struct definition"
}
declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
impl LateLintPass<'_> for InconsistentStructConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
if let ExprKind::Struct(qpath, fields, base) = expr.kind;
if let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id();
let ty = cx.tcx.type_of(def_id);
if let Some(adt_def) = ty.ty_adt_def();
if adt_def.is_struct();
if let Some(variant) = adt_def.variants.iter().next();
if fields.iter().all(|f| f.is_shorthand);
then {
let mut def_order_map = FxHashMap::default();
for (idx, field) in variant.fields.iter().enumerate() {
def_order_map.insert(field.ident.name, idx);
}
if is_consistent_order(fields, &def_order_map) {
return;
}
let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);
let mut fields_snippet = String::new();
let (last_ident, idents) = ordered_fields.split_last().unwrap();
for ident in idents {
fields_snippet.push_str(&format!("{}, ", ident));
}
fields_snippet.push_str(&last_ident.to_string());
let base_snippet = if let Some(base) = base {
format!(", ..{}", snippet(cx, base.span, ".."))
} else {
String::new()
};
let sugg = format!("{} {{ {}{} }}",
snippet(cx, qpath.span(), ".."),
fields_snippet,
base_snippet,
);
span_lint_and_sugg(
cx,
INCONSISTENT_STRUCT_CONSTRUCTOR,
expr.span,
"inconsistent struct constructor",
"try",
sugg,
Applicability::MachineApplicable,
)
}
}
}
}
// Check whether the order of the fields in the constructor is consistent with the order in the
// definition.
fn is_consistent_order<'tcx>(fields: &'tcx [hir::Field<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool {
let mut cur_idx = usize::MIN;
for f in fields {
let next_idx = def_order_map[&f.ident.name];
if cur_idx > next_idx {
return false;
}
cur_idx = next_idx;
}
true
}

View file

@ -106,6 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
let decl = &signature.decl; let decl = &signature.decl;
if decl.implicit_self.has_implicit_self(); if decl.implicit_self.has_implicit_self();
if decl.inputs.len() == 1; if decl.inputs.len() == 1;
if impl_item.generics.params.is_empty();
// Check if return type is String // Check if return type is String
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::string_type); if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::string_type);

View file

@ -1,10 +1,7 @@
// error-pattern:cargo-clippy // error-pattern:cargo-clippy
#![feature(bindings_after_at)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(concat_idents)]
#![feature(crate_visibility_modifier)]
#![feature(drain_filter)] #![feature(drain_filter)]
#![feature(in_band_lifetimes)] #![feature(in_band_lifetimes)]
#![feature(once_cell)] #![feature(once_cell)]
@ -149,6 +146,20 @@ macro_rules! declare_clippy_lint {
}; };
} }
#[macro_export]
macro_rules! sym {
( $($x:tt)* ) => { clippy_utils::sym!($($x)*) }
}
#[macro_export]
macro_rules! unwrap_cargo_metadata {
( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*) }
}
macro_rules! extract_msrv_attr {
( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); }
}
mod consts; mod consts;
#[macro_use] #[macro_use]
mod utils; mod utils;
@ -181,6 +192,7 @@ mod copy_iterator;
mod create_dir; mod create_dir;
mod dbg_macro; mod dbg_macro;
mod default; mod default;
mod default_numeric_fallback;
mod dereference; mod dereference;
mod derive; mod derive;
mod disallowed_method; mod disallowed_method;
@ -210,6 +222,7 @@ mod floating_point_arithmetic;
mod format; mod format;
mod formatting; mod formatting;
mod from_over_into; mod from_over_into;
mod from_str_radix_10;
mod functions; mod functions;
mod future_not_send; mod future_not_send;
mod get_last_with_len; mod get_last_with_len;
@ -219,6 +232,7 @@ mod if_let_some_result;
mod if_not_else; mod if_not_else;
mod implicit_return; mod implicit_return;
mod implicit_saturating_sub; mod implicit_saturating_sub;
mod inconsistent_struct_constructor;
mod indexing_slicing; mod indexing_slicing;
mod infinite_iter; mod infinite_iter;
mod inherent_impl; mod inherent_impl;
@ -239,6 +253,7 @@ mod loops;
mod macro_use; mod macro_use;
mod main_recursion; mod main_recursion;
mod manual_async_fn; mod manual_async_fn;
mod manual_map;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_ok_or; mod manual_ok_or;
mod manual_strip; mod manual_strip;
@ -585,6 +600,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&dbg_macro::DBG_MACRO, &dbg_macro::DBG_MACRO,
&default::DEFAULT_TRAIT_ACCESS, &default::DEFAULT_TRAIT_ACCESS,
&default::FIELD_REASSIGN_WITH_DEFAULT, &default::FIELD_REASSIGN_WITH_DEFAULT,
&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
&dereference::EXPLICIT_DEREF_METHODS, &dereference::EXPLICIT_DEREF_METHODS,
&derive::DERIVE_HASH_XOR_EQ, &derive::DERIVE_HASH_XOR_EQ,
&derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::DERIVE_ORD_XOR_PARTIAL_ORD,
@ -637,6 +653,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&formatting::SUSPICIOUS_ELSE_FORMATTING, &formatting::SUSPICIOUS_ELSE_FORMATTING,
&formatting::SUSPICIOUS_UNARY_OP_FORMATTING, &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
&from_over_into::FROM_OVER_INTO, &from_over_into::FROM_OVER_INTO,
&from_str_radix_10::FROM_STR_RADIX_10,
&functions::DOUBLE_MUST_USE, &functions::DOUBLE_MUST_USE,
&functions::MUST_USE_CANDIDATE, &functions::MUST_USE_CANDIDATE,
&functions::MUST_USE_UNIT, &functions::MUST_USE_UNIT,
@ -652,6 +669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&if_not_else::IF_NOT_ELSE, &if_not_else::IF_NOT_ELSE,
&implicit_return::IMPLICIT_RETURN, &implicit_return::IMPLICIT_RETURN,
&implicit_saturating_sub::IMPLICIT_SATURATING_SUB, &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
&indexing_slicing::INDEXING_SLICING, &indexing_slicing::INDEXING_SLICING,
&indexing_slicing::OUT_OF_BOUNDS_INDEXING, &indexing_slicing::OUT_OF_BOUNDS_INDEXING,
&infinite_iter::INFINITE_ITER, &infinite_iter::INFINITE_ITER,
@ -702,6 +720,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&macro_use::MACRO_USE_IMPORTS, &macro_use::MACRO_USE_IMPORTS,
&main_recursion::MAIN_RECURSION, &main_recursion::MAIN_RECURSION,
&manual_async_fn::MANUAL_ASYNC_FN, &manual_async_fn::MANUAL_ASYNC_FN,
&manual_map::MANUAL_MAP,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
&manual_ok_or::MANUAL_OK_OR, &manual_ok_or::MANUAL_OK_OR,
&manual_strip::MANUAL_STRIP, &manual_strip::MANUAL_STRIP,
@ -1031,6 +1050,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box strings::StringAdd);
store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_return::ImplicitReturn);
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
let msrv = conf.msrv.as_ref().and_then(|s| { let msrv = conf.msrv.as_ref().and_then(|s| {
parse_msrv(s, None, None).or_else(|| { parse_msrv(s, None, None).or_else(|| {
@ -1256,6 +1277,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
store.register_late_pass(|| box manual_map::ManualMap);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1265,6 +1288,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(&create_dir::CREATE_DIR), LintId::of(&create_dir::CREATE_DIR),
LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
@ -1389,8 +1413,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::PTR_AS_PTR), LintId::of(&types::PTR_AS_PTR),
LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::NON_ASCII_LITERAL),
LintId::of(&unicode::UNICODE_NOT_NFC), LintId::of(&unicode::UNICODE_NOT_NFC),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(&unused_self::UNUSED_SELF), LintId::of(&unused_self::UNUSED_SELF),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::ENUM_GLOB_USE),
LintId::of(&wildcard_imports::WILDCARD_IMPORTS), LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
@ -1468,6 +1494,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::DOUBLE_MUST_USE),
LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::MUST_USE_UNIT),
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
@ -1477,6 +1504,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&identity_op::IDENTITY_OP), LintId::of(&identity_op::IDENTITY_OP),
LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_mutex::IF_LET_MUTEX),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&infinite_iter::INFINITE_ITER),
LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&inherent_to_string::INHERENT_TO_STRING),
@ -1512,6 +1540,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_map::MANUAL_MAP),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&manual_strip::MANUAL_STRIP),
LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
@ -1682,13 +1711,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::PANICKING_UNWRAP),
LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&vec::USELESS_VEC), LintId::of(&vec::USELESS_VEC),
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
@ -1724,10 +1751,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::DOUBLE_MUST_USE),
LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::MUST_USE_UNIT),
LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&functions::RESULT_UNIT_ERR),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&inherent_to_string::INHERENT_TO_STRING),
LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::COMPARISON_TO_EMPTY),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
@ -1741,6 +1770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_map::MANUAL_MAP),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_clone::MAP_CLONE),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
@ -1805,7 +1835,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINTLN_EMPTY_STRING),
LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_LITERAL),
LintId::of(&write::PRINT_WITH_NEWLINE), LintId::of(&write::PRINT_WITH_NEWLINE),
@ -1899,7 +1928,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX), LintId::of(&types::VEC_BOX),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),

View file

@ -0,0 +1,274 @@
use crate::{
map_unit_fn::OPTION_MAP_UNIT_FN,
matches::MATCH_AS_REF,
utils::{
is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs,
peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg,
},
};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::{sym, Ident};
declare_clippy_lint! {
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
///
/// **Why is this bad?** Using the `map` method is clearer and more concise.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// match Some(0) {
/// Some(x) => Some(x + 1),
/// None => None,
/// };
/// ```
/// Use instead:
/// ```rust
/// Some(0).map(|x| x + 1);
/// ```
pub MANUAL_MAP,
style,
"reimplementation of `map`"
}
declare_lint_pass!(ManualMap => [MANUAL_MAP]);
impl LateLintPass<'_> for ManualMap {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) =
expr.kind
{
let (scrutinee_ty, ty_ref_count, ty_mutability) =
peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
|| !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)
{
return;
}
let (some_expr, some_pat, pat_ref_count, is_wild_none) =
match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) {
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count }))
if is_none_expr(cx, arm1.body) =>
{
(arm2.body, pattern, ref_count, true)
},
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count }))
if is_none_expr(cx, arm1.body) =>
{
(arm2.body, pattern, ref_count, false)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild))
if is_none_expr(cx, arm2.body) =>
{
(arm1.body, pattern, ref_count, true)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None))
if is_none_expr(cx, arm2.body) =>
{
(arm1.body, pattern, ref_count, false)
},
_ => return,
};
// Top level or patterns aren't allowed in closures.
if matches!(some_pat.kind, PatKind::Or(_)) {
return;
}
let some_expr = match get_some_expr(cx, some_expr) {
Some(expr) => expr,
None => return,
};
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
&& !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
{
return;
}
// Determine which binding mode to use.
let explicit_ref = some_pat.contains_explicit_ref_binding();
let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
let as_ref_str = match binding_ref {
Some(Mutability::Mut) => ".as_mut()",
Some(Mutability::Not) => ".as_ref()",
None => "",
};
let mut app = Applicability::MachineApplicable;
// Remove address-of expressions from the scrutinee. `as_ref` will be called,
// the type is copyable, or the option is being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0;
let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app);
let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX {
// Parens are needed to chain method calls.
format!("({})", scrutinee_str)
} else {
scrutinee_str.into()
};
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
} else {
if match_var(some_expr, some_binding.name)
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
return;
}
// `ref` and `ref mut` annotations were handled earlier.
let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
"mut "
} else {
""
};
format!(
"|{}{}| {}",
annotation,
some_binding,
snippet_with_applicability(cx, some_expr.span, "..", &mut app)
)
}
} else if !is_wild_none && explicit_ref.is_none() {
// TODO: handle explicit reference annotations.
format!(
"|{}| {}",
snippet_with_applicability(cx, some_pat.span, "..", &mut app),
snippet_with_applicability(cx, some_expr.span, "..", &mut app)
)
} else {
// Refutable bindings and mixed reference annotations can't be handled by `map`.
return;
};
span_lint_and_sugg(
cx,
MANUAL_MAP,
expr.span,
"manual implementation of `Option::map`",
"try this",
format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str),
app,
);
}
}
}
// Checks whether the expression could be passed as a function, or whether a closure is needed.
// Returns the function to be passed to `map` if it exists.
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
{
Some(func)
},
_ => None,
}
}
enum OptionPat<'a> {
Wild,
None,
Some {
// The pattern contained in the `Some` tuple.
pattern: &'a Pat<'a>,
// The number of references before the `Some` tuple.
// e.g. `&&Some(_)` has a ref count of 2.
ref_count: usize,
},
}
// Try to parse into a recognized `Option` pattern.
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option<OptionPat<'tcx>> {
fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option<OptionPat<'tcx>> {
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1),
PatKind::Path(QPath::Resolved(None, path))
if path
.res
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
{
Some(OptionPat::None)
},
PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
if path
.res
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) =>
{
Some(OptionPat::Some { pattern, ref_count })
},
_ => None,
}
}
f(cx, pat, 0)
}
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
// TODO: Allow more complex expressions.
match expr.kind {
ExprKind::Call(
Expr {
kind: ExprKind::Path(QPath::Resolved(None, path)),
..
},
[arg],
) => {
if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
Some(arg)
} else {
None
}
},
ExprKind::Block(
Block {
stmts: [],
expr: Some(expr),
..
},
_,
) => get_some_expr(cx, expr),
_ => None,
}
}
// Checks for the `None` value.
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
match expr.kind {
ExprKind::Path(QPath::Resolved(None, path)) => path
.res
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
ExprKind::Block(
Block {
stmts: [],
expr: Some(expr),
..
},
_,
) => is_none_expr(cx, expr),
_ => false,
}
}

View file

@ -3,19 +3,19 @@ use crate::utils::sugg::Sugg;
use crate::utils::visitors::LocalUsedVisitor; use crate::utils::visitors::LocalUsedVisitor;
use crate::utils::{ use crate::utils::{
expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local_id, is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local,
peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block,
snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
strip_pat_refs, span_lint_and_then, strip_pat_refs,
}; };
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::CtorKind; use rustc_hir::def::CtorKind;
use rustc_hir::{ use rustc_hir::{
Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat,
PatKind, QPath, RangeEnd, PatKind, QPath, RangeEnd,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -24,7 +24,7 @@ use rustc_middle::ty::{self, Ty, TyS};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned}; use rustc_span::source_map::{Span, Spanned};
use rustc_span::{sym, Symbol}; use rustc_span::sym;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::Bound; use std::collections::Bound;
@ -1873,13 +1873,6 @@ fn test_overlapping() {
/// Implementation of `MATCH_SAME_ARMS`. /// Implementation of `MATCH_SAME_ARMS`.
fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
lhs.len() == rhs.len()
&& lhs
.iter()
.all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
}
if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
let mut h = SpanlessHash::new(cx); let mut h = SpanlessHash::new(cx);
@ -1891,12 +1884,38 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
let min_index = usize::min(lindex, rindex); let min_index = usize::min(lindex, rindex);
let max_index = usize::max(lindex, rindex); let max_index = usize::max(lindex, rindex);
let mut local_map: FxHashMap<HirId, HirId> = FxHashMap::default();
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
if_chain! {
if let Some(a_id) = path_to_local(a);
if let Some(b_id) = path_to_local(b);
let entry = match local_map.entry(a_id) {
Entry::Vacant(entry) => entry,
// check if using the same bindings as before
Entry::Occupied(entry) => return *entry.get() == b_id,
};
// the names technically don't have to match; this makes the lint more conservative
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
if pat_contains_local(lhs.pat, a_id);
if pat_contains_local(rhs.pat, b_id);
then {
entry.insert(b_id);
true
} else {
false
}
}
};
// Arms with a guard are ignored, those cant always be merged together // Arms with a guard are ignored, those cant always be merged together
// This is also the case for arms in-between each there is an arm with a guard // This is also the case for arms in-between each there is an arm with a guard
(min_index..=max_index).all(|index| arms[index].guard.is_none()) && (min_index..=max_index).all(|index| arms[index].guard.is_none())
SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && && SpanlessEq::new(cx)
// all patterns should have the same bindings .expr_fallback(eq_fallback)
same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) .eq_expr(&lhs.body, &rhs.body)
// these checks could be removed to allow unused bindings
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
}; };
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
@ -1939,50 +1958,18 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
} }
} }
/// Returns the list of bindings in a pattern. fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> { let mut result = false;
fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) { pat.walk_short(|p| {
match pat.kind { result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), !result
PatKind::TupleStruct(_, pats, _) => { });
for pat in pats {
bindings_impl(cx, pat, map);
}
},
PatKind::Binding(.., ident, ref as_pat) => {
if let Entry::Vacant(v) = map.entry(ident.name) {
v.insert(cx.typeck_results().pat_ty(pat));
}
if let Some(ref as_pat) = *as_pat {
bindings_impl(cx, as_pat, map);
}
},
PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
for pat in fields {
bindings_impl(cx, pat, map);
}
},
PatKind::Struct(_, fields, _) => {
for pat in fields {
bindings_impl(cx, &pat.pat, map);
}
},
PatKind::Slice(lhs, ref mid, rhs) => {
for pat in lhs {
bindings_impl(cx, pat, map);
}
if let Some(ref mid) = *mid {
bindings_impl(cx, mid, map);
}
for pat in rhs {
bindings_impl(cx, pat, map);
}
},
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
}
}
let mut result = FxHashMap::default();
bindings_impl(cx, pat, &mut result);
result result
} }
/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet<HirId>) -> bool {
let mut result = true;
pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
result && ids.is_empty()
}

View file

@ -90,10 +90,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true; self.found = true;
return; return;
}, },
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => { ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj if adj

View file

@ -1,7 +1,6 @@
use crate::utils::{span_lint, span_lint_and_then}; use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{ use rustc_ast::ast::{
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind,
PatKind,
}; };
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};

View file

@ -212,10 +212,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
if !expr_borrows(cx, left_expr) { if !expr_borrows(cx, left_expr) {
return Some(LintTrigger::SortByKey(SortByKeyDetection { return Some(LintTrigger::SortByKey(SortByKeyDetection {
vec_name, vec_name,
unstable,
closure_arg, closure_arg,
closure_body, closure_body,
reverse reverse,
unstable,
})); }));
} }
} }

View file

@ -1,5 +1,5 @@
use crate::utils::{ use crate::utils::{
contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then,
visitors::find_all_ret_expressions, visitors::find_all_ret_expressions,
}; };
use if_chain::if_chain; use if_chain::if_chain;
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
@ -17,8 +17,8 @@ declare_clippy_lint! {
/// ///
/// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
/// ///
/// **Known problems:** Since this lint changes function type signature, you may need to /// **Known problems:** There can be false positives if the function signature is designed to
/// adjust some code at callee side. /// fit some external requirement.
/// ///
/// **Example:** /// **Example:**
/// ///
@ -48,7 +48,7 @@ declare_clippy_lint! {
/// } /// }
/// ``` /// ```
pub UNNECESSARY_WRAPS, pub UNNECESSARY_WRAPS,
complexity, pedantic,
"functions that only return `Ok` or `Some`" "functions that only return `Ok` or `Some`"
} }
@ -64,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
span: Span, span: Span,
hir_id: HirId, hir_id: HirId,
) { ) {
// Abort if public function/method or closure.
match fn_kind { match fn_kind {
FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => {
if visibility.node.is_pub() { if visibility.node.is_pub() {
@ -74,6 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
_ => (), _ => (),
} }
// Abort if the method is implementing a trait or of it a trait method.
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
if matches!( if matches!(
item.kind, item.kind,
@ -83,25 +85,44 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
} }
} }
let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { // Get the wrapper and inner types, if can't, abort.
("Option", &paths::OPTION_SOME) let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
} else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
("Result", &paths::RESULT_OK) ("Option", &paths::OPTION_SOME, subst.type_at(0))
} else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
("Result", &paths::RESULT_OK, subst.type_at(0))
} else {
return;
}
} else { } else {
return; return;
}; };
// Check if all return expression respect the following condition and collect them.
let mut suggs = Vec::new(); let mut suggs = Vec::new();
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
if_chain! { if_chain! {
if !in_macro(ret_expr.span); if !in_macro(ret_expr.span);
// Check if a function call.
if let ExprKind::Call(ref func, ref args) = ret_expr.kind; if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
// Get the Path of the function call.
if let ExprKind::Path(ref qpath) = func.kind; if let ExprKind::Path(ref qpath) = func.kind;
// Check if OPTION_SOME or RESULT_OK, depending on return type.
if match_qpath(qpath, path); if match_qpath(qpath, path);
if args.len() == 1; if args.len() == 1;
// Make sure the function argument does not contain a return expression.
if !contains_return(&args[0]); if !contains_return(&args[0]);
then { then {
suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); suggs.push(
(
ret_expr.span,
if inner_type.is_unit() {
"".to_string()
} else {
snippet(cx, args[0].span.source_callsite(), "..").to_string()
}
)
);
true true
} else { } else {
false false
@ -110,39 +131,34 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
}); });
if can_sugg && !suggs.is_empty() { if can_sugg && !suggs.is_empty() {
span_lint_and_then( let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() {
cx, (
UNNECESSARY_WRAPS, "this function's return value is unnecessary".to_string(),
span, "remove the return type...".to_string(),
format!( snippet(cx, fn_decl.output.span(), "..").to_string(),
"this function's return value is unnecessarily wrapped by `{}`", "...and then remove returned values",
return_type
) )
.as_str(), } else {
|diag| { (
let inner_ty = return_ty(cx, hir_id) format!(
.walk() "this function's return value is unnecessarily wrapped by `{}`",
.skip(1) // skip `std::option::Option` or `std::result::Result` return_type_label
.take(1) // take the first outermost inner type ),
.filter_map(|inner| match inner.unpack() { format!("remove `{}` from the return type...", return_type_label),
GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), inner_type.to_string(),
_ => None, "...and then change returning expressions",
}); )
inner_ty.for_each(|inner_ty| { };
diag.span_suggestion(
fn_decl.output.span(), span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
format!("remove `{}` from the return type...", return_type).as_str(), diag.span_suggestion(
inner_ty, fn_decl.output.span(),
Applicability::MaybeIncorrect, return_type_sugg_msg.as_str(),
); return_type_sugg,
}); Applicability::MaybeIncorrect,
diag.multipart_suggestion( );
"...and change the returning expressions", diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect);
suggs, });
Applicability::MaybeIncorrect,
);
},
);
} }
} }
} }

View file

@ -29,7 +29,7 @@ declare_clippy_lint! {
/// struct HttpResponse; /// struct HttpResponse;
/// ``` /// ```
pub UPPER_CASE_ACRONYMS, pub UPPER_CASE_ACRONYMS,
style, pedantic,
"capitalized acronyms are against the naming convention" "capitalized acronyms are against the naming convention"
} }

View file

@ -1,24 +1,24 @@
use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::DefKind;
use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor};
use rustc_hir::{ use rustc_hir::{
def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath, def,
TyKind, def_id::LocalDefId,
intravisit::{walk_ty, NestedVisitorMap, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
QPath, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{AssocKind, Ty, TyS};
use rustc_middle::ty;
use rustc_middle::ty::{DefIdTree, Ty};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::kw; use rustc_span::{BytePos, Span};
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;
use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for unnecessary repetition of structure name when a /// **What it does:** Checks for unnecessary repetition of structure name when a
/// replacement with `Self` is applicable. /// replacement with `Self` is applicable.
@ -28,10 +28,11 @@ declare_clippy_lint! {
/// feels inconsistent. /// feels inconsistent.
/// ///
/// **Known problems:** /// **Known problems:**
/// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843)) /// - Unaddressed false negative in fn bodies of trait implementations
/// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410)) /// - False positive with assotiated types in traits (#4140)
/// ///
/// **Example:** /// **Example:**
///
/// ```rust /// ```rust
/// struct Foo {} /// struct Foo {}
/// impl Foo { /// impl Foo {
@ -54,52 +55,326 @@ declare_clippy_lint! {
"unnecessary structure name repetition whereas `Self` is applicable" "unnecessary structure name repetition whereas `Self` is applicable"
} }
#[derive(Default)]
pub struct UseSelf {
msrv: Option<RustcVersion>,
stack: Vec<StackItem>,
}
const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
impl UseSelf {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self {
msrv,
..Self::default()
}
}
}
#[derive(Debug)]
enum StackItem {
Check {
hir_id: HirId,
impl_trait_ref_def_id: Option<LocalDefId>,
types_to_skip: Vec<HirId>,
types_to_lint: Vec<HirId>,
},
NoCheck,
}
impl_lint_pass!(UseSelf => [USE_SELF]); impl_lint_pass!(UseSelf => [USE_SELF]);
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
fn span_use_self_lint(cx: &LateContext<'_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) { impl<'tcx> LateLintPass<'tcx> for UseSelf {
let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG)); fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
// Path segments only include actual path, no methods or fields. // relevant for linting, since this is the self type of the `impl` we're currently in. To
let last_path_span = last_segment.ident.span; // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
// we're in an `impl` or nested item, that we don't want to lint
if differing_macro_contexts(path.span, last_path_span) { //
return; // NB: If you push something on the stack in this method, remember to also pop it in the
// `check_item_post` method.
match &item.kind {
ItemKind::Impl(Impl {
self_ty: hir_self_ty,
of_trait,
..
}) => {
let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind {
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
parameters.as_ref().map_or(true, |params| {
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
})
} else {
false
};
let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
if should_check {
self.stack.push(StackItem::Check {
hir_id: hir_self_ty.hir_id,
impl_trait_ref_def_id,
types_to_lint: Vec::new(),
types_to_skip: Vec::new(),
});
} else {
self.stack.push(StackItem::NoCheck);
}
},
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Fn(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..) => {
self.stack.push(StackItem::NoCheck);
},
_ => (),
}
} }
// Only take path up to the end of last_path_span. fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
let span = path.span.with_hi(last_path_span.hi()); use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
match item.kind {
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
self.stack.pop();
},
_ => (),
}
}
span_lint_and_sugg( fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
cx, // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
USE_SELF, // declaration. The collection of those types is all this method implementation does.
span, if_chain! {
"unnecessary structure name repetition", if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
"use the applicable keyword", if let Some(&mut StackItem::Check {
"Self".to_owned(), impl_trait_ref_def_id: Some(def_id),
Applicability::MachineApplicable, ref mut types_to_skip,
); ..
}) = self.stack.last_mut();
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`.
let self_ty = impl_trait_ref.self_ty();
// `trait_method_sig` is the signature of the function, how it is declared in the
// trait, not in the impl of the trait.
let trait_method = cx
.tcx
.associated_items(impl_trait_ref.def_id)
.find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
.expect("impl method matches a trait method");
let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
// `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
// implementation of the trait.
let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
Some(&**ty)
} else {
None
};
let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
// `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
//
// `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
// trait declaration. This is used to check if `Self` was used in the trait
// declaration.
//
// If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
// to `Self`), we want to skip linting that type and all subtypes of it. This
// avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
// for u8`, when the trait always uses `Vec<u8>`.
//
// See also https://github.com/rust-lang/rust-clippy/issues/2894.
for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
let mut visitor = SkipTyCollector::default();
visitor.visit_ty(&impl_hir_ty);
types_to_skip.extend(visitor.types_to_skip);
}
}
}
}
}
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
// `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
// we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
// However the `node_type()` method can *only* be called in bodies.
//
// This method implementation determines which types should get linted in a `Body` and
// which shouldn't, with a visitor. We could directly lint in the visitor, but then we
// could only allow this lint on item scope. And we would have to check if those types are
// already dealt with in `check_ty` anyway.
if let Some(StackItem::Check {
hir_id,
types_to_lint,
types_to_skip,
..
}) = self.stack.last_mut()
{
let self_ty = ty_from_hir_id(cx, *hir_id);
let mut visitor = LintTyCollector {
cx,
self_ty,
types_to_lint: vec![],
types_to_skip: vec![],
};
visitor.visit_expr(&body.value);
types_to_lint.extend(visitor.types_to_lint);
types_to_skip.extend(visitor.types_to_skip);
}
}
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
return;
}
let lint_dependend_on_expr_kind = if let Some(StackItem::Check {
hir_id,
types_to_lint,
types_to_skip,
..
}) = self.stack.last()
{
if types_to_skip.contains(&hir_ty.hir_id) {
false
} else if types_to_lint.contains(&hir_ty.hir_id) {
true
} else {
let self_ty = ty_from_hir_id(cx, *hir_id);
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
}
} else {
false
};
if lint_dependend_on_expr_kind {
// FIXME: this span manipulation should not be necessary
// @flip1995 found an ast lowering issue in
// https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162
match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_ty.hir_id)) {
Some(Node::Expr(Expr {
kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
..
})) => span_lint_until_last_segment(cx, hir_ty.span, segment),
_ => span_lint(cx, hir_ty.span),
}
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool {
let def_id = expr.hir_id.owner;
if cx.tcx.has_typeck_results(def_id) {
cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty)
} else {
false
}
}
if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
return;
}
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() {
let self_ty = ty_from_hir_id(cx, *hir_id);
match &expr.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
if expr_ty_matches(cx, expr, self_ty) {
match path.res {
def::Res::SelfTy(..) => (),
def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
_ => {
span_lint(cx, path.span);
},
}
}
},
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
ExprKind::Call(fun, _) => {
if let Expr {
kind: ExprKind::Path(ref qpath),
..
} = fun
{
if expr_ty_matches(cx, expr, self_ty) {
let res = cx.qpath_res(qpath, fun.hir_id);
if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
match ctor_of {
def::CtorOf::Variant => {
span_lint_on_qpath_resolved(cx, qpath, true);
},
def::CtorOf::Struct => {
span_lint_on_qpath_resolved(cx, qpath, false);
},
}
}
}
}
},
// unit enum variants (`Enum::A`)
ExprKind::Path(qpath) => {
if expr_ty_matches(cx, expr, self_ty) {
span_lint_on_qpath_resolved(cx, &qpath, true);
}
},
_ => (),
}
}
}
extract_msrv_attr!(LateContext);
} }
// FIXME: always use this (more correct) visitor, not just in method signatures. #[derive(Default)]
struct SemanticUseSelfVisitor<'a, 'tcx> { struct SkipTyCollector {
types_to_skip: Vec<HirId>,
}
impl<'tcx> Visitor<'tcx> for SkipTyCollector {
type Map = Map<'tcx>;
fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
self.types_to_skip.push(hir_ty.hir_id);
walk_ty(self, hir_ty)
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
struct LintTyCollector<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
types_to_lint: Vec<HirId>,
types_to_skip: Vec<HirId>,
} }
impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
type Map = Map<'tcx>; type Map = Map<'tcx>;
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) { fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind { if_chain! {
match path.res { if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
def::Res::SelfTy(..) => {}, if should_lint_ty(hir_ty, ty, self.self_ty);
_ => { then {
if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { self.types_to_lint.push(hir_ty.hir_id);
span_use_self_lint(self.cx, path, None); } else {
} self.types_to_skip.push(hir_ty.hir_id);
},
} }
} }
@ -111,177 +386,78 @@ impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
} }
} }
fn check_trait_method_impl_decl<'tcx>( fn span_lint(cx: &LateContext<'_>, span: Span) {
cx: &LateContext<'tcx>, span_lint_and_sugg(
impl_item: &ImplItem<'_>, cx,
impl_decl: &'tcx FnDecl<'_>, USE_SELF,
impl_trait_ref: ty::TraitRef<'tcx>, span,
) { "unnecessary structure name repetition",
let trait_method = cx "use the applicable keyword",
.tcx "Self".to_owned(),
.associated_items(impl_trait_ref.def_id) Applicability::MachineApplicable,
.find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id) );
.expect("impl method matches a trait method"); }
let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); #[allow(clippy::cast_possible_truncation)]
let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
let sp = span.with_hi(segment.ident.span.lo());
let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output { // remove the trailing ::
Some(&**ty) let span_without_last_segment = match snippet_opt(cx, sp) {
} else { Some(snippet) => match snippet.rfind("::") {
None Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
None => sp,
},
None => sp,
}; };
span_lint(cx, span_without_last_segment);
}
// `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
// `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait. if path.segments.len() > 1 {
// We use `impl_hir_ty` to see if the type was written as `Self`, span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
// `hir_ty_to_ty(...)` to check semantic types of paths, and }
// `trait_ty` to determine which parts of the signature in the trait, mention }
// the type being implemented verbatim (as opposed to `Self`).
for (impl_hir_ty, trait_ty) in impl_decl
.inputs
.iter()
.chain(output_hir_ty)
.zip(trait_method_sig.inputs_and_output)
{
// Check if the input/output type in the trait method specifies the implemented
// type verbatim, and only suggest `Self` if that isn't the case.
// This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
// in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
// See also https://github.com/rust-lang/rust-clippy/issues/2894.
let self_ty = impl_trait_ref.self_ty();
if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
visitor.visit_ty(&impl_hir_ty); fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) {
if let QPath::Resolved(_, path) = qpath {
if until_last_segment {
span_lint_on_path_until_last_segment(cx, path);
} else {
span_lint(cx, path.span);
} }
} }
} }
const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
pub struct UseSelf { hir_ty_to_ty(cx.tcx, hir_ty)
msrv: Option<RustcVersion>, } else {
} unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
impl UseSelf {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
} }
} }
impl<'tcx> LateLintPass<'tcx> for UseSelf { fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let map = cx.tcx.hir();
if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { let parent = map.get_parent_node(hir_ty.hir_id);
return; if_chain! {
} if let Some(Node::Item(item)) = map.find(parent);
if let ItemKind::Impl { .. } = item.kind;
if in_external_macro(cx.sess(), item.span) { then {
return; true
} } else {
if_chain! { false
if let ItemKind::Impl(impl_) = &item.kind;
if let TyKind::Path(QPath::Resolved(_, ref item_path)) = impl_.self_ty.kind;
then {
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
let should_check = parameters.as_ref().map_or(
true,
|params| !params.parenthesized
&&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
);
if should_check {
let visitor = &mut UseSelfVisitor {
item_path,
cx,
};
let impl_trait_ref = cx.tcx.impl_trait_ref(item.def_id);
if let Some(impl_trait_ref) = impl_trait_ref {
for impl_item_ref in impl_.items {
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id)
= &impl_item.kind {
check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref);
let body = cx.tcx.hir().body(*impl_body_id);
visitor.visit_body(body);
} else {
visitor.visit_impl_item(impl_item);
}
}
} else {
for impl_item_ref in impl_.items {
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
visitor.visit_impl_item(impl_item);
}
}
}
}
} }
} }
extract_msrv_attr!(LateContext);
} }
struct UseSelfVisitor<'a, 'tcx> { fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
item_path: &'a Path<'a>, if_chain! {
cx: &'a LateContext<'tcx>, if TyS::same_type(ty, self_ty);
} if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
then {
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> { !matches!(path.res, def::Res::SelfTy(..))
type Map = Map<'tcx>; } else {
false
fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
if !path.segments.iter().any(|p| p.ident.span.is_dummy()) {
if path.segments.len() >= 2 {
let last_but_one = &path.segments[path.segments.len() - 2];
if last_but_one.ident.name != kw::SelfUpper {
let enum_def_id = match path.res {
Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
let variant_def_id = self.cx.tcx.parent(ctor_def_id);
variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
},
_ => None,
};
if self.item_path.res.opt_def_id() == enum_def_id {
span_use_self_lint(self.cx, path, Some(last_but_one));
}
}
}
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
if self.item_path.res == path.res {
span_use_self_lint(self.cx, path, None);
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
span_use_self_lint(self.cx, path, None);
}
}
}
}
walk_path(self, path);
}
fn visit_item(&mut self, item: &'tcx Item<'_>) {
match item.kind {
ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Impl { .. }
| ItemKind::Fn(..) => {
// Don't check statements that shadow `Self` or where `Self` can't be used
},
_ => walk_item(self, item),
} }
} }
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::All(self.cx.tcx.hir())
}
} }

View file

@ -126,7 +126,7 @@ define_Conf! {
"NaN", "NaNs", "NaN", "NaNs",
"OAuth", "GraphQL", "OAuth", "GraphQL",
"OCaml", "OCaml",
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
"WebGL", "WebGL",
"TensorFlow", "TensorFlow",
"TrueType", "TrueType",

View file

@ -4,7 +4,7 @@ use crate::utils::{
span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
use rustc_ast::visit::FnKind; use rustc_ast::visit::FnKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -301,17 +301,12 @@ declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal { impl EarlyLintPass for ClippyLintsInternal {
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
if let Some(utils) = krate if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
.module if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
.items if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
.iter() if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
.find(|item| item.ident.name.as_str() == "utils")
{
if let ItemKind::Mod(ref utils_mod) = utils.kind {
if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
if let ItemKind::Mod(ref paths_mod) = paths.kind {
let mut last_name: Option<SymbolStr> = None; let mut last_name: Option<SymbolStr> = None;
for item in &*paths_mod.items { for item in items {
let name = item.ident.as_str(); let name = item.ident.as_str();
if let Some(ref last_name) = last_name { if let Some(ref last_name) = last_name {
if **last_name > *name { if **last_name > *name {
@ -343,7 +338,7 @@ impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) { if !run_lints(cx, &[DEFAULT_LINT], item.hir_id()) {
return; return;
} }
@ -393,7 +388,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
.find(|iiref| iiref.ident.as_str() == "get_lints") .find(|iiref| iiref.ident.as_str() == "get_lints")
.expect("LintPass needs to implement get_lints") .expect("LintPass needs to implement get_lints")
.id .id
.hir_id, .hir_id(),
); );
collector.visit_expr(&cx.tcx.hir().body(body_id).value); collector.visit_expr(&cx.tcx.hir().body(body_id).value);
} }
@ -861,7 +856,7 @@ declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
impl<'tcx> LateLintPass<'tcx> for InvalidPaths { impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let local_def_id = &cx.tcx.parent_module(item.hir_id); let local_def_id = &cx.tcx.parent_module(item.hir_id());
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
if_chain! { if_chain! {
if mod_name.as_str() == "paths"; if mod_name.as_str() == "paths";

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,14 @@
use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg}; use crate::utils::{
is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg,
};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol}; use rustc_span::{symbol::sym, Span};
use std::convert::TryInto; use std::convert::TryInto;
declare_clippy_lint! { declare_clippy_lint! {
@ -45,8 +47,8 @@ enum VecInitKind {
WithCapacity(u64), WithCapacity(u64),
} }
struct VecPushSearcher { struct VecPushSearcher {
local_id: HirId,
init: VecInitKind, init: VecInitKind,
name: Symbol,
lhs_is_local: bool, lhs_is_local: bool,
lhs_span: Span, lhs_span: Span,
err_span: Span, err_span: Span,
@ -81,17 +83,20 @@ impl VecPushSearcher {
} }
impl LateLintPass<'_> for VecInitThenPush { impl LateLintPass<'_> for VecInitThenPush {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
self.searcher = None; self.searcher = None;
}
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
if_chain! { if_chain! {
if !in_external_macro(cx.sess(), local.span); if !in_external_macro(cx.sess(), local.span);
if let Some(init) = local.init; if let Some(init) = local.init;
if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind; if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind;
if let Some(init_kind) = get_vec_init_kind(cx, init); if let Some(init_kind) = get_vec_init_kind(cx, init);
then { then {
self.searcher = Some(VecPushSearcher { self.searcher = Some(VecPushSearcher {
local_id: id,
init: init_kind, init: init_kind,
name: ident.name,
lhs_is_local: true, lhs_is_local: true,
lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
err_span: local.span, err_span: local.span,
@ -106,13 +111,12 @@ impl LateLintPass<'_> for VecInitThenPush {
if_chain! { if_chain! {
if !in_external_macro(cx.sess(), expr.span); if !in_external_macro(cx.sess(), expr.span);
if let ExprKind::Assign(left, right, _) = expr.kind; if let ExprKind::Assign(left, right, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; if let Some(id) = path_to_local(left);
if let Some(name) = path.segments.get(0);
if let Some(init_kind) = get_vec_init_kind(cx, right); if let Some(init_kind) = get_vec_init_kind(cx, right);
then { then {
self.searcher = Some(VecPushSearcher { self.searcher = Some(VecPushSearcher {
local_id: id,
init: init_kind, init: init_kind,
name: name.ident.name,
lhs_is_local: false, lhs_is_local: false,
lhs_span: left.span, lhs_span: left.span,
err_span: expr.span, err_span: expr.span,
@ -128,10 +132,8 @@ impl LateLintPass<'_> for VecInitThenPush {
if_chain! { if_chain! {
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind; if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
if path_to_local_id(self_arg, searcher.local_id);
if path.ident.name.as_str() == "push"; if path.ident.name.as_str() == "push";
if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind;
if let [self_name] = self_path.segments;
if self_name.ident.name == searcher.name;
then { then {
self.searcher = Some(VecPushSearcher { self.searcher = Some(VecPushSearcher {
found: searcher.found + 1, found: searcher.found + 1,

View file

@ -3,9 +3,7 @@ use std::ops::Range;
use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{ use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle};
Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle,
};
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -149,7 +147,6 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
///
/// // Bad /// // Bad
/// writeln!(buf, ""); /// writeln!(buf, "");
/// ///
@ -176,7 +173,6 @@ declare_clippy_lint! {
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
/// # let name = "World"; /// # let name = "World";
///
/// // Bad /// // Bad
/// write!(buf, "Hello {}!\n", name); /// write!(buf, "Hello {}!\n", name);
/// ///
@ -202,7 +198,6 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
///
/// // Bad /// // Bad
/// writeln!(buf, "{}", "foo"); /// writeln!(buf, "{}", "foo");
/// ///
@ -379,15 +374,10 @@ impl Write {
/// (Some("string to write: {}"), Some(buf)) /// (Some("string to write: {}"), Some(buf))
/// ``` /// ```
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn check_tts<'a>( fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
&self,
cx: &EarlyContext<'a>,
tts: TokenStream,
is_write: bool,
) -> (Option<StrLit>, Option<Expr>) {
use rustc_parse_format::{ use rustc_parse_format::{
AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser,
FormatSpec, ParseMode, Parser, Piece, Piece,
}; };
let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
@ -417,12 +407,7 @@ impl Write {
if let Piece::NextArgument(arg) = piece { if let Piece::NextArgument(arg) = piece {
if !self.in_debug_impl && arg.format.ty == "?" { if !self.in_debug_impl && arg.format.ty == "?" {
// FIXME: modify rustc's fmt string parser to give us the current span // FIXME: modify rustc's fmt string parser to give us the current span
span_lint( span_lint(cx, USE_DEBUG, parser.prev_token.span, "use of `Debug`-based formatting");
cx,
USE_DEBUG,
parser.prev_token.span,
"use of `Debug`-based formatting",
);
} }
args.push(arg); args.push(arg);
} }
@ -450,9 +435,7 @@ impl Write {
return (Some(fmtstr), None); return (Some(fmtstr), None);
}; };
match &token_expr.kind { match &token_expr.kind {
ExprKind::Lit(lit) ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => {
if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) =>
{
let mut all_simple = true; let mut all_simple = true;
let mut seen = false; let mut seen = false;
for arg in &args { for arg in &args {
@ -462,15 +445,15 @@ impl Write {
all_simple &= arg.format == SIMPLE; all_simple &= arg.format == SIMPLE;
seen = true; seen = true;
} }
} },
ArgumentNamed(_) => {} ArgumentNamed(_) => {},
} }
} }
if all_simple && seen { if all_simple && seen {
span_lint(cx, lint, token_expr.span, "literal with an empty format string"); span_lint(cx, lint, token_expr.span, "literal with an empty format string");
} }
idx += 1; idx += 1;
} },
ExprKind::Assign(lhs, rhs, _) => { ExprKind::Assign(lhs, rhs, _) => {
if_chain! { if_chain! {
if let ExprKind::Lit(ref lit) = rhs.kind; if let ExprKind::Lit(ref lit) = rhs.kind;
@ -495,7 +478,7 @@ impl Write {
} }
} }
} }
} },
_ => idx += 1, _ => idx += 1,
} }
} }
@ -527,17 +510,11 @@ impl Write {
cx, cx,
PRINT_WITH_NEWLINE, PRINT_WITH_NEWLINE,
mac.span(), mac.span(),
&format!( &format!("using `{}!()` with a format string that ends in a single newline", name),
"using `{}!()` with a format string that ends in a single newline",
name
),
|err| { |err| {
err.multipart_suggestion( err.multipart_suggestion(
&format!("use `{}!` instead", suggested), &format!("use `{}!` instead", suggested),
vec![ vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
(mac.path.span, suggested),
(newline_span(&fmt_str), String::new()),
],
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
}, },

19
clippy_utils/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "clippy_utils"
version = "0.1.52"
authors = ["The Rust Clippy Developers"]
edition = "2018"
publish = false
[dependencies]
if_chain = "1.0.0"
itertools = "0.9"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }
smallvec = { version = "1", features = ["union"] }
toml = "0.5.3"
unicode-normalization = "0.1"
rustc-semver="1.1.0"
[features]
internal-lints = []

View file

@ -4,7 +4,7 @@
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
use crate::utils::{both, over}; use crate::{both, over};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::{self as ast, *}; use rustc_ast::{self as ast, *};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
@ -229,23 +229,20 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
match (l, r) { match (l, r) {
(ExternCrate(l), ExternCrate(r)) => l == r, (ExternCrate(l), ExternCrate(r)) => l == r,
(Use(l), Use(r)) => eq_use_tree(l, r), (Use(l), Use(r)) => eq_use_tree(l, r),
(Static(lt, lm, le), Static(rt, rm, re)) => { (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re) (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
}
(Const(ld, lt, le), Const(rd, rt, re)) => {
eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re)
}
(Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => { (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
&& eq_fn_sig(lf, rf) },
&& eq_generics(lg, rg) (Mod(lu, lmk), Mod(ru, rmk)) => {
&& both(lb, rb, |l, r| eq_block(l, r)) lu == ru
} && match (lmk, rmk) {
(Mod(lu, lmk), Mod(ru, rmk)) => lu == ru && match (lmk, rmk) { (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
(ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)), },
(ModKind::Unloaded, ModKind::Unloaded) => true, (ModKind::Unloaded, ModKind::Unloaded) => true,
_ => false, _ => false,
}
}, },
(ForeignMod(l), ForeignMod(r)) => { (ForeignMod(l), ForeignMod(r)) => {
both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r)) both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
@ -311,15 +308,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
use ForeignItemKind::*; use ForeignItemKind::*;
match (l, r) { match (l, r) {
(Static(lt, lm, le), Static(rt, rm, re)) => { (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re)
}
(Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => { (Fn(box FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, rb))) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
&& eq_fn_sig(lf, rf) },
&& eq_generics(lg, rg)
&& both(lb, rb, |l, r| eq_block(l, r))
}
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => { (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg) && eq_generics(lg, rg)

View file

@ -55,6 +55,8 @@ pub fn from(s: &str) -> usize {
} }
} else if c.is_lowercase() { } else if c.is_lowercase() {
down = true; down = true;
} else if c.is_uppercase() {
last_i = i;
} else { } else {
return last_i; return last_i;
} }
@ -70,12 +72,16 @@ mod test {
fn from_full() { fn from_full() {
assert_eq!(from("AbcDef"), 0); assert_eq!(from("AbcDef"), 0);
assert_eq!(from("Abc"), 0); assert_eq!(from("Abc"), 0);
assert_eq!(from("ABcd"), 0);
assert_eq!(from("ABcdEf"), 0);
assert_eq!(from("AabABcd"), 0);
} }
#[test] #[test]
fn from_partial() { fn from_partial() {
assert_eq!(from("abcDef"), 3); assert_eq!(from("abcDef"), 3);
assert_eq!(from("aDbc"), 1); assert_eq!(from("aDbc"), 1);
assert_eq!(from("aabABcd"), 3);
} }
#[test] #[test]

574
clippy_utils/src/consts.rs Normal file
View file

@ -0,0 +1,574 @@
#![allow(clippy::float_cmp)]
use crate::{clip, sext, unsext};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Symbol;
use std::cmp::Ordering::{self, Equal};
use std::convert::TryInto;
use std::hash::{Hash, Hasher};
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
pub enum Constant {
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
Binary(Lrc<[u8]>),
/// A single `char` (e.g., `'a'`).
Char(char),
/// An integer's bit representation.
Int(u128),
/// An `f32`.
F32(f32),
/// An `f64`.
F64(f64),
/// `true` or `false`.
Bool(bool),
/// An array of constants.
Vec(Vec<Constant>),
/// Also an array, but with only one constant, repeated N times.
Repeat(Box<Constant>, u64),
/// A tuple of constants.
Tuple(Vec<Constant>),
/// A raw pointer.
RawPtr(u128),
/// A reference
Ref(Box<Constant>),
/// A literal with syntax error.
Err(Symbol),
}
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
(&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
(&Self::Char(l), &Self::Char(r)) => l == r,
(&Self::Int(l), &Self::Int(r)) => l == r,
(&Self::F64(l), &Self::F64(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
l.to_bits() == r.to_bits()
},
(&Self::F32(l), &Self::F32(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
f64::from(l).to_bits() == f64::from(r).to_bits()
},
(&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
(&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
// TODO: are there inter-type equalities?
_ => false,
}
}
}
impl Hash for Constant {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
std::mem::discriminant(self).hash(state);
match *self {
Self::Str(ref s) => {
s.hash(state);
},
Self::Binary(ref b) => {
b.hash(state);
},
Self::Char(c) => {
c.hash(state);
},
Self::Int(i) => {
i.hash(state);
},
Self::F32(f) => {
f64::from(f).to_bits().hash(state);
},
Self::F64(f) => {
f.to_bits().hash(state);
},
Self::Bool(b) => {
b.hash(state);
},
Self::Vec(ref v) | Self::Tuple(ref v) => {
v.hash(state);
},
Self::Repeat(ref c, l) => {
c.hash(state);
l.hash(state);
},
Self::RawPtr(u) => {
u.hash(state);
},
Self::Ref(ref r) => {
r.hash(state);
},
Self::Err(ref s) => {
s.hash(state);
},
}
}
}
impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
(&Self::Int(l), &Self::Int(r)) => {
if let ty::Int(int_ty) = *cmp_type.kind() {
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
} else {
Some(l.cmp(&r))
}
},
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
(&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
(&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
.iter()
.zip(r.iter())
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
.find(|r| r.map_or(true, |o| o != Ordering::Equal))
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
match Self::partial_cmp(tcx, cmp_type, lv, rv) {
Some(Equal) => Some(ls.cmp(rs)),
x => x,
}
},
(&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
// TODO: are there any useful inter-type orderings?
_ => None,
}
}
}
/// Parses a `LitKind` to a `Constant`.
pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, _) => Constant::Int(n),
LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
},
LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
_ => bug!(),
},
LitKind::Bool(b) => Constant::Bool(b),
LitKind::Err(s) => Constant::Err(s),
}
}
pub fn constant<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext {
lcx,
typeck_results,
param_env: lcx.param_env,
needed_resolution: false,
substs: lcx.tcx.intern_substs(&[]),
};
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
}
pub fn constant_simple<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
) -> Option<Constant> {
constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
}
/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
pub fn constant_context<'a, 'tcx>(
lcx: &'a LateContext<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> ConstEvalLateContext<'a, 'tcx> {
ConstEvalLateContext {
lcx,
typeck_results,
param_env: lcx.param_env,
needed_resolution: false,
substs: lcx.tcx.intern_substs(&[]),
}
}
pub struct ConstEvalLateContext<'a, 'tcx> {
lcx: &'a LateContext<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>,
needed_resolution: bool,
substs: SubstsRef<'tcx>,
}
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Simple constant folding: Insert an expression, get a constant or none.
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
match e.kind {
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(ref block, _) => self.block(block),
ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(ref value, _) => {
let n = match self.typeck_results.expr_ty(e).kind() {
ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
_ => span_bug!(e.span, "typeck error"),
};
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
},
ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
}),
ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
ExprKind::Call(ref callee, ref args) => {
// We only handle a few const functions for now.
if_chain! {
if args.is_empty();
if let ExprKind::Path(qpath) = &callee.kind;
let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
if let Some(def_id) = res.opt_def_id();
let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
if let ["core", "num", int_impl, "max_value"] = *def_path;
then {
let value = match int_impl {
"<impl i8>" => i8::MAX as u128,
"<impl i16>" => i16::MAX as u128,
"<impl i32>" => i32::MAX as u128,
"<impl i64>" => i64::MAX as u128,
"<impl i128>" => i128::MAX as u128,
_ => return None,
};
Some(Constant::Int(value))
}
else {
None
}
}
},
ExprKind::Index(ref arr, ref index) => self.index(arr, index),
ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
// TODO: add other expressions.
_ => None,
}
}
#[allow(clippy::cast_possible_wrap)]
fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{Bool, Int};
match *o {
Bool(b) => Some(Bool(!b)),
Int(value) => {
let value = !value;
match *ty.kind() {
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
_ => None,
}
},
_ => None,
}
}
fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
let ity = match *ty.kind() {
ty::Int(ity) => ity,
_ => return None,
};
// sign extend
let value = sext(self.lcx.tcx, value, ity);
let value = value.checked_neg()?;
// clear unused bits
Some(Int(unsext(self.lcx.tcx, value, ity)))
},
F32(f) => Some(F32(-f)),
F64(f) => Some(F64(-f)),
_ => None,
}
}
/// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part.
fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
}
/// Lookup a possibly constant expression from a `ExprKind::Path`.
fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
let res = self.typeck_results.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
let substs = self.typeck_results.node_substs(id);
let substs = if self.substs.is_empty() {
substs
} else {
substs.subst(self.lcx.tcx, self.substs)
};
let result = self
.lcx
.tcx
.const_eval_resolve(
self.param_env,
ty::WithOptConstParam::unknown(def_id),
substs,
None,
None,
)
.ok()
.map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
let result = miri_to_const(&result);
if result.is_some() {
self.needed_resolution = true;
}
result
},
// FIXME: cover all usable cases.
_ => None,
}
}
fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
let lhs = self.expr(lhs);
let index = self.expr(index);
match (lhs, index) {
(Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
Some(Constant::F32(x)) => Some(Constant::F32(*x)),
Some(Constant::F64(x)) => Some(Constant::F64(*x)),
_ => None,
},
(Some(Constant::Vec(vec)), _) => {
if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
match vec.get(0) {
Some(Constant::F32(x)) => Some(Constant::F32(*x)),
Some(Constant::F64(x)) => Some(Constant::F64(*x)),
_ => None,
}
} else {
None
}
},
_ => None,
}
}
/// A block can only yield a constant if it only has one constant expression.
fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
if block.stmts.is_empty() {
block.expr.as_ref().and_then(|b| self.expr(b))
} else {
None
}
}
fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
if let Some(Constant::Bool(b)) = self.expr(cond) {
if b {
self.expr(&*then)
} else {
otherwise.as_ref().and_then(|expr| self.expr(expr))
}
} else {
None
}
}
fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
(Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
ty::Int(ity) => {
let l = sext(self.lcx.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity);
let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
match op.node {
BinOpKind::Add => l.checked_add(r).map(zext),
BinOpKind::Sub => l.checked_sub(r).map(zext),
BinOpKind::Mul => l.checked_mul(r).map(zext),
BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
BinOpKind::BitXor => Some(zext(l ^ r)),
BinOpKind::BitOr => Some(zext(l | r)),
BinOpKind::BitAnd => Some(zext(l & r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
}
},
ty::Uint(_) => match op.node {
BinOpKind::Add => l.checked_add(r).map(Constant::Int),
BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
BinOpKind::Div => l.checked_div(r).map(Constant::Int),
BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
BinOpKind::BitOr => Some(Constant::Int(l | r)),
BinOpKind::BitAnd => Some(Constant::Int(l & r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
_ => None,
},
(Constant::F32(l), Some(Constant::F32(r))) => match op.node {
BinOpKind::Add => Some(Constant::F32(l + r)),
BinOpKind::Sub => Some(Constant::F32(l - r)),
BinOpKind::Mul => Some(Constant::F32(l * r)),
BinOpKind::Div => Some(Constant::F32(l / r)),
BinOpKind::Rem => Some(Constant::F32(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F64(l), Some(Constant::F64(r))) => match op.node {
BinOpKind::Add => Some(Constant::F64(l + r)),
BinOpKind::Sub => Some(Constant::F64(l - r)),
BinOpKind::Mul => Some(Constant::F64(l * r)),
BinOpKind::Div => Some(Constant::F64(l / r)),
BinOpKind::Rem => Some(Constant::F64(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(l, r) => match (op.node, l, r) {
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
(BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
Some(r)
},
(BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
(BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
(BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
_ => None,
},
}
}
}
pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
use rustc_middle::mir::interpret::ConstValue;
match result.val {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
match result.ty.kind() {
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
int.try_into().expect("invalid f32 bit representation"),
))),
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
int.try_into().expect("invalid f64 bit representation"),
))),
ty::RawPtr(type_and_mut) => {
if let ty::Uint(_) = type_and_mut.ty.kind() {
return Some(Constant::RawPtr(int.assert_bits(int.size())));
}
None
},
// FIXME: implement other conversions.
_ => None,
}
},
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8(
data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
.to_owned(),
)
.ok()
.map(Constant::Str),
_ => None,
},
_ => None,
},
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match miri_to_const(len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
.to_owned()
.chunks(4)
.map(|chunk| {
Some(Constant::F32(f32::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match miri_to_const(len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
.to_owned()
.chunks(8)
.map(|chunk| {
Some(Constant::F64(f64::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,
},
// FIXME: implement other array type conversions.
_ => None,
},
_ => None,
},
// FIXME: implement other conversions.
_ => None,
}
}

View file

@ -9,7 +9,7 @@
//! - or-fun-call //! - or-fun-call
//! - option-if-let-else //! - option-if-let-else
use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths};
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit; use rustc_hir::intravisit;

View file

@ -3,7 +3,7 @@
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
use crate::utils::{is_expn_of, match_def_path, paths}; use crate::{is_expn_of, match_def_path, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_hir as hir; use rustc_hir as hir;

View file

@ -1,10 +1,12 @@
use crate::consts::{constant_context, constant_simple}; use crate::consts::{constant_context, constant_simple};
use crate::utils::differing_macro_contexts; use crate::differing_macro_contexts;
use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def::Res;
use rustc_hir::{ use rustc_hir::{
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy,
GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
}; };
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -24,7 +26,7 @@ pub struct SpanlessEq<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
allow_side_effects: bool, allow_side_effects: bool,
expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>, expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
} }
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@ -45,15 +47,54 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
} }
} }
pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
Self { Self {
expr_fallback: Some(Box::new(expr_fallback)), expr_fallback: Some(Box::new(expr_fallback)),
..self ..self
} }
} }
/// Checks whether two statements are the same. /// Use this method to wrap comparisons that may involve inter-expression context.
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { /// See `self.locals`.
fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
HirEqInterExpr {
inner: self,
locals: FxHashMap::default(),
}
}
pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
self.inter_expr().eq_block(left, right)
}
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
self.inter_expr().eq_expr(left, right)
}
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
self.inter_expr().eq_path_segment(left, right)
}
pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
self.inter_expr().eq_path_segments(left, right)
}
pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
self.inter_expr().eq_ty_kind(left, right)
}
}
struct HirEqInterExpr<'a, 'b, 'tcx> {
inner: &'a mut SpanlessEq<'b, 'tcx>,
// When binding are declared, the binding ID in the left expression is mapped to the one on the
// right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
// these blocks are considered equal since `x` is mapped to `y`.
locals: FxHashMap<HirId, HirId>,
}
impl HirEqInterExpr<'_, '_, '_> {
fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
match (&left.kind, &right.kind) { match (&left.kind, &right.kind) {
(&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
self.eq_pat(&l.pat, &r.pat) self.eq_pat(&l.pat, &r.pat)
@ -68,21 +109,21 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
} }
/// Checks whether two blocks are the same. /// Checks whether two blocks are the same.
pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
&& both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
} }
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
return false; return false;
} }
if let Some(typeck_results) = self.maybe_typeck_results { if let Some(typeck_results) = self.inner.maybe_typeck_results {
if let (Some(l), Some(r)) = ( if let (Some(l), Some(r)) = (
constant_simple(self.cx, typeck_results, left), constant_simple(self.inner.cx, typeck_results, left),
constant_simple(self.cx, typeck_results, right), constant_simple(self.inner.cx, typeck_results, right),
) { ) {
if l == r { if l == r {
return true; return true;
@ -98,10 +139,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
}, },
(&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
}, },
(&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
}, },
(&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
(&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
@ -116,7 +157,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
}, },
(&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
(&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
}, },
(&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
| (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
@ -139,19 +180,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
ls == rs ls == rs
&& self.eq_expr(le, re) && self.eq_expr(le, re)
&& over(la, ra, |l, r| { && over(la, ra, |l, r| {
self.eq_expr(&l.body, &r.body) self.eq_pat(&l.pat, &r.pat)
&& both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
&& self.eq_pat(&l.pat, &r.pat) && self.eq_expr(&l.body, &r.body)
}) })
}, },
(&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
}, },
(&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body));
let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value);
let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body)); let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body));
let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); let rl = celcx.expr(&self.inner.cx.tcx.hir().body(rl_id.body).value);
self.eq_expr(le, re) && ll == rl self.eq_expr(le, re) && ll == rl
}, },
@ -168,7 +209,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
(&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
_ => false, _ => false,
}; };
is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right)) is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
} }
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
@ -199,13 +240,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
left.name == right.name left.name == right.name
} }
pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
li.name == ri.name && self.eq_pat(lp, rp) li.name == ri.name && self.eq_pat(lp, rp)
} }
/// Checks whether two patterns are the same. /// Checks whether two patterns are the same.
pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) { match (&left.kind, &right.kind) {
(&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
(&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
@ -214,8 +255,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
(&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
}, },
(&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
if eq {
self.locals.insert(li, ri);
}
eq
}, },
(&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
(&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
@ -251,8 +296,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
} }
fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
left.is_global() == right.is_global() match (left.res, right.res) {
&& over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)) (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
(Res::Local(_), _) | (_, Res::Local(_)) => false,
_ => over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)),
}
} }
fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
@ -279,28 +327,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
} }
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
self.eq_ty_kind(&left.kind, &right.kind) self.eq_ty_kind(&left.kind, &right.kind)
} }
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
match (left, right) { match (left, right) {
(&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
let old_maybe_typeck_results = self.maybe_typeck_results; let cx = self.inner.cx;
let eval_const =
let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(ll_id.body)); self.eq_ty(lt, rt) && eval_const(ll_id.body) == eval_const(rl_id.body)
let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body));
self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(rl_id.body));
let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
let eq_ty = self.eq_ty(lt, rt);
self.maybe_typeck_results = old_maybe_typeck_results;
eq_ty && ll == rl
}, },
(&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
@ -667,10 +706,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
} }
pub fn hash_path(&mut self, p: &Path<'_>) { pub fn hash_path(&mut self, path: &Path<'_>) {
p.is_global().hash(&mut self.s); match path.res {
for p in p.segments { // constant hash since equality is dependant on inter-expression context
self.hash_name(p.ident.name); Res::Local(_) => 1_usize.hash(&mut self.s),
_ => {
for seg in path.segments {
self.hash_name(seg.ident.name);
}
},
} }
} }

1884
clippy_utils/src/lib.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
use crate::utils::{get_pat_name, match_var, snippet}; use crate::{get_pat_name, match_var, snippet};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
use rustc_lint::LateContext; use rustc_lint::LateContext;

View file

@ -1,7 +1,7 @@
//! Contains utility functions to generate suggestions. //! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
use rustc_ast::util::parser::AssocOp; use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token}; use rustc_ast::{ast, token};
use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_ast_pretty::pprust::token_kind_to_string;

View file

@ -1,4 +1,4 @@
use crate::utils; use crate as utils;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::Res; use rustc_hir::def::Res;

View file

@ -1,4 +1,4 @@
use crate::utils::path_to_local_id; use crate::path_to_local_id;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Arm, Body, Expr, HirId, Stmt}; use rustc_hir::{Arm, Body, Expr, HirId, Stmt};

View file

@ -108,6 +108,9 @@ should only commit files changed by `cargo dev bless` for the
specific lint you are creating/editing. Note that if the generated files are specific lint you are creating/editing. Note that if the generated files are
empty, they should be removed. empty, they should be removed.
Note that you can run multiple test files by specifying a comma separated list:
`TESTNAME=foo_functions,test2,test3`.
### Cargo lints ### Cargo lints
For cargo lints, the process of testing differs in that we are interested in For cargo lints, the process of testing differs in that we are interested in
@ -289,7 +292,7 @@ the next section. Let's worry about the details later and emit our lint for
Depending on how complex we want our lint message to be, we can choose from a Depending on how complex we want our lint message to be, we can choose from a
variety of lint emission functions. They can all be found in variety of lint emission functions. They can all be found in
[`clippy_lints/src/utils/diagnostics.rs`][diagnostics]. [`clippy_utils/src/diagnostics.rs`][diagnostics].
`span_lint_and_help` seems most appropriate in this case. It allows us to `span_lint_and_help` seems most appropriate in this case. It allows us to
provide an extra help message and we can't really suggest a better name provide an extra help message and we can't really suggest a better name
@ -318,7 +321,7 @@ When code or an identifier must appear in a message or label, it should be
surrounded with single grave accents \`. surrounded with single grave accents \`.
[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html [the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
## Adding the lint logic ## Adding the lint logic
@ -534,7 +537,7 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c
behavior that can be seen as a false positive for some users. Adding a configuration is done behavior that can be seen as a false positive for some users. Adding a configuration is done
in the following steps: in the following steps:
1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) 1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
like this: like this:
```rust ```rust
/// Lint: LINT_NAME. <The configuration field doc comment> /// Lint: LINT_NAME. <The configuration field doc comment>
@ -633,7 +636,7 @@ documentation currently. This is unfortunate, but in most cases you can probably
get away with copying things from existing similar lints. If you are stuck, get away with copying things from existing similar lints. If you are stuck,
don't hesitate to ask on [Zulip] or in the issue/PR. don't hesitate to ask on [Zulip] or in the issue/PR.
[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
[if_chain]: https://docs.rs/if_chain/*/if_chain/ [if_chain]: https://docs.rs/if_chain/*/if_chain/
[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html

View file

@ -78,7 +78,7 @@ impl LateLintPass<'_> for MyStructLint {
There are two ways to do this, depending if the target trait is part of lang items. There are two ways to do this, depending if the target trait is part of lang items.
```rust ```rust
use crate::utils::{implements_trait, match_trait_method, paths}; use clippy_utils::{implements_trait, match_trait_method, paths};
impl LateLintPass<'_> for MyStructLint { impl LateLintPass<'_> for MyStructLint {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@ -112,7 +112,7 @@ We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`]
To check if our type defines a method called `some_method`: To check if our type defines a method called `some_method`:
```rust ```rust
use crate::utils::{is_type_diagnostic_item, return_ty}; use clippy_utils::{is_type_diagnostic_item, return_ty};
impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
# Dealing with macros # Dealing with macros
There are several helpers in Clippy's utils to deal with macros: There are several helpers in [`clippy_utils`][utils] to deal with macros:
- `in_macro()`: detect if the given span is expanded by a macro - `in_macro()`: detect if the given span is expanded by a macro
@ -199,4 +199,5 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
[paths]: ../clippy_lints/src/utils/paths.rs [paths]: ../clippy_utils/src/paths.rs
[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs

View file

@ -1,6 +1,5 @@
clippy 0.1.51 (7f5bb7fd0 2021-02-06) clippy 0.1.52 (697f3b6d4 2021-02-22)
cargo-0.49.0//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/macros/mod.rs:409:34 clippy::match_same_arms "this `match` has identical arm bodies"
cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata"
cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata"
cargo-0.49.0/src/bin/cargo/cli.rs:104:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/bin/cargo/cli.rs:104:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -22,6 +21,7 @@ cargo-0.49.0/src/bin/cargo/commands/check.rs:1:5 clippy::wildcard_imports "usage
cargo-0.49.0/src/bin/cargo/commands/clean.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/clean.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
cargo-0.49.0/src/bin/cargo/commands/doc.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/doc.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
cargo-0.49.0/src/bin/cargo/commands/fetch.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/fetch.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
cargo-0.49.0/src/bin/cargo/commands/fetch.rs:22:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/bin/cargo/commands/fix.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/fix.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
cargo-0.49.0/src/bin/cargo/commands/generate_lockfile.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/generate_lockfile.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
cargo-0.49.0/src/bin/cargo/commands/git_checkout.rs:1:5 clippy::wildcard_imports "usage of wildcard import" cargo-0.49.0/src/bin/cargo/commands/git_checkout.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
@ -99,10 +99,10 @@ cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:411:9 clippy::
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:420:69 clippy::doc_markdown "you should put `mode/target_kind` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:420:69 clippy::doc_markdown "you should put `mode/target_kind` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:423:19 clippy::doc_markdown "you should put `CrateTypes` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:423:19 clippy::doc_markdown "you should put `CrateTypes` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:469:58 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:469:58 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:603:19 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:603:19 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:665:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:665:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:697:12 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:82:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:82:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:84:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:84:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -151,7 +151,6 @@ cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:361:5 clippy::must_use_candi
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:374:43 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:374:43 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:383:41 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:383:41 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:391:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:391:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:397:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:397:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -173,14 +172,13 @@ cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_error
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:48:56 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:48:56 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:567:20 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:567:20 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:576:28 clippy::shadow_unrelated "`mut value` is being shadowed" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:576:28 clippy::shadow_unrelated "`mut value` is being shadowed"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:606:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:606:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:688:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:688:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:756:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:756:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1021:51 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1021:51 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1656:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1656:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -236,7 +234,7 @@ cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:93:24 clippy::doc_markdown "yo
cargo-0.49.0/src/cargo/core/compiler/links.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name" cargo-0.49.0/src/cargo/core/compiler/links.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:1016:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/mod.rs:1016:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:1094:19 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/mod.rs:1094:19 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:1268:34 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" cargo-0.49.0/src/cargo/core/compiler/mod.rs:1268:34 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:1277:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/mod.rs:1277:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:179:1 clippy::too_many_lines "this function has too many lines (162/100)" cargo-0.49.0/src/cargo/core/compiler/mod.rs:179:1 clippy::too_many_lines "this function has too many lines (162/100)"
@ -245,13 +243,13 @@ cargo-0.49.0/src/cargo/core/compiler/mod.rs:201:25 clippy::single_match_else "yo
cargo-0.49.0/src/cargo/core/compiler/mod.rs:267:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/mod.rs:267:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:324:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/mod.rs:324:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:392:45 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/compiler/mod.rs:392:45 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:415:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/core/compiler/mod.rs:415:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:464:18 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do." cargo-0.49.0/src/cargo/core/compiler/mod.rs:464:18 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do."
cargo-0.49.0/src/cargo/core/compiler/mod.rs:488:61 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do." cargo-0.49.0/src/cargo/core/compiler/mod.rs:488:61 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do."
cargo-0.49.0/src/cargo/core/compiler/mod.rs:667:15 clippy::similar_names "binding's name is too similar to existing binding" cargo-0.49.0/src/cargo/core/compiler/mod.rs:667:15 clippy::similar_names "binding's name is too similar to existing binding"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:725:42 clippy::match_same_arms "this `match` has identical arm bodies" cargo-0.49.0/src/cargo/core/compiler/mod.rs:725:42 clippy::match_same_arms "this `match` has identical arm bodies"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:736:1 clippy::too_many_lines "this function has too many lines (141/100)" cargo-0.49.0/src/cargo/core/compiler/mod.rs:736:1 clippy::too_many_lines "this function has too many lines (141/100)"
cargo-0.49.0/src/cargo/core/compiler/mod.rs:73:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/compiler/mod.rs:73:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -290,7 +288,6 @@ cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::missing_errors_d
cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::module_name_repetitions "item name ends with its containing module's name" cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::module_name_repetitions "item name ends with its containing module's name"
cargo-0.49.0/src/cargo/core/dependency.rs:157:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/dependency.rs:157:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/dependency.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/dependency.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:224:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:224:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:23:1 clippy::struct_excessive_bools "more than 3 bools in a struct" cargo-0.49.0/src/cargo/core/dependency.rs:23:1 clippy::struct_excessive_bools "more than 3 bools in a struct"
@ -300,13 +297,10 @@ cargo-0.49.0/src/cargo/core/dependency.rs:274:5 clippy::must_use_candidate "this
cargo-0.49.0/src/cargo/core/dependency.rs:278:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:278:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:296:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/dependency.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:311:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:311:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:319:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:319:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:323:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/dependency.rs:337:75 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/dependency.rs:337:75 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/dependency.rs:379:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/dependency.rs:397:56 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/dependency.rs:397:56 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/dependency.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/dependency.rs:408:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/dependency.rs:408:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -452,12 +446,10 @@ cargo-0.49.0/src/cargo/core/package.rs:287:1 clippy::module_name_repetitions "it
cargo-0.49.0/src/cargo/core/package.rs:385:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:385:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:421:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" cargo-0.49.0/src/cargo/core/package.rs:421:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)"
cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/package.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:453:60 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/package.rs:453:60 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/package.rs:459:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:459:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:473:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:473:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:552:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/package.rs:587:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package.rs:587:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package.rs:588:9 clippy::needless_question_mark "Question mark operator is useless here" cargo-0.49.0/src/cargo/core/package.rs:588:9 clippy::needless_question_mark "Question mark operator is useless here"
cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value"
@ -490,13 +482,12 @@ cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_errors_doc "
cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/package_id_spec.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/package_id_spec.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/package_id_spec.rs:88:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/package_id_spec.rs:88:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:1014:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/profiles.rs:1014:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:1018:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/profiles.rs:1018:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:1028:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/profiles.rs:1028:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:106:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/profiles.rs:106:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/profiles.rs:294:40 clippy::if_not_else "unnecessary boolean `not` operation" cargo-0.49.0/src/cargo/core/profiles.rs:294:40 clippy::if_not_else "unnecessary boolean `not` operation"
@ -522,9 +513,7 @@ cargo-0.49.0/src/cargo/core/registry.rs:19:5 clippy::missing_errors_doc "docs fo
cargo-0.49.0/src/cargo/core/registry.rs:240:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/registry.rs:240:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/registry.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/registry.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/registry.rs:344:49 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/core/registry.rs:344:49 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/core/registry.rs:358:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/registry.rs:369:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/registry.rs:369:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/registry.rs:49:1 clippy::module_name_repetitions "item name ends with its containing module's name" cargo-0.49.0/src/cargo/core/registry.rs:49:1 clippy::module_name_repetitions "item name ends with its containing module's name"
cargo-0.49.0/src/cargo/core/registry.rs:520:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/core/registry.rs:520:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -538,7 +527,6 @@ cargo-0.49.0/src/cargo/core/resolver/context.rs:274:53 clippy::redundant_closure
cargo-0.49.0/src/cargo/core/resolver/context.rs:42:1 clippy::module_name_repetitions "item name starts with its containing module's name" cargo-0.49.0/src/cargo/core/resolver/context.rs:42:1 clippy::module_name_repetitions "item name starts with its containing module's name"
cargo-0.49.0/src/cargo/core/resolver/context.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/resolver/context.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::too_many_lines "this function has too many lines (164/100)" cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::too_many_lines "this function has too many lines (164/100)"
cargo-0.49.0/src/cargo/core/resolver/encode.rs:339:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" cargo-0.49.0/src/cargo/core/resolver/encode.rs:339:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants"
cargo-0.49.0/src/cargo/core/resolver/encode.rs:438:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/core/resolver/encode.rs:438:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@ -596,7 +584,6 @@ cargo-0.49.0/src/cargo/core/resolver/resolve.rs:255:5 clippy::must_use_candidate
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:269:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:269:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:274:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:274:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead"
cargo-0.49.0/src/cargo/core/resolver/resolve.rs:280:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/resolver/resolve.rs:280:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -639,6 +626,7 @@ cargo-0.49.0/src/cargo/core/shell.rs:314:5 clippy::must_use_candidate "this meth
cargo-0.49.0/src/cargo/core/shell.rs:322:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/shell.rs:322:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/shell.rs:330:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/shell.rs:330:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/shell.rs:345:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/core/shell.rs:345:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/shell.rs:459:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/core/shell.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/shell.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/source/mod.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/source/mod.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/source/mod.rs:247:1 clippy::module_name_repetitions "item name starts with its containing module's name" cargo-0.49.0/src/cargo/core/source/mod.rs:247:1 clippy::module_name_repetitions "item name starts with its containing module's name"
@ -682,7 +670,6 @@ cargo-0.49.0/src/cargo/core/source/source_id.rs:241:5 clippy::must_use_candidate
cargo-0.49.0/src/cargo/core/source/source_id.rs:252:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/source/source_id.rs:252:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/source/source_id.rs:257:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/source/source_id.rs:257:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/core/source/source_id.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/source/source_id.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/source/source_id.rs:310:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/source/source_id.rs:310:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/core/source/source_id.rs:318:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/core/source/source_id.rs:318:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -780,7 +767,6 @@ cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_panics_doc "docs
cargo-0.49.0/src/cargo/core/workspace.rs:893:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/workspace.rs:893:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/workspace.rs:906:24 clippy::redundant_else "redundant else block" cargo-0.49.0/src/cargo/core/workspace.rs:906:24 clippy::redundant_else "redundant else block"
cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found"
@ -808,7 +794,6 @@ cargo-0.49.0/src/cargo/ops/cargo_compile.rs:249:1 clippy::missing_errors_doc "do
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:258:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:258:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:267:16 clippy::needless_question_mark "Question mark operator is useless here" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:267:16 clippy::needless_question_mark "Question mark operator is useless here"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::too_many_lines "this function has too many lines (219/100)" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::too_many_lines "this function has too many lines (219/100)"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:468:9 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:468:9 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:548:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:548:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -824,7 +809,6 @@ cargo-0.49.0/src/cargo/ops/cargo_compile.rs:612:21 clippy::doc_markdown "you sho
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:613:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:613:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:618:9 clippy::similar_names "binding's name is too similar to existing binding" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:618:9 clippy::similar_names "binding's name is too similar to existing binding"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:655:50 clippy::match_same_arms "this `match` has identical arm bodies" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:655:50 clippy::match_same_arms "this `match` has identical arm bodies"
cargo-0.49.0/src/cargo/ops/cargo_compile.rs:673:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/cargo_compile.rs:673:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -879,6 +863,7 @@ cargo-0.49.0/src/cargo/ops/cargo_package.rs:394:5 clippy::items_after_statements
cargo-0.49.0/src/cargo/ops/cargo_package.rs:425:61 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/ops/cargo_package.rs:425:61 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/ops/cargo_package.rs:459:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/ops/cargo_package.rs:459:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/ops/cargo_package.rs:66:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_package.rs:66:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_package.rs:69:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/ops/cargo_package.rs:93:20 clippy::if_not_else "unnecessary boolean `not` operation" cargo-0.49.0/src/cargo/ops/cargo_package.rs:93:20 clippy::if_not_else "unnecessary boolean `not` operation"
cargo-0.49.0/src/cargo/ops/cargo_pkgid.rs:5:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_pkgid.rs:5:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -893,7 +878,6 @@ cargo-0.49.0/src/cargo/ops/cargo_run.rs:37:16 clippy::redundant_else "redundant
cargo-0.49.0/src/cargo/ops/cargo_run.rs:53:9 clippy::if_not_else "unnecessary boolean `not` operation" cargo-0.49.0/src/cargo/ops/cargo_run.rs:53:9 clippy::if_not_else "unnecessary boolean `not` operation"
cargo-0.49.0/src/cargo/ops/cargo_run.rs:65:16 clippy::redundant_else "redundant else block" cargo-0.49.0/src/cargo/ops/cargo_run.rs:65:16 clippy::redundant_else "redundant else block"
cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/ops/cargo_test.rs:16:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_test.rs:16:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_test.rs:43:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/cargo_test.rs:43:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/cargo_test.rs:84:17 clippy::similar_names "binding's name is too similar to existing binding" cargo-0.49.0/src/cargo/ops/cargo_test.rs:84:17 clippy::similar_names "binding's name is too similar to existing binding"
@ -904,7 +888,7 @@ cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:233:21 clippy::si
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:22 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:22 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:63 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:63 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:253:17 clippy::if_not_else "unnecessary boolean `not` operation" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:253:17 clippy::if_not_else "unnecessary boolean `not` operation"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:505:8 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:505:8 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:525:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:525:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:27 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:27 clippy::redundant_closure_for_method_calls "redundant closure found"
@ -967,7 +951,6 @@ cargo-0.49.0/src/cargo/ops/registry.rs:794:16 clippy::single_match_else "you see
cargo-0.49.0/src/cargo/ops/registry.rs:828:14 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" cargo-0.49.0/src/cargo/ops/registry.rs:828:14 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation"
cargo-0.49.0/src/cargo/ops/registry.rs:848:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/registry.rs:848:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::module_name_repetitions "item name starts with its containing module's name" cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::module_name_repetitions "item name starts with its containing module's name"
cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::too_many_lines "this function has too many lines (137/100)" cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::too_many_lines "this function has too many lines (137/100)"
cargo-0.49.0/src/cargo/ops/resolve.rs:241:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/ops/resolve.rs:241:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -997,6 +980,7 @@ cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::struct_excessive_bools "more
cargo-0.49.0/src/cargo/ops/tree/mod.rs:360:30 clippy::match_same_arms "this `match` has identical arm bodies" cargo-0.49.0/src/cargo/ops/tree/mod.rs:360:30 clippy::match_same_arms "this `match` has identical arm bodies"
cargo-0.49.0/src/cargo/ops/tree/mod.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/ops/tree/mod.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/ops/vendor.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name" cargo-0.49.0/src/cargo/ops/vendor.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name"
cargo-0.49.0/src/cargo/ops/vendor.rs:215:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/ops/vendor.rs:314:34 clippy::match_same_arms "this `match` has identical arm bodies" cargo-0.49.0/src/cargo/ops/vendor.rs:314:34 clippy::match_same_arms "this `match` has identical arm bodies"
@ -1017,7 +1001,6 @@ cargo-0.49.0/src/cargo/sources/directory.rs:14:1 clippy::module_name_repetitions
cargo-0.49.0/src/cargo/sources/directory.rs:90:56 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/sources/directory.rs:90:56 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/sources/git/source.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name" cargo-0.49.0/src/cargo/sources/git/source.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name"
cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/sources/git/source.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/sources/git/source.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
@ -1032,7 +1015,6 @@ cargo-0.49.0/src/cargo/sources/git/utils.rs:184:5 clippy::missing_errors_doc "do
cargo-0.49.0/src/cargo/sources/git/utils.rs:188:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/utils.rs:188:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/utils.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/utils.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/sources/git/utils.rs:262:13 clippy::if_not_else "unnecessary boolean `not` operation" cargo-0.49.0/src/cargo/sources/git/utils.rs:262:13 clippy::if_not_else "unnecessary boolean `not` operation"
cargo-0.49.0/src/cargo/sources/git/utils.rs:289:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/git/utils.rs:289:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/git/utils.rs:294:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/sources/git/utils.rs:294:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -1062,7 +1044,6 @@ cargo-0.49.0/src/cargo/sources/path.rs:429:5 clippy::missing_errors_doc "docs fo
cargo-0.49.0/src/cargo/sources/path.rs:460:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/sources/path.rs:460:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/sources/path.rs:473:43 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/sources/path.rs:473:43 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/sources/path.rs:482:43 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/sources/path.rs:482:43 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/sources/path.rs:55:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/sources/path.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/path.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/path.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/path.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/sources/path.rs:98:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/sources/path.rs:98:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -1142,7 +1123,6 @@ cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you shoul
cargo-0.49.0/src/cargo/util/config/mod.rs:1049:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1049:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1064:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1064:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1166:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1166:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1181:33 clippy::needless_question_mark "Question mark operator is useless here" cargo-0.49.0/src/cargo/util/config/mod.rs:1181:33 clippy::needless_question_mark "Question mark operator is useless here"
@ -1157,7 +1137,6 @@ cargo-0.49.0/src/cargo/util/config/mod.rs:1225:5 clippy::missing_errors_doc "doc
cargo-0.49.0/src/cargo/util/config/mod.rs:1229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:124:1 clippy::struct_excessive_bools "more than 3 bools in a struct" cargo-0.49.0/src/cargo/util/config/mod.rs:124:1 clippy::struct_excessive_bools "more than 3 bools in a struct"
cargo-0.49.0/src/cargo/util/config/mod.rs:1254:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1254:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1263:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/config/mod.rs:1279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/config/mod.rs:1281:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" cargo-0.49.0/src/cargo/util/config/mod.rs:1281:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
cargo-0.49.0/src/cargo/util/config/mod.rs:1323:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/util/config/mod.rs:1323:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@ -1224,7 +1203,6 @@ cargo-0.49.0/src/cargo/util/dependency_queue.rs:151:5 clippy::must_use_candidate
cargo-0.49.0/src/cargo/util/dependency_queue.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/dependency_queue.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/dependency_queue.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/util/dependency_queue.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/dependency_queue.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/dependency_queue.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/dependency_queue.rs:66:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/dependency_queue.rs:91:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/util/dependency_queue.rs:91:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/util/diagnostic_server.rs:218:1 clippy::module_name_repetitions "item name ends with its containing module's name" cargo-0.49.0/src/cargo/util/diagnostic_server.rs:218:1 clippy::module_name_repetitions "item name ends with its containing module's name"
cargo-0.49.0/src/cargo/util/diagnostic_server.rs:230:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/diagnostic_server.rs:230:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -1238,7 +1216,6 @@ cargo-0.49.0/src/cargo/util/errors.rs:143:5 clippy::must_use_candidate "this met
cargo-0.49.0/src/cargo/util/errors.rs:150:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/errors.rs:150:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/errors.rs:15:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/errors.rs:15:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/errors.rs:237:5 clippy::pub_enum_variant_names "variant name ends with the enum's name" cargo-0.49.0/src/cargo/util/errors.rs:237:5 clippy::pub_enum_variant_names "variant name ends with the enum's name"
cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/errors.rs:321:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/errors.rs:321:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/errors.rs:328:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/errors.rs:328:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -1263,12 +1240,11 @@ cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_possible_truncation "ca
cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value" cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value"
cargo-0.49.0/src/cargo/util/flock.rs:335:44 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" cargo-0.49.0/src/cargo/util/flock.rs:335:44 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value"
cargo-0.49.0/src/cargo/util/flock.rs:379:35 clippy::match_same_arms "this `match` has identical arm bodies" cargo-0.49.0/src/cargo/util/flock.rs:379:35 clippy::match_same_arms "this `match` has identical arm bodies"
cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/flock.rs:96:17 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/util/graph.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/graph.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/graph.rs:41:51 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/util/graph.rs:41:51 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/util/graph.rs:45:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/graph.rs:45:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -1331,8 +1307,8 @@ cargo-0.49.0/src/cargo/util/paths.rs:445:1 clippy::missing_errors_doc "docs for
cargo-0.49.0/src/cargo/util/paths.rs:459:45 clippy::redundant_closure_for_method_calls "redundant closure found" cargo-0.49.0/src/cargo/util/paths.rs:459:45 clippy::redundant_closure_for_method_calls "redundant closure found"
cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/paths.rs:514:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/util/paths.rs:54:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/paths.rs:54:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/paths.rs:63:19 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" cargo-0.49.0/src/cargo/util/paths.rs:63:19 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else"
cargo-0.49.0/src/cargo/util/paths.rs:88:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/paths.rs:88:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -1346,6 +1322,7 @@ cargo-0.49.0/src/cargo/util/process_builder.rs:185:5 clippy::missing_errors_doc
cargo-0.49.0/src/cargo/util/process_builder.rs:190:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/process_builder.rs:190:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
cargo-0.49.0/src/cargo/util/process_builder.rs:278:22 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
cargo-0.49.0/src/cargo/util/process_builder.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/process_builder.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/process_builder.rs:343:39 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" cargo-0.49.0/src/cargo/util/process_builder.rs:343:39 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
cargo-0.49.0/src/cargo/util/progress.rs:122:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/progress.rs:122:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@ -1386,6 +1363,7 @@ cargo-0.49.0/src/cargo/util/rustc.rs:115:5 clippy::doc_markdown "you should put
cargo-0.49.0/src/cargo/util/rustc.rs:162:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" cargo-0.49.0/src/cargo/util/rustc.rs:162:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
cargo-0.49.0/src/cargo/util/rustc.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/rustc.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/sha256.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" cargo-0.49.0/src/cargo/util/sha256.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
cargo-0.49.0/src/cargo/util/sha256.rs:16:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
cargo-0.49.0/src/cargo/util/sha256.rs:20:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/sha256.rs:20:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/sha256.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" cargo-0.49.0/src/cargo/util/sha256.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
cargo-0.49.0/src/cargo/util/sha256.rs:40:24 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" cargo-0.49.0/src/cargo/util/sha256.rs:40:24 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
@ -1451,6 +1429,7 @@ iron-0.6.1/src/iron.rs:133:5 clippy::missing_errors_doc "docs for function retur
iron-0.6.1/src/iron.rs:143:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" iron-0.6.1/src/iron.rs:143:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
iron-0.6.1/src/iron.rs:149:13 clippy::redundant_field_names "redundant field names in struct initialization" iron-0.6.1/src/iron.rs:149:13 clippy::redundant_field_names "redundant field names in struct initialization"
iron-0.6.1/src/iron.rs:167:49 clippy::similar_names "binding's name is too similar to existing binding" iron-0.6.1/src/iron.rs:167:49 clippy::similar_names "binding's name is too similar to existing binding"
iron-0.6.1/src/iron.rs:196:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
iron-0.6.1/src/iron.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" iron-0.6.1/src/iron.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
iron-0.6.1/src/iron.rs:85:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" iron-0.6.1/src/iron.rs:85:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
iron-0.6.1/src/iron.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" iron-0.6.1/src/iron.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@ -1991,18 +1970,15 @@ log-0.4.11/src/lib.rs:1118:5 clippy::must_use_candidate "this method could have
log-0.4.11/src/lib.rs:1177:1 clippy::inline_always "you have declared `#[inline(always)]` on `max_level`. This is usually a bad idea" log-0.4.11/src/lib.rs:1177:1 clippy::inline_always "you have declared `#[inline(always)]` on `max_level`. This is usually a bad idea"
log-0.4.11/src/lib.rs:1178:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:1178:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:1306:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" log-0.4.11/src/lib.rs:1306:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
log-0.4.11/src/lib.rs:1306:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
log-0.4.11/src/lib.rs:1358:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:1358:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:1359:5 clippy::if_not_else "unnecessary `!=` operation" log-0.4.11/src/lib.rs:1359:5 clippy::if_not_else "unnecessary `!=` operation"
log-0.4.11/src/lib.rs:1407:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:1407:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:329:27 clippy::derive_hash_xor_eq "you are deriving `Hash` but have implemented `PartialEq` explicitly"
log-0.4.11/src/lib.rs:356:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" log-0.4.11/src/lib.rs:356:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type"
log-0.4.11/src/lib.rs:448:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" log-0.4.11/src/lib.rs:448:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation"
log-0.4.11/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:506:28 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" log-0.4.11/src/lib.rs:506:28 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)"
log-0.4.11/src/lib.rs:506:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" log-0.4.11/src/lib.rs:506:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
log-0.4.11/src/lib.rs:506:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:506:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:520:27 clippy::derive_hash_xor_eq "you are deriving `Hash` but have implemented `PartialEq` explicitly"
log-0.4.11/src/lib.rs:538:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" log-0.4.11/src/lib.rs:538:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type"
log-0.4.11/src/lib.rs:653:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" log-0.4.11/src/lib.rs:653:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
log-0.4.11/src/lib.rs:661:21 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" log-0.4.11/src/lib.rs:661:21 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)"
@ -2112,7 +2088,6 @@ puffin-02dd4a3/puffin/src/data.rs:137:24 clippy::match_same_arms "this `match` h
puffin-02dd4a3/puffin/src/data.rs:177:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" puffin-02dd4a3/puffin/src/data.rs:177:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
puffin-02dd4a3/puffin/src/data.rs:211:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" puffin-02dd4a3/puffin/src/data.rs:211:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
puffin-02dd4a3/puffin/src/data.rs:24:5 clippy::wildcard_imports "usage of wildcard import" puffin-02dd4a3/puffin/src/data.rs:24:5 clippy::wildcard_imports "usage of wildcard import"
puffin-02dd4a3/puffin/src/data.rs:75:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
puffin-02dd4a3/puffin/src/lib.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" puffin-02dd4a3/puffin/src/lib.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
@ -2146,7 +2121,6 @@ quote-1.0.7/src/ext.rs:10:1 clippy::module_name_repetitions "item name ends with
quote-1.0.7/src/ext.rs:7:5 clippy::doc_markdown "you should put `TokenStream` between ticks in the documentation" quote-1.0.7/src/ext.rs:7:5 clippy::doc_markdown "you should put `TokenStream` between ticks in the documentation"
quote-1.0.7/src/ident_fragment.rs:13:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" quote-1.0.7/src/ident_fragment.rs:13:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
quote-1.0.7/src/ident_fragment.rs:51:31 clippy::manual_strip "stripping a prefix manually" quote-1.0.7/src/ident_fragment.rs:51:31 clippy::manual_strip "stripping a prefix manually"
quote-1.0.7/src/runtime.rs:332:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
quote-1.0.7/src/runtime.rs:52:5 clippy::module_name_repetitions "item name ends with its containing module's name" quote-1.0.7/src/runtime.rs:52:5 clippy::module_name_repetitions "item name ends with its containing module's name"
quote-1.0.7/src/runtime.rs:63:5 clippy::module_name_repetitions "item name ends with its containing module's name" quote-1.0.7/src/runtime.rs:63:5 clippy::module_name_repetitions "item name ends with its containing module's name"
quote-1.0.7/src/runtime.rs:66:33 clippy::doc_markdown "you should put `DoesNotHaveIter` between ticks in the documentation" quote-1.0.7/src/runtime.rs:66:33 clippy::doc_markdown "you should put `DoesNotHaveIter` between ticks in the documentation"
@ -2183,7 +2157,6 @@ rand-0.7.3/src/distributions/binomial.rs:233:32 clippy::cast_precision_loss "cas
rand-0.7.3/src/distributions/binomial.rs:234:27 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" rand-0.7.3/src/distributions/binomial.rs:234:27 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
rand-0.7.3/src/distributions/binomial.rs:251:22 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value" rand-0.7.3/src/distributions/binomial.rs:251:22 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value"
rand-0.7.3/src/distributions/binomial.rs:255:9 clippy::if_not_else "unnecessary `!=` operation" rand-0.7.3/src/distributions/binomial.rs:255:9 clippy::if_not_else "unnecessary `!=` operation"
rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/binomial.rs:45:17 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" rand-0.7.3/src/distributions/binomial.rs:45:17 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
rand-0.7.3/src/distributions/binomial.rs:46:5 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value" rand-0.7.3/src/distributions/binomial.rs:46:5 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value"
@ -2194,25 +2167,18 @@ rand-0.7.3/src/distributions/binomial.rs:81:21 clippy::cast_precision_loss "cast
rand-0.7.3/src/distributions/binomial.rs:82:32 clippy::cast_possible_truncation "casting `u64` to `i32` may truncate the value" rand-0.7.3/src/distributions/binomial.rs:82:32 clippy::cast_possible_truncation "casting `u64` to `i32` may truncate the value"
rand-0.7.3/src/distributions/binomial.rs:88:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" rand-0.7.3/src/distributions/binomial.rs:88:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
rand-0.7.3/src/distributions/binomial.rs:99:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" rand-0.7.3/src/distributions/binomial.rs:99:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/dirichlet.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/dirichlet.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/dirichlet.rs:64:32 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" rand-0.7.3/src/distributions/dirichlet.rs:64:32 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
rand-0.7.3/src/distributions/dirichlet.rs:65:23 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" rand-0.7.3/src/distributions/dirichlet.rs:65:23 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/float.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name" rand-0.7.3/src/distributions/float.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name"
rand-0.7.3/src/distributions/gamma.rs:13:5 clippy::enum_glob_use "usage of wildcard import for enum variants" rand-0.7.3/src/distributions/gamma.rs:13:5 clippy::enum_glob_use "usage of wildcard import for enum variants"
rand-0.7.3/src/distributions/gamma.rs:14:5 clippy::enum_glob_use "usage of wildcard import for enum variants" rand-0.7.3/src/distributions/gamma.rs:14:5 clippy::enum_glob_use "usage of wildcard import for enum variants"
rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/integer.rs:23:9 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" rand-0.7.3/src/distributions/integer.rs:23:9 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value"
rand-0.7.3/src/distributions/integer.rs:30:9 clippy::cast_possible_truncation "casting `u32` to `u16` may truncate the value" rand-0.7.3/src/distributions/integer.rs:30:9 clippy::cast_possible_truncation "casting `u32` to `u16` may truncate the value"
@ -2226,7 +2192,6 @@ rand-0.7.3/src/distributions/normal.rs:47:25 clippy::unseparated_literal_suffix
rand-0.7.3/src/distributions/normal.rs:48:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" rand-0.7.3/src/distributions/normal.rs:48:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
rand-0.7.3/src/distributions/other.rs:89:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" rand-0.7.3/src/distributions/other.rs:89:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value"
rand-0.7.3/src/distributions/pareto.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/pareto.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value"
rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value"
@ -2316,6 +2281,7 @@ rand-0.7.3/src/rngs/adapter/reseeding.rs:112:5 clippy::inline_always "you have d
rand-0.7.3/src/rngs/adapter/reseeding.rs:117:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" rand-0.7.3/src/rngs/adapter/reseeding.rs:117:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea"
rand-0.7.3/src/rngs/adapter/reseeding.rs:198:13 clippy::cast_possible_wrap "casting `u64` to `i64` may wrap around the value" rand-0.7.3/src/rngs/adapter/reseeding.rs:198:13 clippy::cast_possible_wrap "casting `u64` to `i64` may wrap around the value"
rand-0.7.3/src/rngs/adapter/reseeding.rs:231:9 clippy::cast_possible_wrap "casting `usize` to `isize` may wrap around the value" rand-0.7.3/src/rngs/adapter/reseeding.rs:231:9 clippy::cast_possible_wrap "casting `usize` to `isize` may wrap around the value"
rand-0.7.3/src/rngs/adapter/reseeding.rs:249:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
rand-0.7.3/src/rngs/adapter/reseeding.rs:27:28 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation" rand-0.7.3/src/rngs/adapter/reseeding.rs:27:28 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation"
rand-0.7.3/src/rngs/adapter/reseeding.rs:79:1 clippy::module_name_repetitions "item name starts with its containing module's name" rand-0.7.3/src/rngs/adapter/reseeding.rs:79:1 clippy::module_name_repetitions "item name starts with its containing module's name"
rand-0.7.3/src/rngs/entropy.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" rand-0.7.3/src/rngs/entropy.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name"
@ -2344,7 +2310,6 @@ rand-0.7.3/src/seq/index.rs:139:13 clippy::enum_glob_use "usage of wildcard impo
rand-0.7.3/src/seq/index.rs:159:1 clippy::module_name_repetitions "item name starts with its containing module's name" rand-0.7.3/src/seq/index.rs:159:1 clippy::module_name_repetitions "item name starts with its containing module's name"
rand-0.7.3/src/seq/index.rs:171:13 clippy::enum_glob_use "usage of wildcard import for enum variants" rand-0.7.3/src/seq/index.rs:171:13 clippy::enum_glob_use "usage of wildcard import for enum variants"
rand-0.7.3/src/seq/index.rs:180:13 clippy::enum_glob_use "usage of wildcard import for enum variants" rand-0.7.3/src/seq/index.rs:180:13 clippy::enum_glob_use "usage of wildcard import for enum variants"
rand-0.7.3/src/seq/index.rs:213:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand-0.7.3/src/seq/index.rs:223:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" rand-0.7.3/src/seq/index.rs:223:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
rand-0.7.3/src/seq/index.rs:224:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" rand-0.7.3/src/seq/index.rs:224:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
rand-0.7.3/src/seq/index.rs:233:25 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" rand-0.7.3/src/seq/index.rs:233:25 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)"
@ -2369,14 +2334,12 @@ rand-0.7.3/src/seq/mod.rs:45:4 clippy::needless_doctest_main "needless `fn main`
rand-0.7.3/src/seq/mod.rs:527:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" rand-0.7.3/src/seq/mod.rs:527:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
rand_core-0.6.0/src/block.rs:117:1 clippy::module_name_repetitions "item name starts with its containing module's name" rand_core-0.6.0/src/block.rs:117:1 clippy::module_name_repetitions "item name starts with its containing module's name"
rand_core-0.6.0/src/block.rs:153:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:153:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand_core-0.6.0/src/block.rs:230:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:230:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:240:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:240:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:245:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:245:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:250:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:250:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:280:1 clippy::module_name_repetitions "item name starts with its containing module's name" rand_core-0.6.0/src/block.rs:280:1 clippy::module_name_repetitions "item name starts with its containing module's name"
rand_core-0.6.0/src/block.rs:319:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:319:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:335:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand_core-0.6.0/src/block.rs:405:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:405:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:415:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:415:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea"
rand_core-0.6.0/src/block.rs:420:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" rand_core-0.6.0/src/block.rs:420:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea"
@ -2386,8 +2349,6 @@ rand_core-0.6.0/src/block.rs:68:1 clippy::module_name_repetitions "item name sta
rand_core-0.6.0/src/error.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand_core-0.6.0/src/error.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand_core-0.6.0/src/error.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand_core-0.6.0/src/error.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand_core-0.6.0/src/error.rs:95:74 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" rand_core-0.6.0/src/error.rs:95:74 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value"
rand_core-0.6.0/src/le.rs:18:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand_core-0.6.0/src/le.rs:27:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
rand_core-0.6.0/src/lib.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" rand_core-0.6.0/src/lib.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
rand_core-0.6.0/src/lib.rs:301:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" rand_core-0.6.0/src/lib.rs:301:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
rand_core-0.6.0/src/lib.rs:303:26 clippy::unreadable_literal "long literal lacking separators" rand_core-0.6.0/src/lib.rs:303:26 clippy::unreadable_literal "long literal lacking separators"
@ -2433,6 +2394,7 @@ rayon-1.5.0/src/iter/chain.rs:58:17 clippy::shadow_unrelated "`b` is being shado
rayon-1.5.0/src/iter/chain.rs:78:14 clippy::shadow_unrelated "`a` is being shadowed" rayon-1.5.0/src/iter/chain.rs:78:14 clippy::shadow_unrelated "`a` is being shadowed"
rayon-1.5.0/src/iter/chain.rs:78:17 clippy::shadow_unrelated "`b` is being shadowed" rayon-1.5.0/src/iter/chain.rs:78:17 clippy::shadow_unrelated "`b` is being shadowed"
rayon-1.5.0/src/iter/chain.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" rayon-1.5.0/src/iter/chain.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
rayon-1.5.0/src/iter/chunks.rs:29:9 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
rayon-1.5.0/src/iter/chunks.rs:3:5 clippy::wildcard_imports "usage of wildcard import" rayon-1.5.0/src/iter/chunks.rs:3:5 clippy::wildcard_imports "usage of wildcard import"
rayon-1.5.0/src/iter/chunks.rs:4:5 clippy::wildcard_imports "usage of wildcard import" rayon-1.5.0/src/iter/chunks.rs:4:5 clippy::wildcard_imports "usage of wildcard import"
rayon-1.5.0/src/iter/chunks.rs:77:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" rayon-1.5.0/src/iter/chunks.rs:77:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@ -2699,7 +2661,6 @@ regex-1.3.2/src/compile.rs:1040:38 clippy::cast_possible_truncation "casting `u1
regex-1.3.2/src/compile.rs:1051:25 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" regex-1.3.2/src/compile.rs:1051:25 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
regex-1.3.2/src/compile.rs:1071:8 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" regex-1.3.2/src/compile.rs:1071:8 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type"
regex-1.3.2/src/compile.rs:112:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" regex-1.3.2/src/compile.rs:112:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
regex-1.3.2/src/compile.rs:112:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
regex-1.3.2/src/compile.rs:154:30 clippy::redundant_closure_for_method_calls "redundant closure found" regex-1.3.2/src/compile.rs:154:30 clippy::redundant_closure_for_method_calls "redundant closure found"
regex-1.3.2/src/compile.rs:156:30 clippy::redundant_closure_for_method_calls "redundant closure found" regex-1.3.2/src/compile.rs:156:30 clippy::redundant_closure_for_method_calls "redundant closure found"
regex-1.3.2/src/compile.rs:185:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" regex-1.3.2/src/compile.rs:185:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
@ -3114,10 +3075,6 @@ regex-1.3.2/src/utf8.rs:85:19 clippy::cast_lossless "casting `u8` to `u32` may b
regex-1.3.2/src/utf8.rs:92:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" regex-1.3.2/src/utf8.rs:92:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
regex-1.3.2/src/utf8.rs:92:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" regex-1.3.2/src/utf8.rs:92:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
regex-1.3.2/src/utf8.rs:97:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" regex-1.3.2/src/utf8.rs:97:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
ripgrep-12.1.1/build.rs:133:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead" ripgrep-12.1.1/build.rs:133:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead"
ripgrep-12.1.1/build.rs:18:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" ripgrep-12.1.1/build.rs:18:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
ripgrep-12.1.1/build.rs:225:14 clippy::redundant_closure_for_method_calls "redundant closure found" ripgrep-12.1.1/build.rs:225:14 clippy::redundant_closure_for_method_calls "redundant closure found"
@ -3171,6 +3128,7 @@ ripgrep-12.1.1/crates/core/args.rs:1524:5 clippy::unnecessary_wraps "this functi
ripgrep-12.1.1/crates/core/args.rs:1635:14 clippy::doc_markdown "you should put `values_of_lossy` between ticks in the documentation" ripgrep-12.1.1/crates/core/args.rs:1635:14 clippy::doc_markdown "you should put `values_of_lossy` between ticks in the documentation"
ripgrep-12.1.1/crates/core/args.rs:1693:41 clippy::redundant_closure_for_method_calls "redundant closure found" ripgrep-12.1.1/crates/core/args.rs:1693:41 clippy::redundant_closure_for_method_calls "redundant closure found"
ripgrep-12.1.1/crates/core/args.rs:1770:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" ripgrep-12.1.1/crates/core/args.rs:1770:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
ripgrep-12.1.1/crates/core/args.rs:1829:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
ripgrep-12.1.1/crates/core/args.rs:287:13 clippy::similar_names "binding's name is too similar to existing binding" ripgrep-12.1.1/crates/core/args.rs:287:13 clippy::similar_names "binding's name is too similar to existing binding"
ripgrep-12.1.1/crates/core/args.rs:33:1 clippy::single_component_path_imports "this import is redundant" ripgrep-12.1.1/crates/core/args.rs:33:1 clippy::single_component_path_imports "this import is redundant"
ripgrep-12.1.1/crates/core/args.rs:34:1 clippy::single_component_path_imports "this import is redundant" ripgrep-12.1.1/crates/core/args.rs:34:1 clippy::single_component_path_imports "this import is redundant"
@ -3189,6 +3147,8 @@ ripgrep-12.1.1/crates/core/config.rs:58:6 clippy::type_complexity "very complex
ripgrep-12.1.1/crates/core/config.rs:79:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions" ripgrep-12.1.1/crates/core/config.rs:79:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions"
ripgrep-12.1.1/crates/core/logger.rs:11:30 clippy::doc_markdown "you should put `max_level` between ticks in the documentation" ripgrep-12.1.1/crates/core/logger.rs:11:30 clippy::doc_markdown "you should put `max_level` between ticks in the documentation"
ripgrep-12.1.1/crates/core/logger.rs:15:16 clippy::redundant_static_lifetimes "constants have by default a `'static` lifetime" ripgrep-12.1.1/crates/core/logger.rs:15:16 clippy::redundant_static_lifetimes "constants have by default a `'static` lifetime"
ripgrep-12.1.1/crates/core/main.rs:114:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
ripgrep-12.1.1/crates/core/main.rs:189:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
ripgrep-12.1.1/crates/core/main.rs:55:19 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" ripgrep-12.1.1/crates/core/main.rs:55:19 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
ripgrep-12.1.1/crates/core/main.rs:56:9 clippy::enum_glob_use "usage of wildcard import for enum variants" ripgrep-12.1.1/crates/core/main.rs:56:9 clippy::enum_glob_use "usage of wildcard import for enum variants"
ripgrep-12.1.1/crates/core/messages.rs:46:1 clippy::module_name_repetitions "item name ends with its containing module's name" ripgrep-12.1.1/crates/core/messages.rs:46:1 clippy::module_name_repetitions "item name ends with its containing module's name"
@ -3216,19 +3176,18 @@ ripgrep-12.1.1/crates/core/search.rs:533:36 clippy::cast_lossless "casting `u32`
ripgrep-12.1.1/crates/core/search.rs:533:5 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" ripgrep-12.1.1/crates/core/search.rs:533:5 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
ripgrep-12.1.1/crates/core/subject.rs:20:1 clippy::module_name_repetitions "item name starts with its containing module's name" ripgrep-12.1.1/crates/core/subject.rs:20:1 clippy::module_name_repetitions "item name starts with its containing module's name"
ripgrep-12.1.1/crates/core/subject.rs:4:1 clippy::single_component_path_imports "this import is redundant" ripgrep-12.1.1/crates/core/subject.rs:4:1 clippy::single_component_path_imports "this import is redundant"
serde-1.0.118/src/de/mod.rs:1592:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:1616:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:1627:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:1638:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:1649:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:952:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
serde-1.0.118/src/de/mod.rs:986:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
syn-1.0.54/build.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" syn-1.0.54/build.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata"
syn-1.0.54/build.rs:1:null clippy::multiple_crate_versions "could not read cargo metadata: `cargo metadata` exited with an error: Downloading crates ...\n Downloaded syn-test-suite v0.0.0\nerror: failed to verify the checksum of `syn-test-suite v0.0.0`"
syn-1.0.54/src/generics.rs:174:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
syn-1.0.54/src/lib.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" syn-1.0.54/src/lib.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata"
syn-1.0.54/src/lib.rs:1:null clippy::multiple_crate_versions "could not read cargo metadata: `cargo metadata` exited with an error: Downloading crates ...\n Downloaded syn-test-suite v0.0.0\nerror: failed to verify the checksum of `syn-test-suite v0.0.0`"
syn-1.0.54/src/lit.rs:1397:40 clippy::redundant_else "redundant else block" syn-1.0.54/src/lit.rs:1397:40 clippy::redundant_else "redundant else block"
syn-1.0.54/src/lit.rs:1405:28 clippy::redundant_else "redundant else block" syn-1.0.54/src/lit.rs:1405:28 clippy::redundant_else "redundant else block"
syn-1.0.54/src/lit.rs:1485:32 clippy::redundant_else "redundant else block" syn-1.0.54/src/lit.rs:1485:32 clippy::redundant_else "redundant else block"
syn-1.0.54/src/lit.rs:343:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
syn-1.0.54/src/lit.rs:437:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
syn-1.0.54/src/lit.rs:916:9 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
syn-1.0.54/src/token.rs:974:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
syn-1.0.54/src/token.rs:996:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
unicode-xid-0.2.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `unicode-xid` is missing `package.categories` metadata" unicode-xid-0.2.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `unicode-xid` is missing `package.categories` metadata"
unicode-xid-0.2.1/src/lib.rs:56:11 clippy::upper_case_acronyms "name `UnicodeXID` contains a capitalized acronym" unicode-xid-0.2.1/src/lib.rs:56:11 clippy::upper_case_acronyms "name `UnicodeXID` contains a capitalized acronym"
unicode-xid-0.2.1/src/lib.rs:57:64 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" unicode-xid-0.2.1/src/lib.rs:57:64 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation"
@ -3248,7 +3207,6 @@ xsv-0.13.0/src/cmd/cat.rs:7:16 clippy::redundant_static_lifetimes "statics have
xsv-0.13.0/src/cmd/count.rs:32:9 clippy::similar_names "binding's name is too similar to existing binding" xsv-0.13.0/src/cmd/count.rs:32:9 clippy::similar_names "binding's name is too similar to existing binding"
xsv-0.13.0/src/cmd/count.rs:38:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" xsv-0.13.0/src/cmd/count.rs:38:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
xsv-0.13.0/src/cmd/count.rs:42:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" xsv-0.13.0/src/cmd/count.rs:42:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
xsv-0.13.0/src/cmd/count.rs:50:5 clippy::unit_arg "passing a unit value to a function"
xsv-0.13.0/src/cmd/count.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" xsv-0.13.0/src/cmd/count.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime"
xsv-0.13.0/src/cmd/fixlengths.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding" xsv-0.13.0/src/cmd/fixlengths.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding"
xsv-0.13.0/src/cmd/fixlengths.rs:50:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" xsv-0.13.0/src/cmd/fixlengths.rs:50:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@ -3324,7 +3282,7 @@ xsv-0.13.0/src/cmd/sort.rs:48:9 clippy::similar_names "binding's name is too sim
xsv-0.13.0/src/cmd/sort.rs:91:14 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" xsv-0.13.0/src/cmd/sort.rs:91:14 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods"
xsv-0.13.0/src/cmd/split.rs:14:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" xsv-0.13.0/src/cmd/split.rs:14:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime"
xsv-0.13.0/src/cmd/split.rs:61:9 clippy::similar_names "binding's name is too similar to existing binding" xsv-0.13.0/src/cmd/split.rs:61:9 clippy::similar_names "binding's name is too similar to existing binding"
xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
xsv-0.13.0/src/cmd/split.rs:96:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" xsv-0.13.0/src/cmd/split.rs:96:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
xsv-0.13.0/src/cmd/split.rs:99:13 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" xsv-0.13.0/src/cmd/split.rs:99:13 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
xsv-0.13.0/src/cmd/stats.rs:110:36 clippy::redundant_closure_for_method_calls "redundant closure found" xsv-0.13.0/src/cmd/stats.rs:110:36 clippy::redundant_closure_for_method_calls "redundant closure found"
@ -3438,14 +3396,12 @@ clippy::redundant_slicing 1
clippy::same_item_push 1 clippy::same_item_push 1
clippy::should_implement_trait 1 clippy::should_implement_trait 1
clippy::stable_sort_primitive 1 clippy::stable_sort_primitive 1
clippy::unit_arg 1
clippy::unnecessary_lazy_evaluations 1 clippy::unnecessary_lazy_evaluations 1
clippy::unsafe_derive_deserialize 1 clippy::unsafe_derive_deserialize 1
clippy::used_underscore_binding 1 clippy::used_underscore_binding 1
clippy::verbose_bit_mask 1 clippy::verbose_bit_mask 1
clippy::while_let_on_iterator 1 clippy::while_let_on_iterator 1
clippy::comparison_to_empty 2 clippy::comparison_to_empty 2
clippy::derive_hash_xor_eq 2
clippy::expl_impl_clone_on_copy 2 clippy::expl_impl_clone_on_copy 2
clippy::filter_map 2 clippy::filter_map 2
clippy::len_zero 2 clippy::len_zero 2
@ -3463,10 +3419,10 @@ clippy::write_with_newline 2
clippy::filter_map_next 3 clippy::filter_map_next 3
clippy::fn_params_excessive_bools 3 clippy::fn_params_excessive_bools 3
clippy::if_same_then_else 3 clippy::if_same_then_else 3
clippy::inconsistent_struct_constructor 3
clippy::mut_mut 3 clippy::mut_mut 3
clippy::ptr_arg 3 clippy::ptr_arg 3
clippy::zero_ptr 3 clippy::zero_ptr 3
clippy::let_underscore_drop 4
clippy::too_many_arguments 4 clippy::too_many_arguments 4
clippy::explicit_iter_loop 5 clippy::explicit_iter_loop 5
clippy::field_reassign_with_default 5 clippy::field_reassign_with_default 5
@ -3488,11 +3444,11 @@ clippy::range_plus_one 7
clippy::invalid_upcast_comparisons 8 clippy::invalid_upcast_comparisons 8
clippy::needless_question_mark 8 clippy::needless_question_mark 8
clippy::wrong_self_convention 8 clippy::wrong_self_convention 8
clippy::multiple_crate_versions 9
clippy::manual_range_contains 10 clippy::manual_range_contains 10
clippy::match_wildcard_for_single_variants 10 clippy::match_wildcard_for_single_variants 10
clippy::missing_safety_doc 10 clippy::missing_safety_doc 10
clippy::needless_doctest_main 10 clippy::needless_doctest_main 10
clippy::multiple_crate_versions 11
clippy::needless_lifetimes 12 clippy::needless_lifetimes 12
clippy::cargo_common_metadata 13 clippy::cargo_common_metadata 13
clippy::shadow_unrelated 13 clippy::shadow_unrelated 13
@ -3511,6 +3467,7 @@ clippy::struct_excessive_bools 20
clippy::redundant_static_lifetimes 21 clippy::redundant_static_lifetimes 21
clippy::default_trait_access 22 clippy::default_trait_access 22
clippy::cast_lossless 23 clippy::cast_lossless 23
clippy::let_underscore_drop 23
clippy::trivially_copy_pass_by_ref 26 clippy::trivially_copy_pass_by_ref 26
clippy::redundant_else 29 clippy::redundant_else 29
clippy::too_many_lines 32 clippy::too_many_lines 32
@ -3519,11 +3476,11 @@ clippy::enum_glob_use 40
clippy::unseparated_literal_suffix 41 clippy::unseparated_literal_suffix 41
clippy::cast_precision_loss 44 clippy::cast_precision_loss 44
clippy::single_match_else 45 clippy::single_match_else 45
clippy::missing_panics_doc 54
clippy::inline_always 59 clippy::inline_always 59
clippy::match_same_arms 65 clippy::match_same_arms 60
clippy::similar_names 78 clippy::similar_names 78
clippy::cast_possible_truncation 95 clippy::cast_possible_truncation 95
clippy::missing_panics_doc 108
clippy::redundant_field_names 111 clippy::redundant_field_names 111
clippy::redundant_closure_for_method_calls 135 clippy::redundant_closure_for_method_calls 135
clippy::items_after_statements 139 clippy::items_after_statements 139
@ -3533,3 +3490,4 @@ clippy::doc_markdown 178
clippy::missing_errors_doc 343 clippy::missing_errors_doc 343
clippy::unreadable_literal 365 clippy::unreadable_literal 365
clippy::must_use_candidate 565 clippy::must_use_candidate 565
ICEs:

View file

@ -66,8 +66,8 @@ fn third_party_crates() -> String {
fn default_config() -> compiletest::Config { fn default_config() -> compiletest::Config {
let mut config = compiletest::Config::default(); let mut config = compiletest::Config::default();
if let Ok(name) = env::var("TESTNAME") { if let Ok(filters) = env::var("TESTNAME") {
config.filter = Some(name); config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
} }
if let Some(path) = option_env!("RUSTC_LIB_PATH") { if let Some(path) = option_env!("RUSTC_LIB_PATH") {
@ -167,7 +167,7 @@ fn run_ui_toml(config: &mut compiletest::Config) {
fn run_ui_cargo(config: &mut compiletest::Config) { fn run_ui_cargo(config: &mut compiletest::Config) {
fn run_tests( fn run_tests(
config: &compiletest::Config, config: &compiletest::Config,
filter: &Option<String>, filters: &[String],
mut tests: Vec<tester::TestDescAndFn>, mut tests: Vec<tester::TestDescAndFn>,
) -> Result<bool, io::Error> { ) -> Result<bool, io::Error> {
let mut result = true; let mut result = true;
@ -181,9 +181,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
// Use the filter if provided // Use the filter if provided
let dir_path = dir.path(); let dir_path = dir.path();
match &filter { for filter in filters {
Some(name) if !dir_path.ends_with(name) => continue, if !dir_path.ends_with(filter) {
_ => {}, continue;
}
} }
for case in fs::read_dir(&dir_path)? { for case in fs::read_dir(&dir_path)? {
@ -243,8 +244,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
let current_dir = env::current_dir().unwrap(); let current_dir = env::current_dir().unwrap();
let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default();
let filter = env::var("TESTNAME").ok(); let res = run_tests(&config, &config.filters, tests);
let res = run_tests(&config, &filter, tests);
env::set_current_dir(current_dir).unwrap(); env::set_current_dir(current_dir).unwrap();
set_var("CLIPPY_CONF_DIR", conf_dir); set_var("CLIPPY_CONF_DIR", conf_dir);

View file

@ -41,3 +41,15 @@ pub fn derive_foo(_input: TokenStream) -> TokenStream {
} }
} }
} }
#[proc_macro_derive(StructAUseSelf)]
pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream {
quote! {
struct A;
impl A {
fn new() -> A {
A
}
}
}
}

View file

@ -44,4 +44,13 @@ fn macro_in_closure() {
} }
} }
fn main() {} #[rustfmt::skip]
fn main() {
let mut range = 0..10;
range.all(|i| {i < 10} );
let v = vec![1, 2, 3];
if v.into_iter().any(|x| {x == 4}) {
println!("contains 4!");
}
}

View file

@ -65,4 +65,13 @@ fn main() {
else { else {
println!("!") println!("!")
} }
if x == "hello" {
print!("Hello ");
} else {
#[cfg(not(roflol))]
if y == "world" {
println!("world!")
}
}
} }

View file

@ -79,4 +79,13 @@ fn main() {
println!("!") println!("!")
} }
} }
if x == "hello" {
print!("Hello ");
} else {
#[cfg(not(roflol))]
if y == "world" {
println!("world!")
}
}
} }

View file

@ -138,4 +138,11 @@ fn main() {
// Fix #5962 // Fix #5962
if matches!(true, true) && matches!(true, true) {} if matches!(true, true) && matches!(true, true) {}
if true {
#[cfg(not(teehee))]
if true {
println!("Hello world!");
}
}
} }

View file

@ -154,4 +154,11 @@ fn main() {
if matches!(true, true) { if matches!(true, true) {
if matches!(true, true) {} if matches!(true, true) {}
} }
if true {
#[cfg(not(teehee))]
if true {
println!("Hello world!");
}
}
} }

View file

@ -232,6 +232,14 @@ fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u
}; };
} }
} }
let _: &dyn std::any::Any = match &Some(Some(1)) {
Some(e) => match e {
Some(e) => e,
e => e,
},
// else branch looks the same but the binding is different
e => e,
};
} }
fn make<T>() -> T { fn make<T>() -> T {

View file

@ -1,4 +1,4 @@
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:7:20 --> $DIR/collapsible_match.rs:7:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
@ -9,15 +9,15 @@ LL | | },
| |_________^ | |_________^
| |
= note: `-D clippy::collapsible-match` implied by `-D warnings` = note: `-D clippy::collapsible-match` implied by `-D warnings`
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:7:12 --> $DIR/collapsible_match.rs:7:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:16:20 --> $DIR/collapsible_match.rs:16:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
@ -27,15 +27,15 @@ LL | | _ => return,
LL | | }, LL | | },
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:16:12 --> $DIR/collapsible_match.rs:16:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:25:9 --> $DIR/collapsible_match.rs:25:9
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
@ -43,15 +43,15 @@ LL | | take(n);
LL | | } LL | | }
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:24:15 --> $DIR/collapsible_match.rs:24:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ Replace this binding | ^^^ replace this binding
LL | if let Some(n) = val { LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:32:9 --> $DIR/collapsible_match.rs:32:9
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
@ -61,15 +61,15 @@ LL | | return;
LL | | } LL | | }
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:31:15 --> $DIR/collapsible_match.rs:31:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ Replace this binding | ^^^ replace this binding
LL | if let Some(n) = val { LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:43:9 --> $DIR/collapsible_match.rs:43:9
| |
LL | / match val { LL | / match val {
@ -78,16 +78,16 @@ LL | | _ => (),
LL | | } LL | | }
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:42:15 --> $DIR/collapsible_match.rs:42:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ Replace this binding | ^^^ replace this binding
LL | match val { LL | match val {
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:52:13 --> $DIR/collapsible_match.rs:52:13
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
@ -95,15 +95,15 @@ LL | | take(n);
LL | | } LL | | }
| |_____________^ | |_____________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:51:12 --> $DIR/collapsible_match.rs:51:12
| |
LL | Ok(val) => { LL | Ok(val) => {
| ^^^ Replace this binding | ^^^ replace this binding
LL | if let Some(n) = val { LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:61:9 --> $DIR/collapsible_match.rs:61:9
| |
LL | / match val { LL | / match val {
@ -112,16 +112,16 @@ LL | | _ => return,
LL | | } LL | | }
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:60:15 --> $DIR/collapsible_match.rs:60:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ Replace this binding | ^^^ replace this binding
LL | match val { LL | match val {
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:72:13 --> $DIR/collapsible_match.rs:72:13
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
@ -131,15 +131,15 @@ LL | | return;
LL | | } LL | | }
| |_____________^ | |_____________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:71:12 --> $DIR/collapsible_match.rs:71:12
| |
LL | Ok(val) => { LL | Ok(val) => {
| ^^^ Replace this binding | ^^^ replace this binding
LL | if let Some(n) = val { LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:83:20 --> $DIR/collapsible_match.rs:83:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
@ -149,15 +149,15 @@ LL | | None => return,
LL | | }, LL | | },
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:83:12 --> $DIR/collapsible_match.rs:83:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match.rs:92:22 --> $DIR/collapsible_match.rs:92:22
| |
LL | Some(val) => match val { LL | Some(val) => match val {
@ -167,11 +167,11 @@ LL | | _ => return,
LL | | }, LL | | },
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match.rs:92:14 --> $DIR/collapsible_match.rs:92:14
| |
LL | Some(val) => match val { LL | Some(val) => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern

View file

@ -1,4 +1,4 @@
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match2.rs:8:34 --> $DIR/collapsible_match2.rs:8:34
| |
LL | Ok(val) if make() => match val { LL | Ok(val) if make() => match val {
@ -9,15 +9,15 @@ LL | | },
| |_____________^ | |_____________^
| |
= note: `-D clippy::collapsible-match` implied by `-D warnings` = note: `-D clippy::collapsible-match` implied by `-D warnings`
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match2.rs:8:16 --> $DIR/collapsible_match2.rs:8:16
| |
LL | Ok(val) if make() => match val { LL | Ok(val) if make() => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match2.rs:15:24 --> $DIR/collapsible_match2.rs:15:24
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
@ -27,15 +27,15 @@ LL | | _ => return,
LL | | }, LL | | },
| |_____________^ | |_____________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match2.rs:15:16 --> $DIR/collapsible_match2.rs:15:16
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ Replace this binding | ^^^ replace this binding
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match2.rs:29:29 --> $DIR/collapsible_match2.rs:29:29
| |
LL | $pat => match $e { LL | $pat => match $e {
@ -48,16 +48,16 @@ LL | | },
LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
| ------------------------------------------------- in this macro invocation | ------------------------------------------------- in this macro invocation
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match2.rs:41:28 --> $DIR/collapsible_match2.rs:41:28
| |
LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
| ^^^ ^^^^^^^ with this pattern | ^^^ ^^^^^^^ with this pattern
| | | |
| Replace this binding | replace this binding
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match2.rs:46:20 --> $DIR/collapsible_match2.rs:46:20
| |
LL | Some(s) => match *s { LL | Some(s) => match *s {
@ -67,15 +67,15 @@ LL | | _ => (),
LL | | }, LL | | },
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match2.rs:46:14 --> $DIR/collapsible_match2.rs:46:14
| |
LL | Some(s) => match *s { LL | Some(s) => match *s {
| ^ Replace this binding | ^ replace this binding
LL | [n] => foo(n), LL | [n] => foo(n),
| ^^^ with this pattern | ^^^ with this pattern
error: Unnecessary nested match error: unnecessary nested match
--> $DIR/collapsible_match2.rs:55:24 --> $DIR/collapsible_match2.rs:55:24
| |
LL | Some(ref s) => match &*s { LL | Some(ref s) => match &*s {
@ -85,11 +85,11 @@ LL | | _ => (),
LL | | }, LL | | },
| |_________^ | |_________^
| |
help: The outer pattern can be modified to include the inner pattern. help: the outer pattern can be modified to include the inner pattern
--> $DIR/collapsible_match2.rs:55:14 --> $DIR/collapsible_match2.rs:55:14
| |
LL | Some(ref s) => match &*s { LL | Some(ref s) => match &*s {
| ^^^^^ Replace this binding | ^^^^^ replace this binding
LL | [n] => foo(n), LL | [n] => foo(n),
| ^^^ with this pattern | ^^^ with this pattern

View file

@ -0,0 +1,21 @@
//! This is a minimal reproducer for the ICE in https://github.com/rust-lang/rust-clippy/pull/6179.
//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
#![warn(clippy::use_self)]
#![allow(dead_code)]
struct Foo {}
impl Foo {
fn foo() -> Self {
impl Foo {
fn bar() {}
}
let _: _ = 1;
Self {}
}
}
fn main() {}

View file

@ -0,0 +1,135 @@
#![warn(clippy::default_numeric_fallback)]
#![allow(unused)]
#![allow(clippy::never_loop)]
#![allow(clippy::no_effect)]
#![allow(clippy::unnecessary_operation)]
mod basic_expr {
fn test() {
// Should lint unsuffixed literals typed `i32`.
let x = 22;
let x = [1, 2, 3];
let x = if true { (1, 2) } else { (3, 4) };
let x = match 1 {
1 => 1,
_ => 2,
};
// Should lint unsuffixed literals typed `f64`.
let x = 0.12;
// Should NOT lint suffixed literals.
let x = 22_i32;
let x = 0.12_f64;
// Should NOT lint literals in init expr if `Local` has a type annotation.
let x: f64 = 0.1;
let x: [i32; 3] = [1, 2, 3];
let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
let x: _ = 1;
}
}
mod nested_local {
fn test() {
let x: _ = {
// Should lint this because this literal is not bound to any types.
let y = 1;
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
1
};
let x: _ = if true {
// Should lint this because this literal is not bound to any types.
let y = 1;
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
1
} else {
// Should lint this because this literal is not bound to any types.
let y = 1;
// Should NOT lint this because this literal is bound to `_` of outer `Local`.
2
};
}
}
mod function_def {
fn ret_i32() -> i32 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1
}
fn test() {
// Should lint this because return type is inferred to `i32` and NOT bound to a concrete
// type.
let f = || -> _ { 1 };
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
let f = || -> i32 { 1 };
}
}
mod function_calls {
fn concrete_arg(x: i32) {}
fn generic_arg<T>(t: T) {}
fn test() {
// Should NOT lint this because the argument type is bound to a concrete type.
concrete_arg(1);
// Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
generic_arg(1);
// Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
let x: _ = generic_arg(1);
}
}
mod struct_ctor {
struct ConcreteStruct {
x: i32,
}
struct GenericStruct<T> {
x: T,
}
fn test() {
// Should NOT lint this because the field type is bound to a concrete type.
ConcreteStruct { x: 1 };
// Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
GenericStruct { x: 1 };
// Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
let _ = GenericStruct { x: 1 };
}
}
mod method_calls {
struct StructForMethodCallTest {}
impl StructForMethodCallTest {
fn concrete_arg(&self, x: i32) {}
fn generic_arg<T>(&self, t: T) {}
}
fn test() {
let s = StructForMethodCallTest {};
// Should NOT lint this because the argument type is bound to a concrete type.
s.concrete_arg(1);
// Should lint this because the argument type is bound to a concrete type.
s.generic_arg(1);
}
}
fn main() {}

View file

@ -0,0 +1,148 @@
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:10:17
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
|
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:11:18
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:11:21
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:11:24
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:28
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:31
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:44
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:47
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `4_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:13:23
|
LL | let x = match 1 {
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:14:13
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:14:18
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:15:18
|
LL | _ => 2,
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:19:17
|
LL | let x = 0.12;
| ^^^^ help: consider adding suffix: `0.12_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:37:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:45:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:51:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:63:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:69:27
|
LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:73:29
|
LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:87:21
|
LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:90:32
|
LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:108:28
|
LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:111:36
|
LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:131:23
|
LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: aborting due to 24 previous errors

View file

@ -50,11 +50,23 @@ fn test_units() {
} }
/// This tests allowed identifiers. /// This tests allowed identifiers.
/// KiB MiB GiB TiB PiB EiB
/// DirectX /// DirectX
/// ECMAScript /// ECMAScript
/// GPLv2 GPLv3
/// GitHub GitLab
/// IPv4 IPv6
/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
/// NaN NaNs
/// OAuth GraphQL /// OAuth GraphQL
/// OCaml
/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
/// WebGL /// WebGL
/// TensorFlow
/// TrueType
/// iOS macOS
/// TeX LaTeX BibTeX BibLaTeX /// TeX LaTeX BibTeX BibLaTeX
/// MinGW
/// CamelCase (see also #2395) /// CamelCase (see also #2395)
/// be_sure_we_got_to_the_end_of_it /// be_sure_we_got_to_the_end_of_it
fn test_allowed() { fn test_allowed() {

View file

@ -55,133 +55,133 @@ LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:59:5 --> $DIR/doc.rs:71:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `link_with_underscores` between ticks in the documentation error: you should put `link_with_underscores` between ticks in the documentation
--> $DIR/doc.rs:63:22 --> $DIR/doc.rs:75:22
| |
LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: you should put `inline_link2` between ticks in the documentation error: you should put `inline_link2` between ticks in the documentation
--> $DIR/doc.rs:66:21 --> $DIR/doc.rs:78:21
| |
LL | /// It can also be [inline_link2]. LL | /// It can also be [inline_link2].
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:76:5 --> $DIR/doc.rs:88:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:84:8 --> $DIR/doc.rs:96:8
| |
LL | /// ## CamelCaseThing LL | /// ## CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:87:7 --> $DIR/doc.rs:99:7
| |
LL | /// # CamelCaseThing LL | /// # CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:89:22 --> $DIR/doc.rs:101:22
| |
LL | /// Not a title #897 CamelCaseThing LL | /// Not a title #897 CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:90:5 --> $DIR/doc.rs:102:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:97:5 --> $DIR/doc.rs:109:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:110:5 --> $DIR/doc.rs:122:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation error: you should put `FooBar` between ticks in the documentation
--> $DIR/doc.rs:121:43 --> $DIR/doc.rs:133:43
| |
LL | /** E.g., serialization of an empty list: FooBar LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^ | ^^^^^^
error: you should put `BarQuz` between ticks in the documentation error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:126:5 --> $DIR/doc.rs:138:5
| |
LL | And BarQuz too. LL | And BarQuz too.
| ^^^^^^ | ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:127:1 --> $DIR/doc.rs:139:1
| |
LL | be_sure_we_got_to_the_end_of_it LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation error: you should put `FooBar` between ticks in the documentation
--> $DIR/doc.rs:132:43 --> $DIR/doc.rs:144:43
| |
LL | /** E.g., serialization of an empty list: FooBar LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^ | ^^^^^^
error: you should put `BarQuz` between ticks in the documentation error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:137:5
|
LL | And BarQuz too.
| ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:138:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:149:5 --> $DIR/doc.rs:149:5
| |
LL | And BarQuz too.
| ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:150:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:161:5
|
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:176:13 --> $DIR/doc.rs:188:13
| |
LL | /// Not ok: http://www.unicode.org LL | /// Not ok: http://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:177:13 --> $DIR/doc.rs:189:13
| |
LL | /// Not ok: https://www.unicode.org LL | /// Not ok: https://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:178:13 --> $DIR/doc.rs:190:13
| |
LL | /// Not ok: http://www.unicode.org/ LL | /// Not ok: http://www.unicode.org/
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:179:13 --> $DIR/doc.rs:191:13
| |
LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `mycrate::Collection` between ticks in the documentation error: you should put `mycrate::Collection` between ticks in the documentation
--> $DIR/doc.rs:182:22 --> $DIR/doc.rs:194:22
| |
LL | /// An iterator over mycrate::Collection's values. LL | /// An iterator over mycrate::Collection's values.
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^

View file

@ -28,6 +28,15 @@ pub fn inner_body(opt: Option<u32>) {
}); });
} }
/// This needs to be documented
pub fn unreachable_and_panic() {
if true {
unreachable!()
} else {
panic!()
}
}
/// This is documented /// This is documented
/// ///
/// # Panics /// # Panics
@ -69,6 +78,19 @@ pub fn todo_documented() {
todo!() todo!()
} }
/// This is documented
///
/// # Panics
///
/// We still need to do this part
pub fn unreachable_amd_panic_documented() {
if true {
unreachable!()
} else {
panic!()
}
}
/// This is okay because it is private /// This is okay because it is private
fn unwrap_private() { fn unwrap_private() {
let result = Err("Hi"); let result = Err("Hi");
@ -93,3 +115,8 @@ fn inner_body_private(opt: Option<u32>) {
} }
}); });
} }
/// This is okay because unreachable
pub fn unreachable() {
unreachable!("This function panics")
}

View file

@ -63,5 +63,24 @@ LL | panic!()
| ^^^^^^^^ | ^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors error: docs for function which may panic missing `# Panics` section
--> $DIR/doc_panics.rs:32:1
|
LL | / pub fn unreachable_and_panic() {
LL | | if true {
LL | | unreachable!()
LL | | } else {
LL | | panic!()
LL | | }
LL | | }
| |_^
|
note: first possible panic found here
--> $DIR/doc_panics.rs:36:9
|
LL | panic!()
| ^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 5 previous errors

View file

@ -133,4 +133,17 @@ pub enum NetworkLayer {
Layer3, Layer3,
} }
// should lint suggesting `IData`, not only `Data` (see #4639)
enum IDataRequest {
PutIData(String),
GetIData(String),
DeleteUnpubIData(String),
}
enum HIDataRequest {
PutHIData(String),
GetHIData(String),
DeleteUnpubHIData(String),
}
fn main() {} fn main() {}

View file

@ -97,5 +97,29 @@ LL | | }
= note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings`
= help: remove the prefixes and use full paths to the variants instead of glob imports = help: remove the prefixes and use full paths to the variants instead of glob imports
error: aborting due to 10 previous errors error: all variants have the same postfix: `IData`
--> $DIR/enum_variants.rs:137:1
|
LL | / enum IDataRequest {
LL | | PutIData(String),
LL | | GetIData(String),
LL | | DeleteUnpubIData(String),
LL | | }
| |_^
|
= help: remove the postfixes and use full paths to the variants instead of glob imports
error: all variants have the same postfix: `HIData`
--> $DIR/enum_variants.rs:143:1
|
LL | / enum HIDataRequest {
LL | | PutHIData(String),
LL | | GetHIData(String),
LL | | DeleteUnpubHIData(String),
LL | | }
| |_^
|
= help: remove the postfixes and use full paths to the variants instead of glob imports
error: aborting due to 12 previous errors

View file

@ -0,0 +1,52 @@
#![warn(clippy::from_str_radix_10)]
mod some_mod {
// fake function that shouldn't trigger the lint
pub fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
unimplemented!()
}
}
// fake function that shouldn't trigger the lint
fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
unimplemented!()
}
// to test parenthesis addition
struct Test;
impl std::ops::Add<Test> for Test {
type Output = &'static str;
fn add(self, _: Self) -> Self::Output {
"304"
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// all of these should trigger the lint
u32::from_str_radix("30", 10)?;
i64::from_str_radix("24", 10)?;
isize::from_str_radix("100", 10)?;
u8::from_str_radix("7", 10)?;
u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
i128::from_str_radix(Test + Test, 10)?;
let string = "300";
i32::from_str_radix(string, 10)?;
let stringier = "400".to_string();
i32::from_str_radix(&stringier, 10)?;
// none of these should trigger the lint
u16::from_str_radix("20", 3)?;
i32::from_str_radix("45", 12)?;
usize::from_str_radix("10", 16)?;
i128::from_str_radix("10", 13)?;
some_mod::from_str_radix("50", 10)?;
some_mod::from_str_radix("50", 6)?;
from_str_radix("50", 10)?;
from_str_radix("50", 6)?;
Ok(())
}

View file

@ -0,0 +1,52 @@
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:28:5
|
LL | u32::from_str_radix("30", 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
|
= note: `-D clippy::from-str-radix-10` implied by `-D warnings`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:29:5
|
LL | i64::from_str_radix("24", 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:30:5
|
LL | isize::from_str_radix("100", 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:31:5
|
LL | u8::from_str_radix("7", 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:32:5
|
LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::<u16>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:33:5
|
LL | i128::from_str_radix(Test + Test, 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:36:5
|
LL | i32::from_str_radix(string, 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
--> $DIR/from_str_radix_10.rs:39:5
|
LL | i32::from_str_radix(&stringier, 10)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
error: aborting due to 8 previous errors

View file

@ -12,7 +12,7 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
if true { if true {
for _ in &[42] { for _ in &[42] {
let foo: &Option<_> = &Some::<u8>(42); let foo: &Option<_> = &Some::<u8>(42);
if true { if foo.is_some() {
break; break;
} else { } else {
continue; continue;
@ -21,8 +21,8 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
} else { } else {
//~ ERROR same body as `if` block //~ ERROR same body as `if` block
for _ in &[42] { for _ in &[42] {
let foo: &Option<_> = &Some::<u8>(42); let bar: &Option<_> = &Some::<u8>(42);
if true { if bar.is_some() {
break; break;
} else { } else {
continue; continue;

View file

@ -5,7 +5,7 @@ LL | } else {
| ____________^ | ____________^
LL | | //~ ERROR same body as `if` block LL | | //~ ERROR same body as `if` block
LL | | for _ in &[42] { LL | | for _ in &[42] {
LL | | let foo: &Option<_> = &Some::<u8>(42); LL | | let bar: &Option<_> = &Some::<u8>(42);
... | ... |
LL | | } LL | | }
LL | | } LL | | }
@ -19,7 +19,7 @@ LL | if true {
| _____________^ | _____________^
LL | | for _ in &[42] { LL | | for _ in &[42] {
LL | | let foo: &Option<_> = &Some::<u8>(42); LL | | let foo: &Option<_> = &Some::<u8>(42);
LL | | if true { LL | | if foo.is_some() {
... | ... |
LL | | } LL | | }
LL | | } else { LL | | } else {

View file

@ -0,0 +1,61 @@
// run-rustfix
// edition:2018
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::no_effect)]
#![allow(dead_code)]
#[derive(Default)]
struct Foo {
x: i32,
y: i32,
z: i32,
}
mod without_base {
use super::Foo;
fn test() {
let x = 1;
let y = 1;
let z = 1;
// Should lint.
Foo { x, y, z };
// Shoule NOT lint because the order is the same as in the definition.
Foo { x, y, z };
// Should NOT lint because z is not a shorthand init.
Foo { y, x, z: z };
}
}
mod with_base {
use super::Foo;
fn test() {
let x = 1;
let z = 1;
// Should lint.
Foo { x, z, ..Default::default() };
// Should NOT lint because the order is consistent with the definition.
Foo {
x,
z,
..Default::default()
};
// Should NOT lint because z is not a shorthand init.
Foo {
z: z,
x,
..Default::default()
};
}
}
fn main() {}

View file

@ -0,0 +1,65 @@
// run-rustfix
// edition:2018
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::no_effect)]
#![allow(dead_code)]
#[derive(Default)]
struct Foo {
x: i32,
y: i32,
z: i32,
}
mod without_base {
use super::Foo;
fn test() {
let x = 1;
let y = 1;
let z = 1;
// Should lint.
Foo { y, x, z };
// Shoule NOT lint because the order is the same as in the definition.
Foo { x, y, z };
// Should NOT lint because z is not a shorthand init.
Foo { y, x, z: z };
}
}
mod with_base {
use super::Foo;
fn test() {
let x = 1;
let z = 1;
// Should lint.
Foo {
z,
x,
..Default::default()
};
// Should NOT lint because the order is consistent with the definition.
Foo {
x,
z,
..Default::default()
};
// Should NOT lint because z is not a shorthand init.
Foo {
z: z,
x,
..Default::default()
};
}
}
fn main() {}

View file

@ -0,0 +1,20 @@
error: inconsistent struct constructor
--> $DIR/inconsistent_struct_constructor.rs:25:9
|
LL | Foo { y, x, z };
| ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
|
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
error: inconsistent struct constructor
--> $DIR/inconsistent_struct_constructor.rs:43:9
|
LL | / Foo {
LL | | z,
LL | | x,
LL | | ..Default::default()
LL | | };
| |_________^ help: try: `Foo { x, z, ..Default::default() }`
error: aborting due to 2 previous errors

View file

@ -14,6 +14,7 @@ struct C;
struct D; struct D;
struct E; struct E;
struct F; struct F;
struct G;
impl A { impl A {
// Should be detected; emit warning // Should be detected; emit warning
@ -73,6 +74,13 @@ impl F {
} }
} }
impl G {
// Should not be detected, as it does not match the function signature
fn to_string<const _N: usize>(&self) -> String {
"G.to_string()".to_string()
}
}
fn main() { fn main() {
let a = A; let a = A;
a.to_string(); a.to_string();
@ -93,4 +101,7 @@ fn main() {
let f = F; let f = F;
f.to_string(1); f.to_string(1);
let g = G;
g.to_string::<1>();
} }

View file

@ -1,5 +1,5 @@
error: implementation of inherent method `to_string(&self) -> String` for type `A` error: implementation of inherent method `to_string(&self) -> String` for type `A`
--> $DIR/inherent_to_string.rs:20:5 --> $DIR/inherent_to_string.rs:21:5
| |
LL | / fn to_string(&self) -> String { LL | / fn to_string(&self) -> String {
LL | | "A.to_string()".to_string() LL | | "A.to_string()".to_string()
@ -10,7 +10,7 @@ LL | | }
= help: implement trait `Display` for type `A` instead = help: implement trait `Display` for type `A` instead
error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
--> $DIR/inherent_to_string.rs:44:5 --> $DIR/inherent_to_string.rs:45:5
| |
LL | / fn to_string(&self) -> String { LL | / fn to_string(&self) -> String {
LL | | "C.to_string()".to_string() LL | | "C.to_string()".to_string()

View file

@ -0,0 +1,70 @@
// run-rustfix
#![warn(clippy::manual_map)]
#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)]
fn main() {
Some(0).map(|_| 2);
Some(0).map(|x| x + 1);
Some("").map(|x| x.is_empty());
Some(0).map(|x| !x);
#[rustfmt::skip]
Some(0).map(std::convert::identity);
Some(&String::new()).map(|x| str::len(x));
match Some(0) {
Some(x) if false => Some(x + 1),
_ => None,
};
Some([0, 1]).as_ref().map(|x| x[0]);
Some(0).map(|x| x * 2);
Some(String::new()).as_ref().map(|x| x.is_empty());
Some(String::new()).as_ref().map(|x| x.len());
Some(0).map(|x| x + x);
#[warn(clippy::option_map_unit_fn)]
match &mut Some(String::new()) {
Some(x) => Some(x.push_str("")),
None => None,
};
#[allow(clippy::option_map_unit_fn)]
{
Some(String::new()).as_mut().map(|x| x.push_str(""));
}
Some(String::new()).as_ref().map(|x| x.len());
Some(String::new()).as_ref().map(|x| x.is_empty());
Some((0, 1, 2)).map(|(x, y, z)| x + y + z);
Some([1, 2, 3]).map(|[first, ..]| first);
Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x));
match Some((String::new(), 0)) {
Some((ref x, y)) => Some((y, x)),
None => None,
};
match Some(Some(0)) {
Some(Some(_)) | Some(None) => Some(0),
None => None,
};
match Some(Some((0, 1))) {
Some(Some((x, 1))) => Some(x),
_ => None,
};
}

View file

@ -0,0 +1,122 @@
// run-rustfix
#![warn(clippy::manual_map)]
#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)]
fn main() {
match Some(0) {
Some(_) => Some(2),
None::<u32> => None,
};
match Some(0) {
Some(x) => Some(x + 1),
_ => None,
};
match Some("") {
Some(x) => Some(x.is_empty()),
None => None,
};
if let Some(x) = Some(0) {
Some(!x)
} else {
None
};
#[rustfmt::skip]
match Some(0) {
Some(x) => { Some(std::convert::identity(x)) }
None => { None }
};
match Some(&String::new()) {
Some(x) => Some(str::len(x)),
None => None,
};
match Some(0) {
Some(x) if false => Some(x + 1),
_ => None,
};
match &Some([0, 1]) {
Some(x) => Some(x[0]),
&None => None,
};
match &Some(0) {
&Some(x) => Some(x * 2),
None => None,
};
match Some(String::new()) {
Some(ref x) => Some(x.is_empty()),
_ => None,
};
match &&Some(String::new()) {
Some(x) => Some(x.len()),
_ => None,
};
match &&Some(0) {
&&Some(x) => Some(x + x),
&&_ => None,
};
#[warn(clippy::option_map_unit_fn)]
match &mut Some(String::new()) {
Some(x) => Some(x.push_str("")),
None => None,
};
#[allow(clippy::option_map_unit_fn)]
{
match &mut Some(String::new()) {
Some(x) => Some(x.push_str("")),
None => None,
};
}
match &mut Some(String::new()) {
Some(ref x) => Some(x.len()),
None => None,
};
match &mut &Some(String::new()) {
Some(x) => Some(x.is_empty()),
&mut _ => None,
};
match Some((0, 1, 2)) {
Some((x, y, z)) => Some(x + y + z),
None => None,
};
match Some([1, 2, 3]) {
Some([first, ..]) => Some(first),
None => None,
};
match &Some((String::new(), "test")) {
Some((x, y)) => Some((y, x)),
None => None,
};
match Some((String::new(), 0)) {
Some((ref x, y)) => Some((y, x)),
None => None,
};
match Some(Some(0)) {
Some(Some(_)) | Some(None) => Some(0),
None => None,
};
match Some(Some((0, 1))) {
Some(Some((x, 1))) => Some(x),
_ => None,
};
}

View file

@ -0,0 +1,158 @@
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:7:5
|
LL | / match Some(0) {
LL | | Some(_) => Some(2),
LL | | None::<u32> => None,
LL | | };
| |_____^ help: try this: `Some(0).map(|_| 2)`
|
= note: `-D clippy::manual-map` implied by `-D warnings`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:12:5
|
LL | / match Some(0) {
LL | | Some(x) => Some(x + 1),
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(0).map(|x| x + 1)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:17:5
|
LL | / match Some("") {
LL | | Some(x) => Some(x.is_empty()),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some("").map(|x| x.is_empty())`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:22:5
|
LL | / if let Some(x) = Some(0) {
LL | | Some(!x)
LL | | } else {
LL | | None
LL | | };
| |_____^ help: try this: `Some(0).map(|x| !x)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:29:5
|
LL | / match Some(0) {
LL | | Some(x) => { Some(std::convert::identity(x)) }
LL | | None => { None }
LL | | };
| |_____^ help: try this: `Some(0).map(std::convert::identity)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:34:5
|
LL | / match Some(&String::new()) {
LL | | Some(x) => Some(str::len(x)),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:44:5
|
LL | / match &Some([0, 1]) {
LL | | Some(x) => Some(x[0]),
LL | | &None => None,
LL | | };
| |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:49:5
|
LL | / match &Some(0) {
LL | | &Some(x) => Some(x * 2),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(0).map(|x| x * 2)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:54:5
|
LL | / match Some(String::new()) {
LL | | Some(ref x) => Some(x.is_empty()),
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:59:5
|
LL | / match &&Some(String::new()) {
LL | | Some(x) => Some(x.len()),
LL | | _ => None,
LL | | };
| |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:64:5
|
LL | / match &&Some(0) {
LL | | &&Some(x) => Some(x + x),
LL | | &&_ => None,
LL | | };
| |_____^ help: try this: `Some(0).map(|x| x + x)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:77:9
|
LL | / match &mut Some(String::new()) {
LL | | Some(x) => Some(x.push_str("")),
LL | | None => None,
LL | | };
| |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:83:5
|
LL | / match &mut Some(String::new()) {
LL | | Some(ref x) => Some(x.len()),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:88:5
|
LL | / match &mut &Some(String::new()) {
LL | | Some(x) => Some(x.is_empty()),
LL | | &mut _ => None,
LL | | };
| |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:93:5
|
LL | / match Some((0, 1, 2)) {
LL | | Some((x, y, z)) => Some(x + y + z),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:98:5
|
LL | / match Some([1, 2, 3]) {
LL | | Some([first, ..]) => Some(first),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)`
error: manual implementation of `Option::map`
--> $DIR/manual_map_option.rs:103:5
|
LL | / match &Some((String::new(), "test")) {
LL | | Some((x, y)) => Some((y, x)),
LL | | None => None,
LL | | };
| |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))`
error: aborting due to 17 previous errors

View file

@ -1,6 +1,4 @@
#![allow(clippy::unnecessary_wraps)] #![warn(clippy::result_unit_err)]
#[warn(clippy::result_unit_err)]
#[allow(unused)]
pub fn returns_unit_error() -> Result<u32, ()> { pub fn returns_unit_error() -> Result<u32, ()> {
Err(()) Err(())
@ -36,4 +34,23 @@ impl UnitErrorHolder {
} }
} }
// https://github.com/rust-lang/rust-clippy/issues/6546
pub mod issue_6546 {
type ResInv<A, B> = Result<B, A>;
pub fn should_lint() -> ResInv<(), usize> {
Ok(0)
}
pub fn should_not_lint() -> ResInv<usize, ()> {
Ok(())
}
type MyRes<A, B> = Result<(A, B), Box<dyn std::error::Error>>;
pub fn should_not_lint2(x: i32) -> MyRes<i32, ()> {
Ok((x, ()))
}
}
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error: this returns a `Result<_, ()> error: this returns a `Result<_, ()>
--> $DIR/result_unit_error.rs:5:1 --> $DIR/result_unit_error.rs:3:1
| |
LL | pub fn returns_unit_error() -> Result<u32, ()> { LL | pub fn returns_unit_error() -> Result<u32, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result<u32, ()> {
= help: use a custom Error type instead = help: use a custom Error type instead
error: this returns a `Result<_, ()> error: this returns a `Result<_, ()>
--> $DIR/result_unit_error.rs:14:5 --> $DIR/result_unit_error.rs:12:5
| |
LL | fn get_that_error(&self) -> Result<bool, ()>; LL | fn get_that_error(&self) -> Result<bool, ()>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -16,7 +16,7 @@ LL | fn get_that_error(&self) -> Result<bool, ()>;
= help: use a custom Error type instead = help: use a custom Error type instead
error: this returns a `Result<_, ()> error: this returns a `Result<_, ()>
--> $DIR/result_unit_error.rs:16:5 --> $DIR/result_unit_error.rs:14:5
| |
LL | fn get_this_one_too(&self) -> Result<bool, ()> { LL | fn get_this_one_too(&self) -> Result<bool, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -24,12 +24,20 @@ LL | fn get_this_one_too(&self) -> Result<bool, ()> {
= help: use a custom Error type instead = help: use a custom Error type instead
error: this returns a `Result<_, ()> error: this returns a `Result<_, ()>
--> $DIR/result_unit_error.rs:34:5 --> $DIR/result_unit_error.rs:32:5
| |
LL | pub fn unit_error(&self) -> Result<usize, ()> { LL | pub fn unit_error(&self) -> Result<usize, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: use a custom Error type instead = help: use a custom Error type instead
error: aborting due to 4 previous errors error: this returns a `Result<_, ()>
--> $DIR/result_unit_error.rs:41:5
|
LL | pub fn should_lint() -> ResInv<(), usize> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a custom Error type instead
error: aborting due to 5 previous errors

View file

@ -1,5 +1,4 @@
#![warn(clippy::temporary_assignment)] #![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};

View file

@ -1,5 +1,5 @@
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5 --> $DIR/temporary_assignment.rs:47:5
| |
LL | Struct { field: 0 }.field = 1; LL | Struct { field: 0 }.field = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1;
= note: `-D clippy::temporary-assignment` implied by `-D warnings` = note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5 --> $DIR/temporary_assignment.rs:48:5
| |
LL | / MultiStruct { LL | / MultiStruct {
LL | | structure: Struct { field: 0 }, LL | | structure: Struct { field: 0 },
@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^ | |______________^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5 --> $DIR/temporary_assignment.rs:53:5
| |
LL | ArrayStruct { array: [0] }.array[0] = 1; LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5 --> $DIR/temporary_assignment.rs:54:5
| |
LL | (0, 0).0 = 1; LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^

Some files were not shown because too many files have changed in this diff Show more