Merge commit '61eb38aeda6cb54b93b872bf503d70084c4d621c' into clippyup

This commit is contained in:
flip1995 2021-07-01 18:17:38 +02:00
parent abc9a46868
commit ebe52869a3
142 changed files with 3185 additions and 1086 deletions

View File

@ -71,7 +71,7 @@ jobs:
working-directory: clippy_workspace_tests
- name: Test cargo-clippy --fix
run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
run: ../target/debug/cargo-clippy clippy --fix
working-directory: clippy_workspace_tests
- name: Test clippy-driver

View File

@ -90,11 +90,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v2.3.3
# FIXME: should not be necessary once 1.24.2 is the default version on the windows runner
- name: Update rustup
run: rustup self update
if: runner.os == 'Windows'
- name: Install toolchain
run: rustup show active-toolchain
@ -139,7 +134,7 @@ jobs:
working-directory: clippy_workspace_tests
- name: Test cargo-clippy --fix
run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
run: ../target/debug/cargo-clippy clippy --fix
working-directory: clippy_workspace_tests
- name: Test clippy-driver

View File

@ -22,7 +22,7 @@ jobs:
uses: actions/setup-node@v1.4.4
- name: Install remark
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
# Run
- name: Check *.md files

View File

@ -1,6 +1,7 @@
{
"plugins": [
"remark-preset-lint-recommended",
"remark-gfm",
["remark-lint-list-item-indent", false],
["remark-lint-no-literal-urls", false],
["remark-lint-no-shortcut-reference-link", false],

View File

@ -6,11 +6,139 @@ document.
## Unreleased / In Rust Nightly
[7c7683c...master](https://github.com/rust-lang/rust-clippy/compare/7c7683c...master)
[3ae8faf...master](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...master)
## Rust 1.54
Current beta, release 2021-07-29
[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
### New Lints
- [`ref_binding_to_reference`]
[#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
- [`needless_bitwise_bool`]
[#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
- [`manual_str_repeat`]
[#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
- [`suspicious_splitn`]
[#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
### Moves and Deprecations
- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
the new `avoid_breaking_exported_api` config option (see
[Enhancements](#1-54-enhancements))
[#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
- Move [`inconsistent_struct_constructor`] to `pedantic`
[#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
- Move [`needless_borrow`] to `style` (now warn-by-default)
[#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
- Move [`suspicious_operation_groupings`] to `nursery`
[#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
- Move [`semicolon_if_nothing_returned`] to `pedantic`
[#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
### Enhancements <a name="1-54-enhancements"></a>
- [`while_let_on_iterator`]: Now also lints in nested loops
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
[#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
- [`needless_collect`]: Now also lints on assignments with type annotations
[#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
- [`if_then_some_else_none`]: Now works with the MSRV config
[#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
- Add `avoid_breaking_exported_api` config option for the lints
[`enum_variant_names`], [`large_types_passed_by_value`],
[`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
[`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
this configuration option to `false` before a major release (1.0/2.0/...) to
clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
- [`needless_collect`]: Now lints on even more data structures
[#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
attributes as sufficient documentation
[#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
Now work as expected when used with `allow`
[#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
### False Positive Fixes
- [`implicit_return`]: Now takes all diverging functions in account to avoid
false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
and the struct is used in the loop
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
- [`multiple_inherent_impl`]: No longer lints with generic arguments
[#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
- [`comparison_chain`]: No longer lints in a `const` context
[#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
- [`while_immutable_condition`]: Fix false positive where mutation in the loop
variable wasn't picked up
[#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
- [`default_trait_access`]: No longer lints in macros
[#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
- [`needless_question_mark`]: No longer lints when the inner value is implicitly
dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
- [`unused_unit`]: No longer lints when multiple macro contexts are involved
[#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
- [`eval_order_dependence`]: Fix false positive in async context
[#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
- [`wrong_self_convention`]: No longer lints in trait implementations of
non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
- [`suboptimal_flops`]: No longer lints on `powi(2)`
[#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
[#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
- [`option_if_let_else`]: No longer lints on `else if let` pattern
[#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
- [`use_self`], [`useless_conversion`]: Fix false positives when generic
arguments are involved
[#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
- [`manual_unwrap_or`]: Fix false positive with deref coercion
[#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
- [`similar_names`]: No longer lints on `wparam`/`lparam`
[#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
### Suggestion Fixes/Improvements
- [`implicit_return`]
[#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
- Fix suggestion for async functions
- Improve suggestion with macros
- Suggest to change `break` to `return` when appropriate
- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
- [`match_single_binding`]: Improve suggestion when match scrutinee has side
effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
- [`needless_borrow`]: Now suggests to also change usage sites as needed
[#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
when a `<_ as Trait>::_` is involved
[#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
- [`not_unsafe_ptr_arg_deref`]: Improved error message
[#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
### ICE Fixes
- Fix ICE when running Clippy on `libstd`
[#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
- [`implicit_return`]
[#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
## Rust 1.53
Current beta, release 2021-06-17
Current stable, released 2021-06-17
[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
@ -194,7 +322,7 @@ Current beta, release 2021-06-17
## Rust 1.52
Current stable, released 2021-05-06
Released 2021-05-06
[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
@ -2295,6 +2423,7 @@ Released 2018-09-13
<!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
@ -2358,6 +2487,8 @@ Released 2018-09-13
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@ -2527,6 +2658,7 @@ Released 2018-09-13
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
@ -2574,6 +2706,7 @@ Released 2018-09-13
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref

View File

@ -115,7 +115,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava
`git clone https://github.com/rust-lang/rust/`.
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
which `IntelliJ Rust` will be able to understand.
Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
you just cloned.
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.

View File

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.1.54"
version = "0.1.55"
authors = ["The Rust Clippy Developers"]
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"

View File

@ -10,16 +10,17 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
| Category | Description | Default level |
| --------------------- | ----------------------------------------------------------------------- | ------------- |
| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or very useless | **deny** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
| Category | Description | Default level |
| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
@ -75,11 +76,10 @@ cargo clippy
#### Automatically applying Clippy suggestions
Clippy can automatically apply some lint suggestions.
Note that this is still experimental and only supported on the nightly channel:
Clippy can automatically apply some lint suggestions, just like the compiler.
```terminal
cargo clippy --fix -Z unstable-options
cargo clippy --fix
```
#### Workspaces

View File

@ -8,7 +8,7 @@ edition = "2018"
bytecount = "0.6"
clap = "2.33"
itertools = "0.9"
opener = "0.4"
opener = "0.5"
regex = "1"
shell-escape = "0.1"
walkdir = "2"

View File

@ -60,11 +60,7 @@ fn try_run(context: &FmtContext) -> Result<bool, CliError> {
let entry = entry?;
let path = entry.path();
if path.extension() != Some("rs".as_ref())
|| entry.file_name() == "ice-3891.rs"
// Avoid rustfmt bug rust-lang/rustfmt#1873
|| cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
{
if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
continue;
}
@ -90,7 +86,7 @@ fn output_err(err: CliError) {
},
CliError::RaSetupActive => {
eprintln!(
"error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
Not formatting because that would format the local repo as well!
Please revert the changes to Cargo.tomls first."
);

View File

@ -1,103 +0,0 @@
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
// the respective rustc subcrates instead of using extern crate xyz.
// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
/// # Panics
///
/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read
pub fn run(rustc_path: Option<&str>) {
// we can unwrap here because the arg is required by clap
let rustc_path = PathBuf::from(rustc_path.unwrap())
.canonicalize()
.expect("failed to get the absolute repo path");
assert!(rustc_path.is_dir(), "path is not a directory");
let rustc_source_basedir = rustc_path.join("compiler");
assert!(
rustc_source_basedir.is_dir(),
"are you sure the path leads to a rustc repo?"
);
let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
inject_deps_into_manifest(
&rustc_source_basedir,
"Cargo.toml",
&clippy_root_manifest,
&clippy_root_lib_rs,
)
.expect("Failed to inject deps into ./Cargo.toml");
let clippy_lints_manifest =
fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
let clippy_lints_lib_rs =
fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
inject_deps_into_manifest(
&rustc_source_basedir,
"clippy_lints/Cargo.toml",
&clippy_lints_manifest,
&clippy_lints_lib_rs,
)
.expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
}
fn inject_deps_into_manifest(
rustc_source_dir: &Path,
manifest_path: &str,
cargo_toml: &str,
lib_rs: &str,
) -> std::io::Result<()> {
// do not inject deps if we have aleady done so
if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
eprintln!(
"cargo dev ide_setup: warning: deps already found inside {}, doing nothing.",
manifest_path
);
return Ok(());
}
let extern_crates = lib_rs
.lines()
// get the deps
.filter(|line| line.starts_with("extern crate"))
// we have something like "extern crate foo;", we only care about the "foo"
// ↓ ↓
// extern crate rustc_middle;
.map(|s| &s[13..(s.len() - 1)]);
let new_deps = extern_crates.map(|dep| {
// format the dependencies that are going to be put inside the Cargo.toml
format!(
"{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
dep = dep,
source_path = rustc_source_dir.display()
)
});
// format a new [dependencies]-block with the new deps we need to inject
let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
new_deps.for_each(|dep_line| {
all_deps.push_str(&dep_line);
});
all_deps.push_str("\n[dependencies]\n");
// replace "[dependencies]" with
// [dependencies]
// dep1 = { path = ... }
// dep2 = { path = ... }
// etc
let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
// println!("{}", new_manifest);
let mut file = File::create(manifest_path)?;
file.write_all(new_manifest.as_bytes())?;
println!("Dependency paths injected: {}", manifest_path);
Ok(())
}

View File

@ -14,9 +14,9 @@
pub mod bless;
pub mod fmt;
pub mod ide_setup;
pub mod new_lint;
pub mod serve;
pub mod setup;
pub mod stderr_length_check;
pub mod update_lints;

View File

@ -2,8 +2,8 @@
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{App, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
fn main() {
let matches = get_clap_config();
@ -36,7 +36,22 @@ fn main() {
("limit_stderr_length", _) => {
stderr_length_check::check();
},
("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")),
("setup", Some(sub_command)) => match sub_command.subcommand() {
("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
matches
.value_of("rustc-repo-path")
.expect("this field is mandatory and therefore always valid"),
),
("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
_ => {},
},
("remove", Some(sub_command)) => match sub_command.subcommand() {
("git-hook", Some(_)) => setup::git_hook::remove_hook(),
("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
_ => {},
},
("serve", Some(matches)) => {
let port = matches.value_of("port").unwrap().parse().unwrap();
let lint = matches.value_of("lint");
@ -48,6 +63,7 @@ fn main() {
fn get_clap_config<'a>() -> ArgMatches<'a> {
App::new("Clippy developer tooling")
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand(
SubCommand::with_name("bless")
.about("bless the test output changes")
@ -123,6 +139,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.possible_values(&[
"style",
"correctness",
"suspicious",
"complexity",
"perf",
"pedantic",
@ -140,16 +157,54 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
)
.subcommand(
SubCommand::with_name("ide_setup")
.about("Alter dependencies so Intellij Rust can find rustc internals")
.arg(
Arg::with_name("rustc-repo-path")
.long("repo-path")
.short("r")
.help("The path to a rustc repo that will be used for setting the dependencies")
.takes_value(true)
.value_name("path")
.required(true),
SubCommand::with_name("setup")
.about("Support for setting up your personal development environment")
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand(
SubCommand::with_name("intellij")
.about("Alter dependencies so Intellij Rust can find rustc internals")
.arg(
Arg::with_name("rustc-repo-path")
.long("repo-path")
.short("r")
.help("The path to a rustc repo that will be used for setting the dependencies")
.takes_value(true)
.value_name("path")
.required(true),
),
)
.subcommand(
SubCommand::with_name("git-hook")
.about("Add a pre-commit git hook that formats your code to make it look pretty")
.arg(
Arg::with_name("force-override")
.long("force-override")
.short("f")
.help("Forces the override of an existing git pre-commit hook")
.required(false),
),
)
.subcommand(
SubCommand::with_name("vscode-tasks")
.about("Add several tasks to vscode for formatting, validation and testing")
.arg(
Arg::with_name("force-override")
.long("force-override")
.short("f")
.help("Forces the override of existing vscode tasks")
.required(false),
),
),
)
.subcommand(
SubCommand::with_name("remove")
.about("Support for undoing changes done by the setup command")
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
.subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
.subcommand(
SubCommand::with_name("intellij")
.about("Removes rustc source paths added via `cargo dev setup intellij`"),
),
)
.subcommand(

View File

@ -0,0 +1,85 @@
use std::fs;
use std::path::Path;
use super::verify_inside_clippy_dir;
/// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
/// I've decided against this for the sake of simplicity and to make sure that it doesn't install
/// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
/// for formatting and should therefor only be used in a normal clone of clippy
const REPO_GIT_DIR: &str = ".git";
const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh";
const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit";
pub fn install_hook(force_override: bool) {
if !check_precondition(force_override) {
return;
}
// So a little bit of a funny story. Git on unix requires the pre-commit file
// to have the `execute` permission to be set. The Rust functions for modifying
// these flags doesn't seem to work when executed with normal user permissions.
//
// However, there is a little hack that is also being used by Rust itself in their
// setup script. Git saves the `execute` flag when syncing files. This means
// that we can check in a file with execution permissions and the sync it to create
// a file with the flag set. We then copy this file here. The copy function will also
// include the `execute` permission.
match fs::copy(HOOK_SOURCE_FILE, HOOK_TARGET_FILE) {
Ok(_) => {
println!("info: the hook can be removed with `cargo dev remove git-hook`");
println!("git hook successfully installed");
},
Err(err) => eprintln!(
"error: unable to copy `{}` to `{}` ({})",
HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
),
}
}
fn check_precondition(force_override: bool) -> bool {
if !verify_inside_clippy_dir() {
return false;
}
// Make sure that we can find the git repository
let git_path = Path::new(REPO_GIT_DIR);
if !git_path.exists() || !git_path.is_dir() {
eprintln!("error: clippy_dev was unable to find the `.git` directory");
return false;
}
// Make sure that we don't override an existing hook by accident
let path = Path::new(HOOK_TARGET_FILE);
if path.exists() {
if force_override {
return delete_git_hook_file(path);
}
eprintln!("error: there is already a pre-commit hook installed");
println!("info: use the `--force-override` flag to override the existing hook");
return false;
}
true
}
pub fn remove_hook() {
let path = Path::new(HOOK_TARGET_FILE);
if path.exists() {
if delete_git_hook_file(path) {
println!("git hook successfully removed");
}
} else {
println!("no pre-commit hook was found");
}
}
fn delete_git_hook_file(path: &Path) -> bool {
if let Err(err) = fs::remove_file(path) {
eprintln!("error: unable to delete existing pre-commit git hook ({})", err);
false
} else {
true
}
}

View File

@ -0,0 +1,223 @@
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
// the respective rustc subcrates instead of using extern crate xyz.
// This allows IntelliJ to analyze rustc internals and show proper information inside Clippy
// code. See https://github.com/rust-lang/rust-clippy/issues/5514 for details
const RUSTC_PATH_SECTION: &str = "[target.'cfg(NOT_A_PLATFORM)'.dependencies]";
const DEPENDENCIES_SECTION: &str = "[dependencies]";
const CLIPPY_PROJECTS: &[ClippyProjectInfo] = &[
ClippyProjectInfo::new("root", "Cargo.toml", "src/driver.rs"),
ClippyProjectInfo::new("clippy_lints", "clippy_lints/Cargo.toml", "clippy_lints/src/lib.rs"),
ClippyProjectInfo::new("clippy_utils", "clippy_utils/Cargo.toml", "clippy_utils/src/lib.rs"),
];
/// Used to store clippy project information to later inject the dependency into.
struct ClippyProjectInfo {
/// Only used to display information to the user
name: &'static str,
cargo_file: &'static str,
lib_rs_file: &'static str,
}
impl ClippyProjectInfo {
const fn new(name: &'static str, cargo_file: &'static str, lib_rs_file: &'static str) -> Self {
Self {
name,
cargo_file,
lib_rs_file,
}
}
}
pub fn setup_rustc_src(rustc_path: &str) {
let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) {
Ok(path) => path,
Err(_) => return,
};
for project in CLIPPY_PROJECTS {
if inject_deps_into_project(&rustc_source_dir, project).is_err() {
return;
}
}
println!("info: the source paths can be removed again with `cargo dev remove intellij`");
}
fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
let mut path = PathBuf::from(rustc_path);
if path.is_relative() {
match path.canonicalize() {
Ok(absolute_path) => {
println!("info: the rustc path was resolved to: `{}`", absolute_path.display());
path = absolute_path;
},
Err(err) => {
eprintln!("error: unable to get the absolute path of rustc ({})", err);
return Err(());
},
};
}
let path = path.join("compiler");
println!("info: looking for compiler sources at: {}", path.display());
if !path.exists() {
eprintln!("error: the given path does not exist");
return Err(());
}
if !path.is_dir() {
eprintln!("error: the given path is not a directory");
return Err(());
}
Ok(path)
}
fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo) -> Result<(), ()> {
let cargo_content = read_project_file(project.cargo_file)?;
let lib_content = read_project_file(project.lib_rs_file)?;
if inject_deps_into_manifest(rustc_source_dir, project.cargo_file, &cargo_content, &lib_content).is_err() {
eprintln!(
"error: unable to inject dependencies into {} with the Cargo file {}",
project.name, project.cargo_file
);
Err(())
} else {
Ok(())
}
}
/// `clippy_dev` expects to be executed in the root directory of Clippy. This function
/// loads the given file or returns an error. Having it in this extra function ensures
/// that the error message looks nice.
fn read_project_file(file_path: &str) -> Result<String, ()> {
let path = Path::new(file_path);
if !path.exists() {
eprintln!("error: unable to find the file `{}`", file_path);
return Err(());
}
match fs::read_to_string(path) {
Ok(content) => Ok(content),
Err(err) => {
eprintln!("error: the file `{}` could not be read ({})", file_path, err);
Err(())
},
}
}
fn inject_deps_into_manifest(
rustc_source_dir: &Path,
manifest_path: &str,
cargo_toml: &str,
lib_rs: &str,
) -> std::io::Result<()> {
// do not inject deps if we have already done so
if cargo_toml.contains(RUSTC_PATH_SECTION) {
eprintln!(
"warn: dependencies are already setup inside {}, skipping file",
manifest_path
);
return Ok(());
}
let extern_crates = lib_rs
.lines()
// only take dependencies starting with `rustc_`
.filter(|line| line.starts_with("extern crate rustc_"))
// we have something like "extern crate foo;", we only care about the "foo"
// extern crate rustc_middle;
// ^^^^^^^^^^^^
.map(|s| &s[13..(s.len() - 1)]);
let new_deps = extern_crates.map(|dep| {
// format the dependencies that are going to be put inside the Cargo.toml
format!(
"{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
dep = dep,
source_path = rustc_source_dir.display()
)
});
// format a new [dependencies]-block with the new deps we need to inject
let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
new_deps.for_each(|dep_line| {
all_deps.push_str(&dep_line);
});
all_deps.push_str("\n[dependencies]\n");
// replace "[dependencies]" with
// [dependencies]
// dep1 = { path = ... }
// dep2 = { path = ... }
// etc
let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
// println!("{}", new_manifest);
let mut file = File::create(manifest_path)?;
file.write_all(new_manifest.as_bytes())?;
println!("info: successfully setup dependencies inside {}", manifest_path);
Ok(())
}
pub fn remove_rustc_src() {
for project in CLIPPY_PROJECTS {
remove_rustc_src_from_project(project);
}
}
fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) {
content
} else {
return false;
};
let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) {
section_start
} else {
println!(
"info: dependencies could not be found in `{}` for {}, skipping file",
project.cargo_file, project.name
);
return true;
};
let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) {
end_point
} else {
eprintln!(
"error: the end of the rustc dependencies section could not be found in `{}`",
project.cargo_file
);
return false;
};
cargo_content.replace_range(section_start..end_point, "");
match File::create(project.cargo_file) {
Ok(mut file) => {
file.write_all(cargo_content.as_bytes()).unwrap();
println!("info: successfully removed dependencies inside {}", project.cargo_file);
true
},
Err(err) => {
eprintln!(
"error: unable to open file `{}` to remove rustc dependencies for {} ({})",
project.cargo_file, project.name, err
);
false
},
}
}

View File

@ -0,0 +1,23 @@
pub mod git_hook;
pub mod intellij;
pub mod vscode;
use std::path::Path;
const CLIPPY_DEV_DIR: &str = "clippy_dev";
/// This function verifies that the tool is being executed in the clippy directory.
/// This is useful to ensure that setups only modify Clippys resources. The verification
/// is done by checking that `clippy_dev` is a sub directory of the current directory.
///
/// It will print an error message and return `false` if the directory could not be
/// verified.
fn verify_inside_clippy_dir() -> bool {
let path = Path::new(CLIPPY_DEV_DIR);
if path.exists() && path.is_dir() {
true
} else {
eprintln!("error: unable to verify that the working directory is clippys directory");
false
}
}

View File

@ -0,0 +1,104 @@
use std::fs;
use std::path::Path;
use super::verify_inside_clippy_dir;
const VSCODE_DIR: &str = ".vscode";
const TASK_SOURCE_FILE: &str = "util/etc/vscode-tasks.json";
const TASK_TARGET_FILE: &str = ".vscode/tasks.json";
pub fn install_tasks(force_override: bool) {
if !check_install_precondition(force_override) {
return;
}
match fs::copy(TASK_SOURCE_FILE, TASK_TARGET_FILE) {
Ok(_) => {
println!("info: the task file can be removed with `cargo dev remove vscode-tasks`");
println!("vscode tasks successfully installed");
},
Err(err) => eprintln!(
"error: unable to copy `{}` to `{}` ({})",
TASK_SOURCE_FILE, TASK_TARGET_FILE, err
),
}
}
fn check_install_precondition(force_override: bool) -> bool {
if !verify_inside_clippy_dir() {
return false;
}
let vs_dir_path = Path::new(VSCODE_DIR);
if vs_dir_path.exists() {
// verify the target will be valid
if !vs_dir_path.is_dir() {
eprintln!("error: the `.vscode` path exists but seems to be a file");
return false;
}
// make sure that we don't override any existing tasks by accident
let path = Path::new(TASK_TARGET_FILE);
if path.exists() {
if force_override {
return delete_vs_task_file(path);
}
eprintln!(
"error: there is already a `task.json` file inside the `{}` directory",
VSCODE_DIR
);
println!("info: use the `--force-override` flag to override the existing `task.json` file");
return false;
}
} else {
match fs::create_dir(vs_dir_path) {
Ok(_) => {
println!("info: created `{}` directory for clippy", VSCODE_DIR);
},
Err(err) => {
eprintln!(
"error: the task target directory `{}` could not be created ({})",
VSCODE_DIR, err
);
},
}
}
true
}
pub fn remove_tasks() {
let path = Path::new(TASK_TARGET_FILE);
if path.exists() {
if delete_vs_task_file(path) {
try_delete_vs_directory_if_empty();
println!("vscode tasks successfully removed");
}
} else {
println!("no vscode tasks were found");
}
}
fn delete_vs_task_file(path: &Path) -> bool {
if let Err(err) = fs::remove_file(path) {
eprintln!("error: unable to delete the existing `tasks.json` file ({})", err);
return false;
}
true
}
/// This function will try to delete the `.vscode` directory if it's empty.
/// It may fail silently.
fn try_delete_vs_directory_if_empty() {
let path = Path::new(VSCODE_DIR);
if path.read_dir().map_or(false, |mut iter| iter.next().is_none()) {
// The directory is empty. We just try to delete it but allow a silence
// fail as an empty `.vscode` directory is still valid
let _silence_result = fs::remove_dir(path);
} else {
// The directory is not empty or could not be read. Either way don't take
// any further actions
}
}

View File

@ -92,7 +92,10 @@ pub fn run(update_mode: UpdateMode) {
|| {
// clippy::all should only include the following lint groups:
let all_group_lints = usable_lints.iter().filter(|l| {
l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
matches!(
&*l.group,
"correctness" | "suspicious" | "style" | "complexity" | "perf"
)
});
gen_lint_group_list(all_group_lints)

View File

@ -1,7 +1,7 @@
[package]
name = "clippy_lints"
# begin automatic update
version = "0.1.54"
version = "0.1.55"
# end automatic update
authors = ["The Rust Clippy Developers"]
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
@ -23,6 +23,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
toml = "0.5.3"
unicode-normalization = "0.1"
unicode-script = { version = "0.5.3", default-features = false }
semver = "0.11"
rustc-semver = "1.1.0"
# NOTE: cargo requires serde feat in its url dep

View File

@ -55,7 +55,7 @@
/// a += a + b;
/// ```
pub MISREFACTORED_ASSIGN_OP,
complexity,
suspicious,
"having a variable on both sides of an assign op"
}

View File

@ -173,7 +173,7 @@
/// #![deny(clippy::as_conversions)]
/// ```
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
style,
suspicious,
"enabling the complete restriction group"
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{diagnostics::span_lint, is_test_module_or_function};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Pat, PatKind};
use rustc_hir::{Item, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -25,18 +25,37 @@
#[derive(Clone, Debug)]
pub struct BlacklistedName {
blacklist: FxHashSet<String>,
test_modules_deep: u32,
}
impl BlacklistedName {
pub fn new(blacklist: FxHashSet<String>) -> Self {
Self { blacklist }
Self {
blacklist,
test_modules_deep: 0,
}
}
fn in_test_module(&self) -> bool {
self.test_modules_deep != 0
}
}
impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_test_module_or_function(cx.tcx, item) {
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
}
}
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
// Check whether we are under the `test` attribute.
if self.in_test_module() {
return;
}
if let PatKind::Binding(.., ident, _) = pat.kind {
if self.blacklist.contains(&ident.name.to_string()) {
span_lint(
@ -48,4 +67,10 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
}
}
}
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_test_module_or_function(cx.tcx, item) {
self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
}
}
}

View File

@ -1,15 +1,15 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::match_type;
use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Checks for naive byte counts
@ -38,42 +38,43 @@
impl<'tcx> LateLintPass<'tcx> for ByteCount {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
if count.ident.name == sym!(count);
if count_args.len() == 1;
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
if filter.ident.name == sym!(filter);
if filter_args.len() == 2;
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
let body = cx.tcx.hir().body(body_id);
if body.params.len() == 1;
if let Some(argname) = get_pat_name(body.params[0].pat);
if let [param] = body.params;
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq;
if match_type(cx,
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
&paths::SLICE_ITER);
let operand_is_arg = |expr| {
let expr = peel_ref_operators(cx, remove_blocks(expr));
path_to_local_id(expr, arg_id)
};
let needle = if operand_is_arg(l) {
r
} else if operand_is_arg(r) {
l
} else {
return;
};
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
then {
let needle = match get_path_name(l) {
Some(name) if check_arg(name, argname, r) => r,
_ => match get_path_name(r) {
Some(name) if check_arg(name, argname, l) => l,
_ => { return; }
}
};
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
return;
}
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
filter_args[0].kind {
filter_recv.kind {
let p = path.ident.name;
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
&filter_args[0]
&filter_recv
}
} else {
&filter_args[0]
&filter_recv
};
let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg(
@ -91,24 +92,3 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
};
}
}
fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool {
name == arg && !contains_name(name, needle)
}
fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> {
match expr.kind {
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => {
get_path_name(e)
},
ExprKind::Block(b, _) => {
if b.stmts.is_empty() {
b.expr.as_ref().and_then(|p| get_path_name(p))
} else {
None
}
},
ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
_ => None,
}
}

View File

@ -1,11 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
use if_chain::if_chain;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span};
@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
if arms_inner.iter().all(|arm| arm.guard.is_none());
// match expression must be a local binding
// match <local> { .. }
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
// one of the branches must be "wild-like"
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
let (wild_inner_arm, non_wild_inner_arm) =
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
});
result
}
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
loop {
match expr.kind {
ExprKind::AddrOf(_, _, e) => expr = e,
ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
_ => break,
}
}
expr
}

View File

@ -7,7 +7,6 @@
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
@ -122,7 +121,7 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if !in_external_macro(cx.tcx.sess, expr.span);
if !in_macro(expr.span);
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`

View File

@ -7,9 +7,10 @@
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::{
hir::map::Map,
lint::in_external_macro,
ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -73,6 +74,7 @@ fn new(cx: &'a LateContext<'tcx>) -> Self {
/// 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 !in_external_macro(self.cx.sess(), lit.span);
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));

View File

@ -149,7 +149,7 @@ macro_rules! declare_deprecated_lint {
/// enables the `enum_variant_names` lint for public items.
/// ```
pub PUB_ENUM_VARIANT_NAMES,
"set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items"
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
}
declare_deprecated_lint! {
@ -158,5 +158,5 @@ macro_rules! declare_deprecated_lint {
/// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which
/// enables the `wrong_self_conversion` lint for public items.
pub WRONG_PUB_SELF_CONVENTION,
"set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items"
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
}

View File

@ -410,11 +410,8 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
}
if let ExprKind::Block(block, _) = expr.kind {
match block.rules {
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => {
self.has_unsafe = true;
},
_ => {},
if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
self.has_unsafe = true;
}
}

View File

@ -2,7 +2,7 @@
use clippy_utils::fn_def_id;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::Expr;
use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
@ -13,21 +13,14 @@
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it's beneficial to lint for them as needed.
///
/// **Known problems:** Currently, you must write each function as a
/// fully-qualified path. This lint doesn't support aliases or reexported
/// names; be aware that many types in `std` are actually reexports.
///
/// For example, if you want to disallow `Duration::as_secs`, your clippy.toml
/// configuration would look like
/// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
/// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
/// **Known problems:** None.
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
/// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
/// ```
///
/// ```rust,ignore
@ -52,6 +45,7 @@
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
}
impl DisallowedMethod {
@ -61,6 +55,7 @@ pub fn new(disallowed: &FxHashSet<String>) -> Self {
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
}
}
}
@ -68,10 +63,20 @@ pub fn new(disallowed: &FxHashSet<String>) -> Self {
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for path in &self.disallowed {
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
{
self.def_ids.insert((id, path.clone()));
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(def_id) = fn_def_id(cx, expr) {
let func_path = cx.get_def_path(def_id);
if self.disallowed.contains(&func_path) {
if self.def_ids.iter().any(|(id, _)| def_id == *id) {
let func_path = cx.get_def_path(def_id);
let func_path_string = func_path
.into_iter()
.map(Symbol::to_ident_string)

View File

@ -0,0 +1,112 @@
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{EarlyContext, EarlyLintPass, Level};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use unicode_script::{Script, UnicodeScript};
declare_clippy_lint! {
/// **What it does:** Checks for usage of unicode scripts other than those explicitly allowed
/// by the lint config.
///
/// This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`.
/// It also ignores the `Common` script type.
/// While configuring, be sure to use official script name [aliases] from
/// [the list of supported scripts][supported_scripts].
///
/// See also: [`non_ascii_idents`].
///
/// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases
/// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html
///
/// **Why is this bad?** It may be not desired to have many different scripts for
/// identifiers in the codebase.
///
/// Note that if you only want to allow plain English, you might want to use
/// built-in [`non_ascii_idents`] lint instead.
///
/// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // Assuming that `clippy.toml` contains the following line:
/// // allowed-locales = ["Latin", "Cyrillic"]
/// let counter = 10; // OK, latin is allowed.
/// let счётчик = 10; // OK, cyrillic is allowed.
/// let zähler = 10; // OK, it's still latin.
/// let カウンタ = 10; // Will spawn the lint.
/// ```
pub DISALLOWED_SCRIPT_IDENTS,
restriction,
"usage of non-allowed Unicode scripts"
}
#[derive(Clone, Debug)]
pub struct DisallowedScriptIdents {
whitelist: FxHashSet<Script>,
}
impl DisallowedScriptIdents {
pub fn new(whitelist: &[String]) -> Self {
let whitelist = whitelist
.iter()
.map(String::as_str)
.filter_map(Script::from_full_name)
.collect();
Self { whitelist }
}
}
impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
impl EarlyLintPass for DisallowedScriptIdents {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs
let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow;
if !check_disallowed_script_idents {
return;
}
let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock();
// Sort by `Span` so that error messages make sense with respect to the
// order of identifier locations in the code.
let mut symbols: Vec<_> = symbols.iter().collect();
symbols.sort_unstable_by_key(|k| k.1);
for (symbol, &span) in &symbols {
// Note: `symbol.as_str()` is an expensive operation, thus should not be called
// more than once for a single symbol.
let symbol_str = symbol.as_str();
if symbol_str.is_ascii() {
continue;
}
for c in symbol_str.chars() {
// We want to iterate through all the scripts associated with this character
// and check whether at least of one scripts is in the whitelist.
let forbidden_script = c
.script_extension()
.iter()
.find(|script| !self.whitelist.contains(script));
if let Some(script) = forbidden_script {
span_lint(
cx,
DISALLOWED_SCRIPT_IDENTS,
span,
&format!(
"identifier `{}` has a Unicode script that is not allowed by configuration: {}",
symbol_str,
script.full_name()
),
);
// We don't want to spawn warning multiple times over a single identifier.
break;
}
}
}
}
}

View File

@ -0,0 +1,126 @@
use clippy_utils::diagnostics::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{
def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, Symbol};
declare_clippy_lint! {
/// **What it does:** Denies the configured types in clippy.toml.
///
/// **Why is this bad?** Some types are undesirable in certain contexts.
///
/// **Known problems:** None.
///
/// N.B. There is no way to ban primitive types.
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["std::collections::BTreeMap"]
/// ```
///
/// ```rust,ignore
/// use std::collections::BTreeMap;
/// // or its use
/// let x = std::collections::BTreeMap::new();
/// ```
/// Use instead:
/// ```rust,ignore
/// // A similar type that is allowed by the config
/// use std::collections::HashMap;
/// ```
pub DISALLOWED_TYPE,
nursery,
"use of a disallowed type"
}
#[derive(Clone, Debug)]
pub struct DisallowedType {
disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
}
impl DisallowedType {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
}
}
}
impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
impl<'tcx> LateLintPass<'tcx> for DisallowedType {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for path in &self.disallowed {
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
{
self.def_ids.insert((id, path.clone()));
}
}
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if_chain! {
if let ItemKind::Use(path, UseKind::Single) = &item.kind;
if let Res::Def(_, did) = path.res;
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, item.span,);
}
}
}
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
if_chain! {
if let TyKind::Path(path) = &ty.kind;
if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id();
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, path.span());
}
}
}
fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
if_chain! {
if let Res::Def(_, did) = poly.trait_ref.path.res;
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, poly.trait_ref.path.span);
}
}
}
// TODO: if non primitive const generics are a thing
// fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) {
// match arg {
// GenericArg::Const(c) => {},
// }
// }
// fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) {
// match param.kind {
// GenericParamKind::Const { .. } => {},
// }
// }
}
fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) {
let name = name.iter().map(|s| s.to_ident_string()).collect::<Vec<_>>().join("::");
span_lint(
cx,
DISALLOWED_TYPE,
span,
&format!("`{}` is not allowed according to config", name),
);
}

View File

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
use clippy_utils::source::first_line_of_span;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
use if_chain::if_chain;
@ -37,7 +38,8 @@
/// consider that.
///
/// **Known problems:** Lots of bad docs wont be fixed, what the lint checks
/// for is limited, and there are still false positives.
/// for is limited, and there are still false positives. HTML elements and their
/// content are not linted.
///
/// In addition, when writing documentation comments, including `[]` brackets
/// inside a link text would trip the parser. Therfore, documenting link with
@ -469,11 +471,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
spans: &[(usize, Span)],
) -> DocHeaders {
// true if a safety header was found
use pulldown_cmark::CodeBlockKind;
use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
};
use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
use pulldown_cmark::{CodeBlockKind, CowStr};
let mut headers = DocHeaders {
safety: false,
@ -485,6 +487,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
let mut in_heading = false;
let mut is_rust = false;
let mut edition = None;
let mut ticks_unbalanced = false;
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
for (event, range) in events {
match event {
Start(CodeBlock(ref kind)) => {
@ -510,13 +515,42 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
},
Start(Link(_, url, _)) => in_link = Some(url),
End(Link(..)) => in_link = None,
Start(Heading(_)) => in_heading = true,
End(Heading(_)) => in_heading = false,
Start(Heading(_) | Paragraph | Item) => {
if let Start(Heading(_)) = event {
in_heading = true;
}
ticks_unbalanced = false;
let (_, span) = get_current_span(spans, range.start);
paragraph_span = first_line_of_span(cx, span);
},
End(Heading(_) | Paragraph | Item) => {
if let End(Heading(_)) = event {
in_heading = false;
}
if ticks_unbalanced {
span_lint_and_help(
cx,
DOC_MARKDOWN,
paragraph_span,
"backticks are unbalanced",
None,
"a backtick may be missing a pair",
);
} else {
for (text, span) in text_to_check {
check_text(cx, valid_idents, &text, span);
}
}
text_to_check = Vec::new();
},
Start(_tag) | End(_tag) => (), // We don't care about other tags
Html(_html) => (), // HTML is weird, just ignore it
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
FootnoteReference(text) | Text(text) => {
if Some(&text) == in_link.as_ref() {
let (begin, span) = get_current_span(spans, range.start);
paragraph_span = paragraph_span.with_hi(span.hi());
ticks_unbalanced |= text.contains('`');
if Some(&text) == in_link.as_ref() || ticks_unbalanced {
// Probably a link of the form `<http://example.com>`
// Which are represented as a link to "http://example.com" with
// text "http://example.com" by pulldown-cmark
@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers.safety |= in_heading && text.trim() == "Safety";
headers.errors |= in_heading && text.trim() == "Errors";
headers.panics |= in_heading && text.trim() == "Panics";
let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
Ok(o) => o,
Err(e) => e - 1,
};
let (begin, span) = spans[index];
if in_code {
if is_rust {
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
@ -538,8 +567,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
} else {
// Adjust for the beginning of the current `Event`
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
check_text(cx, valid_idents, &text, span);
text_to_check.push((text, span));
}
},
}
@ -547,6 +575,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers
}
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
Ok(o) => o,
Err(e) => e - 1,
};
spans[index]
}
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
fn has_needless_main(code: &str, edition: Edition) -> bool {
rustc_driver::catch_fatal_errors(|| {

View File

@ -38,7 +38,7 @@
/// let a = tmp + x;
/// ```
pub EVAL_ORDER_DEPENDENCE,
complexity,
suspicious,
"whether a variable read occurs before a write depends on sub-expression evaluation order"
}

View File

@ -36,7 +36,7 @@
/// }
/// ```
pub FLOAT_EQUALITY_WITHOUT_ABS,
correctness,
suspicious,
"float equality check without `.abs()`"
}

View File

@ -22,7 +22,7 @@
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
/// ```
pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
style,
suspicious,
"suspicious formatting of `*=`, `-=` or `!=`"
}
@ -44,7 +44,7 @@
/// }
/// ```
pub SUSPICIOUS_UNARY_OP_FORMATTING,
style,
suspicious,
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
}
@ -80,7 +80,7 @@
/// }
/// ```
pub SUSPICIOUS_ELSE_FORMATTING,
style,
suspicious,
"suspicious formatting of `else`"
}

View File

@ -74,7 +74,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// LHS of subtraction is "x.len()"
if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args, _) = &lhs.kind;
if arg_lhs_path.ident.name == sym!(len);
if arg_lhs_path.ident.name == sym::len;
if let Some(arg_lhs_struct) = lhs_args.get(0);
// The two vectors referenced (x in x.get(...) and in x.len())

View File

@ -83,7 +83,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
}
}
/// Checks if `Mutex::lock` is called in the `if let _ = expr.
/// Checks if `Mutex::lock` is called in the `if let` expr.
pub struct OppVisitor<'a, 'tcx> {
mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>,

View File

@ -128,7 +128,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if_chain! {
if item.ident.as_str() == "len";
if item.ident.name == sym::len;
if let ImplItemKind::Fn(sig, _) = &item.kind;
if sig.decl.implicit_self.has_implicit_self();
if cx.access_levels.is_exported(item.hir_id());
@ -189,8 +189,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
}
fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: &str) -> bool {
item.ident.name.as_str() == name
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
item.ident.name == name
&& if let AssocItemKind::Fn { has_self } = item.kind {
has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 }
} else {
@ -207,7 +207,9 @@ fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
}
}
if cx.access_levels.is_exported(visited_trait.hir_id()) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
if cx.access_levels.is_exported(visited_trait.hir_id())
&& trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
{
let mut current_and_super_traits = DefIdSet::default();
fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
@ -401,7 +403,7 @@ fn check_len(
return;
}
if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
if method_name == sym::len && args.len() == 1 && has_is_empty(cx, &args[0]) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,

View File

@ -60,9 +60,9 @@
/// 4. The `description` that contains a short explanation on what's wrong with code where the
/// lint is triggered.
///
/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default.
/// As said in the README.md of this repository, if the lint level mapping changes, please update
/// README.md.
/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
/// enabled by default. As said in the README.md of this repository, if the lint level mapping
/// changes, please update README.md.
///
/// # Example
///
@ -106,6 +106,11 @@ macro_rules! declare_clippy_lint {
$(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
}
};
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
@ -187,6 +192,8 @@ macro_rules! declare_clippy_lint {
mod dereference;
mod derive;
mod disallowed_method;
mod disallowed_script_idents;
mod disallowed_type;
mod doc;
mod double_comparison;
mod double_parens;
@ -254,7 +261,6 @@ macro_rules! declare_clippy_lint {
mod manual_unwrap_or;
mod map_clone;
mod map_err_ignore;
mod map_identity;
mod map_unit_fn;
mod match_on_vec_items;
mod matches;
@ -267,6 +273,7 @@ macro_rules! declare_clippy_lint {
mod misc_early;
mod missing_const_for_fn;
mod missing_doc;
mod missing_enforced_import_rename;
mod missing_inline;
mod modulo_arithmetic;
mod multiple_crate_versions;
@ -293,6 +300,7 @@ macro_rules! declare_clippy_lint {
mod non_copy_const;
mod non_expressive_names;
mod non_octal_unix_permissions;
mod nonstandard_macro_braces;
mod open_options;
mod option_env_unwrap;
mod option_if_let_else;
@ -483,11 +491,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
);
store.register_removed(
"clippy::pub_enum_variant_names",
"set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items",
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
);
store.register_removed(
"clippy::wrong_pub_self_convention",
"set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items",
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
);
// end deprecated lints, do not remove this comment, its used in `update_lints`
@ -583,6 +591,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_method::DISALLOWED_METHOD,
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
disallowed_type::DISALLOWED_TYPE,
doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC,
@ -705,7 +715,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
manual_unwrap_or::MANUAL_UNWRAP_OR,
map_clone::MAP_CLONE,
map_err_ignore::MAP_ERR_IGNORE,
map_identity::MAP_IDENTITY,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS,
@ -730,6 +739,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::APPEND_INSTEAD_OF_EXTEND,
methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_NTH,
methods::CHARS_LAST_CMP,
@ -765,6 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
methods::MANUAL_STR_REPEAT,
methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
methods::NEW_RET_NO_SELF,
methods::OK_EXPECT,
@ -810,6 +821,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
misc_early::ZERO_PREFIXED_LITERAL,
missing_const_for_fn::MISSING_CONST_FOR_FN,
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
modulo_arithmetic::MODULO_ARITHMETIC,
multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
@ -843,6 +855,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
non_expressive_names::MANY_SINGLE_CHAR_NAMES,
non_expressive_names::SIMILAR_NAMES,
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
open_options::NONSENSICAL_OPEN_OPTIONS,
option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE,
@ -989,6 +1002,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
@ -1013,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(misc::FLOAT_CMP_CONST),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
@ -1090,6 +1105,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_FLATTEN),
@ -1260,7 +1276,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_clone::MAP_CLONE),
LintId::of(map_identity::MAP_IDENTITY),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@ -1276,6 +1291,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
@ -1286,7 +1302,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
@ -1301,6 +1316,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF),
@ -1359,6 +1375,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
@ -1447,7 +1464,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(assign_ops::ASSIGN_OP_PATTERN),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(blacklisted_name::BLACKLISTED_NAME),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@ -1465,9 +1481,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(eq_op::OP_REF),
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
LintId::of(float_literal::EXCESSIVE_PRECISION),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(from_over_into::FROM_OVER_INTO),
LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(functions::DOUBLE_MUST_USE),
@ -1480,7 +1493,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(len_zero::LEN_ZERO),
LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::FOR_KV_MAP),
LintId::of(loops::NEEDLESS_RANGE_LOOP),
LintId::of(loops::SAME_ITEM_PUSH),
@ -1501,7 +1513,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE),
@ -1535,6 +1546,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ),
@ -1560,7 +1572,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
]);
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(casts::CHAR_LIT_AS_U8),
@ -1570,7 +1581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(double_parens::DOUBLE_PARENS),
LintId::of(duration_subsec::DURATION_SUBSEC),
LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(explicit_write::EXPLICIT_WRITE),
LintId::of(format::USELESS_FORMAT),
LintId::of(functions::TOO_MANY_ARGUMENTS),
@ -1581,12 +1591,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(lifetimes::NEEDLESS_LIFETIMES),
LintId::of(loops::EXPLICIT_COUNTER_LOOP),
LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(loops::SINGLE_ELEMENT_LOOP),
LintId::of(loops::WHILE_LET_LOOP),
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_identity::MAP_IDENTITY),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::MATCH_AS_REF),
@ -1601,11 +1609,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::ITER_COUNT),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
@ -1674,7 +1682,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(eq_op::EQ_OP),
LintId::of(erasing_op::ERASING_OP),
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(if_let_mutex::IF_LET_MUTEX),
@ -1684,7 +1691,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
LintId::of(loops::ITER_NEXT_LOOP),
LintId::of(loops::NEVER_LOOP),
LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
@ -1699,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(misc::CMP_NAN),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
@ -1710,8 +1715,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(self_assignment::SELF_ASSIGNMENT),
LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap::ALMOST_SWAPPED),
LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
@ -1728,6 +1731,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
]);
store.register_group(true, "clippy::suspicious", None, vec![
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
]);
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(entry::MAP_ENTRY),
LintId::of(escape::BOXED_LOCAL),
@ -1735,6 +1755,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::ITER_NTH),
LintId::of(methods::MANUAL_STR_REPEAT),
@ -1761,6 +1782,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(disallowed_method::DISALLOWED_METHOD),
LintId::of(disallowed_type::DISALLOWED_TYPE),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
@ -2038,8 +2060,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
single_char_binding_names_threshold,
});
let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(&macro_matcher));
store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_late_pass(|| box map_identity::MapIdentity);
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
store.register_late_pass(|| box repeat_once::RepeatOnce);
@ -2066,7 +2089,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_late_pass(|| box unused_async::UnusedAsync);
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));
let import_renames = conf.enforced_import_renames.clone();
store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone()));
let scripts = conf.allowed_scripts.clone();
store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
}
#[rustfmt::skip]

View File

@ -118,7 +118,7 @@ fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> {
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
if_chain! {
if let ExprKind::MethodCall(method, _, len_args, _) = end.kind;
if method.ident.name == sym!(len);
if method.ident.name == sym::len;
if len_args.len() == 1;
if let Some(arg) = len_args.get(0);
if path_to_local(arg) == path_to_local(base);

View File

@ -199,7 +199,7 @@
/// }
/// ```
pub FOR_LOOPS_OVER_FALLIBLES,
correctness,
suspicious,
"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
}
@ -313,7 +313,7 @@
/// loop {}
/// ```
pub EMPTY_LOOP,
style,
suspicious,
"empty `loop {}`, which should block or sleep"
}
@ -401,7 +401,7 @@
/// }
/// ```
pub MUT_RANGE_BOUND,
complexity,
suspicious,
"for loop over a range where one of the bounds is a mutable variable"
}

View File

@ -7,10 +7,10 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::symbol::{sym, Ident};
use rustc_span::sym;
use rustc_span::{MultiSpan, Span};
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
@ -24,10 +24,8 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
if let Some(generic_args) = chain_method.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
then {
let ty = cx.typeck_results().expr_ty(&args[0]);
let mut applicability = Applicability::MachineApplicable;
let is_empty_sugg = "next().is_none()".to_string();
let method_name = &*method.ident.name.as_str();
@ -72,40 +70,25 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
}
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
if let Some(ty) = ty {
return Some(ty.hir_id);
}
if let Some(generic_args) = method_args {
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
return Some(ty.hir_id);
}
}
None
}
if let ExprKind::Block(block, _) = expr.kind {
for stmt in block.stmts {
if_chain! {
if let StmtKind::Local(
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
init: Some(init_expr), ty, .. }
) = stmt.kind;
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(_, id, ..) = local.pat.kind;
if let Some(init_expr) = local.init;
if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
if let Some(hir_id) = get_hir_id(*ty, method_name.args);
if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
let ty = cx.typeck_results().expr_ty(init_expr);
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
is_type_diagnostic_item(cx, ty, sym::LinkedList);
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
if let [iter_call] = &*iter_calls;
then {
let mut used_count_visitor = UsedCountVisitor {
cx,
id: *pat_id,
id,
count: 0,
};
walk_block(&mut used_count_visitor, block);
@ -187,48 +170,40 @@ enum IterFunctionKind {
struct IterFunctionVisitor {
uses: Vec<IterFunction>,
seen_other: bool,
target: Ident,
target: HirId,
}
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
if_chain! {
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0);
if let &[name] = &path.segments;
if name.ident == self.target;
then {
let len = sym!(len);
let is_empty = sym!(is_empty);
let contains = sym!(contains);
match method_name.ident.name {
sym::into_iter => self.uses.push(
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
),
name if name == len => self.uses.push(
IterFunction { func: IterFunctionKind::Len, span: expr.span }
),
name if name == is_empty => self.uses.push(
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
),
name if name == contains => self.uses.push(
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
),
if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
if path_to_local_id(recv, self.target) {
match &*method_name.ident.name.as_str() {
"into_iter" => self.uses.push(IterFunction {
func: IterFunctionKind::IntoIter,
span: expr.span,
}),
"len" => self.uses.push(IterFunction {
func: IterFunctionKind::Len,
span: expr.span,
}),
"is_empty" => self.uses.push(IterFunction {
func: IterFunctionKind::IsEmpty,
span: expr.span,
}),
"contains" => self.uses.push(IterFunction {
func: IterFunctionKind::Contains(args[0].span),
span: expr.span,
}),
_ => self.seen_other = true,
}
return
return;
}
}
// Check if the collection is used for anything else
if_chain! {
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr;
if let &[name] = &path.segments;
if name.ident == self.target;
then {
self.seen_other = true;
} else {
walk_expr(self, expr);
}
if path_to_local_id(expr, self.target) {
self.seen_other = true;
} else {
walk_expr(self, expr);
}
}
@ -262,10 +237,10 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
/// Detect the occurrences of calls to `iter` or `into_iter` for the
/// given identifier
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
let mut visitor = IterFunctionVisitor {
uses: Vec::new(),
target: identifier,
target: id,
seen_other: false,
};
visitor.visit_block(block);

View File

@ -192,7 +192,7 @@ fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
if_chain! {
if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind;
if len_args.len() == 1;
if method.ident.name == sym!(len);
if method.ident.name == sym::len;
if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
if path.segments.len() == 1;
if path.segments[0].ident.name == var;

View File

@ -1,7 +1,9 @@
use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
use clippy_utils::{
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
@ -315,9 +317,10 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
}
}
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration unless it is declared within the outer
// loop.
if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration (for loops), or on the next call (for
// closures) unless it is declared within the enclosing expression. TODO: Check for closures
// used where an `FnOnce` type is expected.
let local_id = match iter_expr.path {
Res::Local(id) => id,
_ => return true,

View File

@ -3,19 +3,17 @@
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use clippy_utils::{
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
peel_hir_expr_refs,
};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
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},
SyntaxContext,
};
use rustc_span::{sym, SyntaxContext};
declare_clippy_lint! {
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
@ -141,13 +139,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
scrutinee_str.into()
};
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
match can_pass_as_func(cx, some_binding, some_expr) {
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
match can_pass_as_func(cx, id, some_expr) {
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
},
_ => {
if match_var(some_expr, some_binding.name)
if path_to_local_id(some_expr, id)
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
@ -199,10 +197,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// 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>> {
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, 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() =>
if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
{
Some(func)
},

View File

@ -1,126 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
///
/// **Why is this bad?** It can be written more concisely without the call to `map`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
impl<'tcx> LateLintPass<'tcx> for MapIdentity {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if expr.span.from_expansion() {
return;
}
if_chain! {
if let Some([caller, func]) = get_map_argument(cx, expr);
if is_expr_identity_function(cx, func);
then {
span_lint_and_sugg(
cx,
MAP_IDENTITY,
expr.span.trim_start(caller.span).unwrap(),
"unnecessary map of the identity function",
"remove the call to `map`",
String::new(),
Applicability::MachineApplicable
)
}
}
}
}
/// Returns the arguments passed into map() if the expression is a method call to
/// map(). Otherwise, returns None.
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
if_chain! {
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
if args.len() == 2 && method.ident.name == sym::map;
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
then {
Some(args)
} else {
None
}
}
}
/// Checks if an expression represents the identity function
/// Only examines closures and `std::convert::identity`
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
_ => false,
}
}
/// Checks if a function's body represents the identity function
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
/// return x; }`
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let params = func.params;
let body = remove_blocks(&func.value);
// if there's less/more than one parameter, then it is not the identity function
if params.len() != 1 {
return false;
}
match body.kind {
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
ExprKind::Block(block, _) => {
if_chain! {
if block.stmts.len() == 1;
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind;
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
then {
match_expr_param(cx, ret_val, params[0].pat)
} else {
false
}
}
},
_ => false,
}
}
/// Returns true iff an expression returns the same thing as a parameter's pattern
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
if let PatKind::Binding(_, _, ident, _) = pat.kind {
match_var(expr, ident.name) && !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
} else {
false
}
}

View File

@ -992,9 +992,9 @@ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
}
}
fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
let attrs = cx.tcx.get_attrs(variant_def.def_id);
clippy_utils::attrs::is_doc_hidden(attrs)
clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
}
#[allow(clippy::too_many_lines)]
@ -1033,7 +1033,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
// Accumulate the variants which should be put in place of the wildcard because they're not
// already covered.
let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
let mut path_prefix = CommonPrefixSearcher::None;
for arm in arms {
@ -1118,7 +1119,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
match missing_variants.as_slice() {
[] => (),
[x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg(
[x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
cx,
MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
wildcard_span,
@ -1129,7 +1130,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
),
variants => {
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
let message = if adt_def.is_variant_list_non_exhaustive() {
let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
suggestions.push("_".into());
"wildcard matches known variants and will also match future added variants"
} else {
@ -2266,7 +2267,8 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
),
);
} else {
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
.help("...or consider changing the match arm bodies");
}
},
);

View File

@ -0,0 +1,41 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::APPEND_INSTEAD_OF_EXTEND;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if_chain! {
if is_type_diagnostic_item(cx, ty, sym::vec_type);
//check source object
if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind;
if src_method.ident.as_str() == "drain";
if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs();
if is_type_diagnostic_item(cx, src_ty, sym::vec_type);
//check drain range
if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs();
if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull);
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
APPEND_INSTEAD_OF_EXTEND,
expr.span,
"use of `extend` instead of `append` for adding the full range of a second vector",
"try this",
format!(
"{}.append(&mut {})",
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability)
),
applicability,
);
}
}
}

View File

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@ -9,32 +8,15 @@
use super::FILTER_MAP_IDENTITY;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
if is_trait_method(cx, expr, sym::Iterator) {
let apply_lint = |message: &str| {
span_lint_and_sugg(
cx,
FILTER_MAP_IDENTITY,
filter_map_span.with_hi(expr.span.hi()),
message,
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
};
if_chain! {
if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
let body = cx.tcx.hir().body(body_id);
if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
if path_to_local_id(&body.value, binding_id);
then {
apply_lint("called `filter_map(|x| x)` on an `Iterator`");
}
}
if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
}
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
span_lint_and_sugg(
cx,
FILTER_MAP_IDENTITY,
filter_map_span.with_hi(expr.span.hi()),
"use of `filter_map` with an identity function",
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
}
}

View File

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
use if_chain::if_chain;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span,
) {
if is_trait_method(cx, expr, sym::Iterator) {
let apply_lint = |message: &str| {
span_lint_and_sugg(
cx,
FLAT_MAP_IDENTITY,
flat_map_span.with_hi(expr.span.hi()),
message,
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
};
if_chain! {
if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
let body = cx.tcx.hir().body(body_id);
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
if path.segments.len() == 1;
if path.segments[0].ident.name == binding_ident.name;
then {
apply_lint("called `flat_map(|x| x)` on an `Iterator`");
}
}
if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
}
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
span_lint_and_sugg(
cx,
FLAT_MAP_IDENTITY,
flat_map_span.with_hi(expr.span.hi()),
"use of `flat_map` with an identity function",
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
}
}

View File

@ -0,0 +1,38 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::{source_map::Span, sym};
use super::MAP_IDENTITY;
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
caller: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>,
_map_span: Span,
) {
let caller_ty = cx.typeck_results().expr_ty(caller);
if_chain! {
if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
if is_expr_identity_function(cx, map_arg);
if let Some(sugg_span) = expr.span.trim_start(caller.span);
then {
span_lint_and_sugg(
cx,
MAP_IDENTITY,
sugg_span,
"unnecessary map of the identity function",
"remove the call to `map`",
String::new(),
Applicability::MachineApplicable,
)
}
}
}

View File

@ -1,3 +1,4 @@
mod append_instead_of_extend;
mod bind_instead_of_map;
mod bytes_nth;
mod chars_cmp;
@ -35,6 +36,7 @@
mod manual_str_repeat;
mod map_collect_result_unit;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
mod ok_expect;
mod option_as_ref_deref;
@ -1031,6 +1033,30 @@
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
}
declare_clippy_lint! {
/// **What it does:** Checks for occurrences where one vector gets extended instead of append
///
/// **Why is this bad?** Using `append` instead of `extend` is more concise and faster
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
/// // Bad
/// a.extend(b.drain(..));
///
/// // Good
/// a.append(&mut b);
/// ```
pub APPEND_INSTEAD_OF_EXTEND,
perf,
"using vec.append(&mut vec) to move the full range of a vecor to another"
}
declare_clippy_lint! {
/// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
/// `&str` or `String`.
@ -1222,7 +1248,7 @@
/// let _ = (0..3).map(|x| x + 2).count();
/// ```
pub SUSPICIOUS_MAP,
complexity,
suspicious,
"suspicious usage of map"
}
@ -1504,7 +1530,7 @@
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
pub FROM_ITER_INSTEAD_OF_COLLECT,
style,
pedantic,
"use `.collect()` instead of `::from_iter()`"
}
@ -1561,6 +1587,29 @@
"call to `filter_map` where `flatten` is sufficient"
}
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
///
/// **Why is this bad?** It can be written more concisely without the call to `map`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_clippy_lint! {
/// **What it does:** Checks for the use of `.bytes().nth()`.
///
@ -1728,6 +1777,7 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
FILTER_NEXT,
SKIP_WHILE_NEXT,
FILTER_MAP_IDENTITY,
MAP_IDENTITY,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
OPTION_FILTER_MAP,
@ -1760,7 +1810,8 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
INSPECT_FOR_EACH,
IMPLICIT_CLONE,
SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT
MANUAL_STR_REPEAT,
APPEND_INSTEAD_OF_EXTEND
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -1985,7 +2036,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv);
},
("and_then", [arg]) => {
@ -2022,7 +2073,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::check(cx, expr, recv),
},
("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg),
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
append_instead_of_extend::check(cx, expr, recv, arg);
},
("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg);
filter_map_identity::check(cx, expr, arg, span);
@ -2058,6 +2112,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
_ => {},
}
}
map_identity::check(cx, expr, recv, m_arg, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
("next", []) => {

View File

@ -87,7 +87,7 @@ fn check_general_case<'tcx>(
];
if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
if path.ident.as_str() == "len" {
if path.ident.name == sym::len {
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
match ty.kind() {

View File

@ -0,0 +1,102 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{def::Res, def_id::DefId, Crate, Item, ItemKind, UseKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
use crate::utils::conf::Rename;
declare_clippy_lint! {
/// **What it does:** Checks for imports that do not rename the item as specified
/// in the `enforce-import-renames` config option.
///
/// **Why is this bad?** Consistency is important, if a project has defined import
/// renames they should be followed. More practically, some item names are too
/// vague outside of their defining scope this can enforce a more meaningful naming.
///
/// **Known problems:** None
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
/// ```
///
/// ```rust,ignore
/// use serde_json::Value;
/// ```
/// Use instead:
/// ```rust,ignore
/// use serde_json::Value as JsonValue;
/// ```
pub MISSING_ENFORCED_IMPORT_RENAMES,
restriction,
"enforce import renames"
}
pub struct ImportRename {
conf_renames: Vec<Rename>,
renames: FxHashMap<DefId, Symbol>,
}
impl ImportRename {
pub fn new(conf_renames: Vec<Rename>) -> Self {
Self {
conf_renames,
renames: FxHashMap::default(),
}
}
}
impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]);
impl LateLintPass<'_> for ImportRename {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for Rename { path, rename } in &self.conf_renames {
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) {
self.renames.insert(id, Symbol::intern(rename));
}
}
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Use(path, UseKind::Single) = &item.kind;
if let Res::Def(_, id) = path.res;
if let Some(name) = self.renames.get(&id);
// Remove semicolon since it is not present for nested imports
let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
if let Some(snip) = snippet_opt(cx, span_without_semi);
if let Some(import) = match snip.split_once(" as ") {
None => Some(snip.as_str()),
Some((import, rename)) => {
if rename.trim() == &*name.as_str() {
None
} else {
Some(import.trim())
}
},
};
then {
span_lint_and_sugg(
cx,
MISSING_ENFORCED_IMPORT_RENAMES,
span_without_semi,
"this import should be renamed",
"try",
format!(
"{} as {}",
import,
name,
),
Applicability::MachineApplicable,
);
}
}
}
}

View File

@ -50,7 +50,7 @@
/// }
/// ```
pub MUTABLE_KEY_TYPE,
correctness,
suspicious,
"Check for mutable `Map`/`Set` key type"
}

View File

@ -167,7 +167,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
BlockCheckMode::DefaultBlock => Some(vec![&**e]),
// in case of compiler-inserted signaling blocks
_ => reduce_expression(cx, e),
BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
}
})
} else {

View File

@ -0,0 +1,276 @@
use std::{
fmt,
hash::{Hash, Hasher},
};
use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use serde::{de, Deserialize};
declare_clippy_lint! {
/// **What it does:** Checks that common macros are used with consistent bracing.
///
/// **Why is this bad?** This is mostly a consistency lint although using () or []
/// doesn't give you a semicolon in item position, which can be unexpected.
///
/// **Known problems:**
/// None
///
/// **Example:**
///
/// ```rust
/// vec!{1, 2, 3};
/// ```
/// Use instead:
/// ```rust
/// vec![1, 2, 3];
/// ```
pub NONSTANDARD_MACRO_BRACES,
style,
"check consistent use of braces in macro"
}
const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
/// The (name, (open brace, close brace), source snippet)
type MacroInfo<'a> = (&'a str, &'a (String, String), String);
#[derive(Clone, Debug, Default)]
pub struct MacroBraces {
macro_braces: FxHashMap<String, (String, String)>,
done: FxHashSet<Span>,
}
impl MacroBraces {
pub fn new(conf: &FxHashSet<MacroMatcher>) -> Self {
let macro_braces = macro_braces(conf.clone());
Self {
macro_braces,
done: FxHashSet::default(),
}
}
}
impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]);
impl EarlyLintPass for MacroBraces {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) {
let span = item.span.ctxt().outer_expn_data().call_site;
emit_help(cx, snip, braces, name, span);
self.done.insert(span);
}
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) {
let span = stmt.span.ctxt().outer_expn_data().call_site;
emit_help(cx, snip, braces, name, span);
self.done.insert(span);
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) {
let span = expr.span.ctxt().outer_expn_data().call_site;
emit_help(cx, snip, braces, name, span);
self.done.insert(span);
}
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) {
let span = ty.span.ctxt().outer_expn_data().call_site;
emit_help(cx, snip, braces, name, span);
self.done.insert(span);
}
}
}
fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option<MacroInfo<'a>> {
if_chain! {
if in_macro(span);
if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces);
if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
let c = snip.replace(" ", ""); // make formatting consistent
if !c.starts_with(&format!("{}!{}", name, braces.0));
if !this.done.contains(&span.ctxt().outer_expn_data().call_site);
then {
Some((name, braces, snip))
} else {
None
}
}
}
fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
let with_space = &format!("! {}", braces.0);
let without_space = &format!("!{}", braces.0);
let mut help = snip;
for b in BRACES.iter().filter(|b| b.0 != braces.0) {
help = help.replace(b.0, &braces.0).replace(b.1, &braces.1);
// Only `{` traditionally has space before the brace
if braces.0 != "{" && help.contains(with_space) {
help = help.replace(with_space, without_space);
} else if braces.0 == "{" && help.contains(without_space) {
help = help.replace(without_space, with_space);
}
}
span_lint_and_help(
cx,
NONSTANDARD_MACRO_BRACES,
span,
&format!("use of irregular braces for `{}!` macro", name),
Some(span),
&format!("consider writing `{}`", help),
);
}
fn find_matching_macro(
span: Span,
braces: &FxHashMap<String, (String, String)>,
) -> Option<(&String, &(String, String))> {
braces
.iter()
.find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
}
fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
let mut braces = vec![
macro_matcher!(
name: "print",
braces: ("(", ")"),
),
macro_matcher!(
name: "println",
braces: ("(", ")"),
),
macro_matcher!(
name: "eprint",
braces: ("(", ")"),
),
macro_matcher!(
name: "eprintln",
braces: ("(", ")"),
),
macro_matcher!(
name: "write",
braces: ("(", ")"),
),
macro_matcher!(
name: "writeln",
braces: ("(", ")"),
),
macro_matcher!(
name: "format",
braces: ("(", ")"),
),
macro_matcher!(
name: "format_args",
braces: ("(", ")"),
),
macro_matcher!(
name: "vec",
braces: ("[", "]"),
),
]
.into_iter()
.collect::<FxHashMap<_, _>>();
// We want users items to override any existing items
for it in conf {
braces.insert(it.name, it.braces);
}
braces
}
macro_rules! macro_matcher {
(name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => {
($name.to_owned(), ($open.to_owned(), $close.to_owned()))
};
}
pub(crate) use macro_matcher;
#[derive(Clone, Debug)]
pub struct MacroMatcher {
name: String,
braces: (String, String),
}
impl Hash for MacroMatcher {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for MacroMatcher {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for MacroMatcher {}
impl<'de> Deserialize<'de> for MacroMatcher {
fn deserialize<D>(deser: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Name,
Brace,
}
struct MacVisitor;
impl<'de> de::Visitor<'de> for MacVisitor {
type Value = MacroMatcher;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct MacroMatcher")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut name = None;
let mut brace: Option<&str> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Name => {
if name.is_some() {
return Err(de::Error::duplicate_field("name"));
}
name = Some(map.next_value()?);
},
Field::Brace => {
if brace.is_some() {
return Err(de::Error::duplicate_field("brace"));
}
brace = Some(map.next_value()?);
},
}
}
let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
Ok(MacroMatcher {
name,
braces: BRACES
.iter()
.find(|b| b.0 == brace)
.map(|(o, c)| ((*o).to_owned(), (*c).to_owned()))
.ok_or_else(|| {
de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace))
})?,
})
}
}
const FIELDS: &[&str] = &["name", "brace"];
deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor)
}
}

View File

@ -329,7 +329,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
if is_integer_const(cx, start, 0);
// `.len()` call
if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind;
if len_path.ident.name == sym!(len) && len_args.len() == 1;
if len_path.ident.name == sym::len && len_args.len() == 1;
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;

View File

@ -132,7 +132,7 @@ fn check_fn(
}
}
// `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
// `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
let loc = mir::Location {

View File

@ -1,3 +1,4 @@
use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{in_macro, sugg};
@ -45,6 +46,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
if t_expr.is_unit();
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
if !snippet.ends_with('}');
if cx.sess().source_map().is_multiline(block.span);
then {
// filter out the desugared `for` loop
if let ExprKind::DropTemps(..) = &expr.kind {

View File

@ -26,7 +26,7 @@
/// }
/// ```
pub SUSPICIOUS_ARITHMETIC_IMPL,
correctness,
suspicious,
"suspicious use of operators in impl of arithmetic trait"
}
@ -47,7 +47,7 @@
/// }
/// ```
pub SUSPICIOUS_OP_ASSIGN_IMPL,
correctness,
suspicious,
"suspicious use of operators in impl of OpAssign trait"
}

View File

@ -1,6 +1,6 @@
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path, eq_maybe_qself};
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
@ -277,7 +277,8 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
ps1, start, alternatives,
|k, ps1, idx| matches!(
k,
TupleStruct(qself2, path2, ps2) if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
TupleStruct(qself2, path2, ps2)
if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
),
|k| always_pat!(k, TupleStruct(_, _, ps) => ps),
),

View File

@ -1,23 +1,22 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::same_type_and_consts;
use clippy_utils::{in_macro, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{
self as hir,
def::{self, DefKind},
def::{CtorOf, DefKind, Res},
def_id::LocalDefId,
intravisit::{walk_ty, NestedVisitorMap, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
QPath, TyKind,
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::{AssocKind, Ty};
use rustc_middle::ty::AssocKind;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Span};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;
declare_clippy_lint! {
@ -75,10 +74,9 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
#[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>,
impl_id: LocalDefId,
in_body: u32,
types_to_skip: FxHashSet<HirId>,
},
NoCheck,
}
@ -88,60 +86,41 @@ enum StackItem {
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
impl<'tcx> LateLintPass<'tcx> for UseSelf {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
if !is_item_interesting(item) {
// This does two things:
// 1) Reduce needless churn on `self.stack`
// 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
// in order to lint `foo() -> impl <..>`
return;
}
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
// relevant for linting, since this is the self type of the `impl` we're currently in. To
// 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
//
// 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(_, 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);
let stack_item = if_chain! {
if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
if parameters.as_ref().map_or(true, |params| {
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
});
then {
StackItem::Check {
impl_id: item.def_id,
in_body: 0,
types_to_skip: std::iter::once(self_ty.hir_id).collect(),
}
},
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Fn(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..) => {
self.stack.push(StackItem::NoCheck);
},
_ => (),
}
} else {
StackItem::NoCheck
}
};
self.stack.push(stack_item);
}
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
match item.kind {
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
self.stack.pop();
},
_ => (),
if is_item_interesting(item) {
self.stack.pop();
}
}
@ -151,11 +130,11 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_
if_chain! {
if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
if let Some(&mut StackItem::Check {
impl_trait_ref_def_id: Some(def_id),
impl_id,
ref mut types_to_skip,
..
}) = self.stack.last_mut();
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`.
@ -203,142 +182,76 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_
}
}
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
fn check_body(&mut self, _: &LateContext<'_>, _: &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);
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
*in_body = in_body.saturating_add(1);
}
}
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_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
*in_body = in_body.saturating_sub(1);
}
}
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(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
{
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
if_chain! {
if !in_macro(hir_ty.span);
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check {
impl_id,
in_body,
ref types_to_skip,
}) = self.stack.last();
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
if !types_to_skip.contains(&hir_ty.hir_id);
let ty = if in_body > 0 {
cx.typeck_results().node_type(hir_ty.hir_id)
} 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
hir_ty_to_ty(cx.tcx, hir_ty)
};
if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
let hir = cx.tcx.hir();
let id = hir.get_parent_node(hir_ty.hir_id);
if !hir.opt_span(id).map_or(false, in_macro) {
match hir.find(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),
}
if !hir.opt_span(id).map_or(false, in_macro);
then {
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_chain! {
if !in_macro(expr.span);
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
then {} else { return; }
}
if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
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);
},
match expr.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
Res::SelfTy(..) => (),
Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
_ => span_lint(cx, path.span),
},
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
ExprKind::Call(fun, _) => {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
match ctor_of {
CtorOf::Variant => lint_path_to_variant(cx, path),
CtorOf::Struct => 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);
}
},
_ => (),
}
}
},
// unit enum variants (`Enum::A`)
ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
_ => (),
}
}
@ -364,35 +277,6 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
}
}
struct LintTyCollector<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
self_ty: Ty<'tcx>,
types_to_lint: Vec<HirId>,
types_to_skip: Vec<HirId>,
}
impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
if_chain! {
if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
if should_lint_ty(hir_ty, ty, self.self_ty);
then {
self.types_to_lint.push(hir_ty.hir_id);
} else {
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
}
}
fn span_lint(cx: &LateContext<'_>, span: Span) {
span_lint_and_sugg(
cx,
@ -405,66 +289,19 @@ fn span_lint(cx: &LateContext<'_>, span: Span) {
);
}
#[allow(clippy::cast_possible_truncation)]
fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
let sp = span.with_hi(segment.ident.span.lo());
// remove the trailing ::
let span_without_last_segment = match snippet_opt(cx, sp) {
Some(snippet) => match snippet.rfind("::") {
Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
None => sp,
},
None => sp,
};
span_lint(cx, span_without_last_segment);
}
fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
if path.segments.len() > 1 {
span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
if let [.., self_seg, _variant] = path.segments {
let span = path
.span
.with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
span_lint(cx, span);
}
}
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);
}
}
}
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) {
hir_ty_to_ty(cx.tcx, hir_ty)
} else {
unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
}
}
fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
let map = cx.tcx.hir();
let parent = map.get_parent_node(hir_ty.hir_id);
if_chain! {
if let Some(Node::Item(item)) = map.find(parent);
if let ItemKind::Impl { .. } = item.kind;
then {
true
} else {
false
}
}
}
fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
if_chain! {
if same_type_and_consts(ty, self_ty);
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
then {
!matches!(path.res, def::Res::SelfTy(..))
} else {
false
}
}
fn is_item_interesting(item: &Item<'_>) -> bool {
use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
matches!(
item.kind,
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
)
}

View File

@ -104,7 +104,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
}
}
if_chain! {
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into";
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && name.ident.name == sym::try_into;
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, a, sym::result_type);

View File

@ -8,6 +8,13 @@
use std::path::{Path, PathBuf};
use std::{env, fmt, fs, io};
/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
#[derive(Clone, Debug, Deserialize)]
pub struct Rename {
pub path: String,
pub rename: String,
}
/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
@ -24,6 +31,9 @@ fn from_error(error: impl Error) -> Self {
}
}
/// Note that the configuration parsing currently doesn't support documentation that will
/// that spans over several lines. This will be possible with the new implementation
/// See (rust-clippy#7172)
macro_rules! define_Conf {
($(
#[doc = $doc:literal]
@ -149,7 +159,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
"WebGL",
"TensorFlow",
"TrueType",
"iOS", "macOS",
"iOS", "macOS", "FreeBSD",
"TeX", "LaTeX", "BibTeX", "BibLaTeX",
"MinGW",
"CamelCase",
@ -182,20 +192,28 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
(vec_box_size_threshold: u64 = 4096),
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
(max_trait_bounds: u64 = 3),
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have
(max_struct_bools: u64 = 3),
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have
(max_fn_params_bools: u64 = 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports: bool = false),
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<String> = Vec::new()),
/// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
(disallowed_types: Vec<String> = Vec::new()),
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
(unreadable_literal_lint_fractions: bool = true),
/// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
(upper_case_acronyms_aggressive: bool = false),
/// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
(cargo_ignore_publish: bool = false),
/// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. <br> A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name.
(standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
/// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename.
(enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
/// Lint: RESTRICTED_SCRIPTS. The list of unicode scripts allowed to be used in the scope.
(allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
}
/// Search for the configuration file.

View File

@ -9,6 +9,7 @@
//! a simple mistake)
use if_chain::if_chain;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
@ -46,8 +47,9 @@
const DEPRECATED_LINT_LEVEL: &str = "none";
/// This array holds Clippy's lint groups with their corresponding default lint level. The
/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [
const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
("correctness", "deny"),
("suspicious", "warn"),
("restriction", "allow"),
("style", "warn"),
("pedantic", "allow"),
@ -485,16 +487,32 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<St
///
/// Would result in `Hello world!\n=^.^=\n`
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
cx.tcx
.hir()
.attrs(item.hir_id())
.iter()
.filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string()))
.reduce(|mut acc, sym| {
acc.push_str(&sym);
acc.push('\n');
acc
})
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
let mut docs = String::from(&*lines.next()?.as_str());
let mut in_code_block = false;
for line in lines {
docs.push('\n');
let line = line.as_str();
let line = &*line;
if let Some(info) = line.trim_start().strip_prefix("```") {
in_code_block = !in_code_block;
if in_code_block {
let lang = info
.trim()
.split(',')
// remove rustdoc directives
.find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
// if no language is present, fill in "rust"
.unwrap_or("rust");
docs.push_str("```");
docs.push_str(lang);
continue;
}
}
docs.push_str(line);
}
Some(docs)
}
fn get_lint_group_and_level_or_lint(

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{in_macro, is_test_module_or_function};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
@ -106,7 +106,7 @@ pub fn new(warn_on_all: bool) -> Self {
impl LateLintPass<'_> for WildcardImports {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_test_module_or_function(item) {
if is_test_module_or_function(cx.tcx, item) {
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
}
if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
@ -183,8 +183,8 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
}
}
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
if is_test_module_or_function(item) {
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_test_module_or_function(cx.tcx, item) {
self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
}
}
@ -208,7 +208,3 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
segments.len() == 1 && segments[0].ident.name == kw::Super
}
fn is_test_module_or_function(item: &Item<'_>) -> bool {
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
}

View File

@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
version = "0.1.54"
version = "0.1.55"
authors = ["The Rust Clippy Developers"]
edition = "2018"
publish = false

View File

@ -47,9 +47,14 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
},
(Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
lr == rr && eq_maybe_qself(lqself, rqself) &&eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
lr == rr
&& eq_maybe_qself(lqself, rqself)
&& eq_path(lp, rp)
&& unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
},
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@ -82,7 +87,7 @@ pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
match (l, r) {
(Some(l), Some(r)) => eq_qself(l, r),
(None, None) => true,
_ => false
_ => false,
}
}

View File

@ -157,3 +157,8 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
.filter_map(ast::Attribute::meta_item_list)
.any(|l| attr::list_contains_name(&l, sym::hidden))
}
/// Return true if the attributes contain `#[unstable]`
pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
attrs.iter().any(|attr| attr.has_name(sym::unstable))
}

View File

@ -1,6 +1,6 @@
#![allow(clippy::float_cmp)]
use crate::{clip, sext, unsext};
use crate::{clip, is_direct_expn_of, sext, unsext};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
@ -230,7 +230,13 @@ 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(block, _) => self.block(block),
ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
ExprKind::Lit(ref lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() {
None
} else {
Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
}
},
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(value, _) => {

View File

@ -72,7 +72,7 @@
use rustc_hir::{
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
}
/// Checks if an expression references a variable of the given name.
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
if let [p] = path.segments {
return p.ident.name == var;
}
}
false
}
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
match *path {
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
@ -707,16 +697,6 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
}
}
/// Gets the name of a `Pat`, if any.
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
match pat.kind {
PatKind::Binding(.., ref spname, _) => Some(spname.name),
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
_ => None,
}
}
pub struct ContainsName {
pub name: Symbol,
pub result: bool,
@ -861,14 +841,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
})
}
/// Gets the loop enclosing the given expression, if any.
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
/// Gets the loop or closure enclosing the given expression, if any.
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
let map = tcx.hir();
for (_, node) in map.parent_iter(expr.hir_id) {
match node {
Node::Expr(
e @ Expr {
kind: ExprKind::Loop(..),
e
@
Expr {
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
..
},
) => return Some(e),
@ -1399,6 +1381,55 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
}
/// Checks if an expression represents the identity function
/// Only examines closures and `std::convert::identity`
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
/// * `|x| x`
/// * `|x| return x`
/// * `|x| { return x }`
/// * `|x| { return x; }`
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let id = if_chain! {
if let [param] = func.params;
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
then {
id
} else {
return false;
}
};
let mut expr = &func.value;
loop {
match expr.kind {
#[rustfmt::skip]
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
| ExprKind::Ret(Some(e)) => expr = e,
#[rustfmt::skip]
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
if_chain! {
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
if let ExprKind::Ret(Some(ret_val)) = e.kind;
then {
expr = ret_val;
} else {
return false;
}
}
},
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
}
}
}
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
_ => false,
}
}
/// Gets the node where an expression is either used, or it's type is unified with another branch.
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let map = tcx.hir();
@ -1654,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
(e, count)
}
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
loop {
match expr.kind {
ExprKind::AddrOf(_, _, e) => expr = e,
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
_ => break,
}
}
expr
}
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{
@ -1683,3 +1727,15 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
}
}
}
/// Checks whether item either has `test` attribute applied, or
/// is a module with `test` in its name.
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
if tcx.has_attr(def_id.to_def_id(), sym::test) {
return true;
}
}
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
}

View File

@ -1,10 +1,10 @@
use crate::source::snippet;
use crate::{get_pat_name, match_var};
use crate::{path_to_local_id, strip_pat_refs};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::{Span, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
pub fn get_spans(
@ -14,10 +14,11 @@ pub fn get_spans(
replacements: &[(&'static str, &'static str)],
) -> Option<Vec<(Span, Cow<'static, str>)>> {
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
get_binding_name(&body.params[idx]).map_or_else(
|| Some(vec![]),
|name| extract_clone_suggestions(cx, name, replacements, body),
)
if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
extract_clone_suggestions(cx, binding_id, replacements, body)
} else {
Some(vec![])
}
} else {
Some(vec![])
}
@ -25,13 +26,13 @@ pub fn get_spans(
fn extract_clone_suggestions<'tcx>(
cx: &LateContext<'tcx>,
name: Symbol,
id: HirId,
replace: &[(&'static str, &'static str)],
body: &'tcx Body<'_>,
) -> Option<Vec<(Span, Cow<'static, str>)>> {
let mut visitor = PtrCloneVisitor {
cx,
name,
id,
replace,
spans: vec![],
abort: false,
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
struct PtrCloneVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
name: Symbol,
id: HirId,
replace: &'a [(&'static str, &'static str)],
spans: Vec<(Span, Cow<'static, str>)>,
abort: bool,
@ -55,16 +56,15 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if self.abort {
return;
}
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
if args.len() == 1 && match_var(&args[0], self.name) {
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
if path_to_local_id(recv, self.id) {
if seg.ident.name.as_str() == "capacity" {
self.abort = true;
return;
}
for &(fn_name, suffix) in self.replace {
if seg.ident.name.as_str() == fn_name {
self.spans
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
return;
}
}
@ -77,7 +77,3 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
get_pat_name(arg.pat)
}

View File

@ -90,8 +90,10 @@ cargo dev fmt
cargo dev update_lints
# create a new lint and register it
cargo dev new_lint
# automatically formatting all code before each commit
cargo dev setup git-hook
# (experimental) Setup Clippy to work with IntelliJ-Rust
cargo dev ide_setup
cargo dev setup intellij
```
## lintcheck

View File

@ -6,7 +6,7 @@ You may need following tooltips to catch up with common operations.
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
- [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
- [Checking if a type defines a method](#checking-if-a-type-defines-a-method)
- [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
- [Dealing with macros](#dealing-with-macros)
Useful Rustc dev guide links:

View File

@ -101,6 +101,21 @@ After this, the release should be available on the Clippy [release page].
[release page]: https://github.com/rust-lang/rust-clippy/releases
## Update the `stable` branch
At this step you should have already checked out the commit of the `rust-1.XX.0`
tag. Updating the stable branch from here is as easy as:
```bash
# Assuming the current directory corresponds to the Clippy repository and the
# commit of the just created rust-1.XX.0 tag is checked out.
$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote
```
_NOTE: Usually there are no stable backports for Clippy, so this update should
be possible without force pushing or anything like this. If there should have
happened a stable backport, make sure to re-merge those changes just as with the
`beta` branch._
## Update `CHANGELOG.md`

View File

@ -69,7 +69,7 @@ is checked.
is explicitly specified in the options.
### Fix mode
You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `-Zunstable-options --fix` and
You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
print a warning if Clippys suggestions fail to apply (if the resulting code does not build).
This lets us spot bad suggestions or false positives automatically in some cases.

View File

@ -260,14 +260,7 @@ fn run_clippy_lints(
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
let mut args = if fix {
vec![
"-Zunstable-options",
"--fix",
"-Zunstable-options",
"--allow-no-vcs",
"--",
"--cap-lints=warn",
]
vec!["--fix", "--allow-no-vcs", "--", "--cap-lints=warn"]
} else {
vec!["--", "--message-format=json", "--", "--cap-lints=warn"]
};

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2021-06-03"
channel = "nightly-2021-07-01"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]

View File

@ -70,7 +70,6 @@ fn new<I>(mut old_args: I) -> Self
I: Iterator<Item = String>,
{
let mut cargo_subcommand = "check";
let mut unstable_options = false;
let mut args = vec![];
for arg in old_args.by_ref() {
@ -80,18 +79,12 @@ fn new<I>(mut old_args: I) -> Self
continue;
},
"--" => break,
// Cover -Zunstable-options and -Z unstable-options
s if s.ends_with("unstable-options") => unstable_options = true,
_ => {},
}
args.push(arg);
}
if cargo_subcommand == "fix" && !unstable_options {
panic!("Usage of `--fix` requires `-Z unstable-options`");
}
let mut clippy_args: Vec<String> = old_args.collect();
if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
clippy_args.push("--no-deps".into());
@ -176,34 +169,23 @@ mod tests {
use super::ClippyCmd;
#[test]
#[should_panic]
fn fix_without_unstable() {
fn fix() {
let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
ClippyCmd::new(args);
}
#[test]
fn fix_unstable() {
let args = "cargo clippy --fix -Zunstable-options"
.split_whitespace()
.map(ToString::to_string);
let cmd = ClippyCmd::new(args);
assert_eq!("fix", cmd.cargo_subcommand);
assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
}
#[test]
fn fix_implies_no_deps() {
let args = "cargo clippy --fix -Zunstable-options"
.split_whitespace()
.map(ToString::to_string);
let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
let cmd = ClippyCmd::new(args);
assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
}
#[test]
fn no_deps_not_duplicated_with_fix() {
let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
let args = "cargo clippy --fix -- --no-deps"
.split_whitespace()
.map(ToString::to_string);
let cmd = ClippyCmd::new(args);

View File

@ -48,7 +48,24 @@ fn third_party_crates() -> String {
&& name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
{
if let Some(old) = crates.insert(dep, path.clone()) {
panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
// Check which action should be done in order to remove compiled deps.
// If pre-installed version of compiler is used, `cargo clean` will do.
// Otherwise (for bootstrapped compiler), the dependencies directory
// must be removed manually.
let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
"remove the stageN-tools directory"
} else {
"run `cargo clean`"
};
panic!(
"\n---------------------------------------------------\n\n \
Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \
Probably, you need to {} before running tests again.\n \
\nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \
\n---------------------------------------------------\n",
dep, old, path, suggested_action
);
}
break;
}

View File

@ -0,0 +1,10 @@
enforced-import-renames = [
{ path = "std::option::Option", rename = "Maybe" },
{ path = "std::process::Child", rename = "Kid" },
{ path = "std::process::exit", rename = "goodbye" },
{ path = "std::collections::BTreeMap", rename = "Map" },
{ path = "std::clone", rename = "foo" },
{ path = "std::thread::sleep", rename = "thread_sleep" },
{ path = "std::any::type_name", rename = "ident" },
{ path = "std::sync::Mutex", rename = "StdMutie" }
]

View File

@ -0,0 +1,16 @@
#![warn(clippy::missing_enforced_import_renames)]
use std::alloc as colla;
use std::option::Option as Maybe;
use std::process::{exit as wrong_exit, Child as Kid};
use std::thread::sleep;
#[rustfmt::skip]
use std::{
any::{type_name, Any},
clone,
sync :: Mutex,
};
fn main() {
use std::collections::BTreeMap as OopsWrongRename;
}

View File

@ -0,0 +1,40 @@
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:5:20
|
LL | use std::process::{exit as wrong_exit, Child as Kid};
| ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye`
|
= note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings`
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:6:1
|
LL | use std::thread::sleep;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep`
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:9:11
|
LL | any::{type_name, Any},
| ^^^^^^^^^ help: try: `type_name as ident`
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:10:5
|
LL | clone,
| ^^^^^ help: try: `clone as foo`
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:11:5
|
LL | sync :: Mutex,
| ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie`
error: this import should be renamed
--> $DIR/conf_missing_enforced_import_rename.rs:15:5
|
LL | use std::collections::BTreeMap as OopsWrongRename;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map`
error: aborting due to 6 previous errors

View File

@ -0,0 +1,6 @@
standard-macro-braces = [
{ name = "quote", brace = "{" },
{ name = "quote::quote", brace = "{" },
{ name = "eprint", brace = "[" },
{ name = "type_pos", brace = "[" },
]

View File

@ -0,0 +1,44 @@
// #![warn(clippy::nonstandard_macro_braces)]
extern crate quote;
use quote::quote;
#[rustfmt::skip]
macro_rules! test {
() => {
vec!{0, 0, 0}
};
}
#[rustfmt::skip]
macro_rules! test2 {
($($arg:tt)*) => {
format_args!($($arg)*)
};
}
macro_rules! type_pos {
($what:ty) => {
Vec<$what>
};
}
#[rustfmt::skip]
fn main() {
let _ = vec! {1, 2, 3};
let _ = format!["ugh {} stop being such a good compiler", "hello"];
let _ = quote!(let x = 1;);
let _ = quote::quote!(match match match);
let _ = test!();
let _ = vec![1,2,3];
let _ = quote::quote! {true || false};
let _ = vec! [0 ,0 ,0];
let _ = format!("fds{}fds", 10);
let _ = test2!["{}{}{}", 1, 2, 3];
let _: type_pos!(usize) = vec![];
eprint!("test if user config overrides defaults");
}

View File

@ -0,0 +1,94 @@
error: use of irregular braces for `vec!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:29:13
|
LL | let _ = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings`
help: consider writing `vec![1, 2, 3]`
--> $DIR/conf_nonstandard_macro_braces.rs:29:13
|
LL | let _ = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
error: use of irregular braces for `format!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:30:13
|
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
--> $DIR/conf_nonstandard_macro_braces.rs:30:13
|
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:31:13
|
LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^
|
help: consider writing `quote! {let x = 1;}`
--> $DIR/conf_nonstandard_macro_braces.rs:31:13
|
LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote::quote!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:32:13
|
LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `quote::quote! {match match match}`
--> $DIR/conf_nonstandard_macro_braces.rs:32:13
|
LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `vec!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:10:9
|
LL | vec!{0, 0, 0}
| ^^^^^^^^^^^^^
...
LL | let _ = test!();
| ------- in this macro invocation
|
help: consider writing `vec![0, 0, 0]`
--> $DIR/conf_nonstandard_macro_braces.rs:10:9
|
LL | vec!{0, 0, 0}
| ^^^^^^^^^^^^^
...
LL | let _ = test!();
| ------- in this macro invocation
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
error: use of irregular braces for `type_pos!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:41:12
|
LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^
|
help: consider writing `type_pos![usize]`
--> $DIR/conf_nonstandard_macro_braces.rs:41:12
|
LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^
error: use of irregular braces for `eprint!` macro
--> $DIR/conf_nonstandard_macro_braces.rs:43:5
|
LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `eprint!["test if user config overrides defaults"];`
--> $DIR/conf_nonstandard_macro_braces.rs:43:5
|
LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors

View File

@ -1 +1,5 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"]
disallowed-methods = [
"std::iter::Iterator::sum",
"regex::Regex::is_match",
"regex::Regex::new"
]

View File

@ -0,0 +1,9 @@
disallowed-types = [
"std::collections::HashMap",
"std::sync::atomic::AtomicU32",
"syn::TypePath",
"proc_macro2::Ident",
"std::thread::Thread",
"std::time::Instant",
"std::io::Read",
]

View File

@ -0,0 +1,35 @@
#![warn(clippy::disallowed_type)]
extern crate quote;
extern crate syn;
use std::sync as foo;
use std::sync::atomic::AtomicU32;
use std::time::Instant as Sneaky;
struct HashMap;
fn bad_return_type() -> fn() -> Sneaky {
todo!()
}
fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
todo!()
}
fn trait_obj(_: &dyn std::io::Read) {
todo!()
}
static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
#[allow(clippy::diverging_sub_expression)]
fn main() {
let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
let _ = Sneaky::now();
let _ = foo::atomic::AtomicU32::new(0);
static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
let _ = syn::Ident::new("", todo!());
let _ = HashMap;
}

View File

@ -0,0 +1,88 @@
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:7:1
|
LL | use std::sync::atomic::AtomicU32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-type` implied by `-D warnings`
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:8:1
|
LL | use std::time::Instant as Sneaky;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:12:33
|
LL | fn bad_return_type() -> fn() -> Sneaky {
| ^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:28
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:39
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^^^^^^^^^^^^^^^^^
error: `std::io::Read` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:20:22
|
LL | fn trait_obj(_: &dyn std::io::Read) {
| ^^^^^^^^^^^^^
error: `std::collections::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:48
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::collections::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:12
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:29:13
|
LL | let _ = Sneaky::now();
| ^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:30:13
|
LL | let _ = foo::atomic::AtomicU32::new(0);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:17
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:48
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `syn::TypePath` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:32:43
|
LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
| ^^^^^^^^^^^^^
error: `proc_macro2::Ident` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:33:13
|
LL | let _ = syn::Ident::new("", todo!());
| ^^^^^^^^^^
error: aborting due to 14 previous errors

View File

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `third-party` at line 5 column 1
error: aborting due to previous error

View File

@ -0,0 +1,55 @@
// run-rustfix
#![warn(clippy::append_instead_of_extend)]
use std::collections::BinaryHeap;
fn main() {
//gets linted
let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.append(&mut vec1);
let mut vec3 = vec![0u8; 1024];
let mut vec4: std::vec::Vec<u8> = Vec::new();
vec4.append(&mut vec3);
let mut vec11: std::vec::Vec<u8> = Vec::new();
vec11.append(&mut return_vector());
//won't get linted it dosen't move the entire content of a vec into another
let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new();
test2.extend(test1.drain(4..10));
let mut vec3 = vec![0u8; 104];
let mut vec7: std::vec::Vec<u8> = Vec::new();
vec3.append(&mut vec7);
let mut vec5 = vec![0u8; 1024];
let mut vec6: std::vec::Vec<u8> = Vec::new();
vec5.extend(vec6.drain(..4));
let mut vec9: std::vec::Vec<u8> = Vec::new();
return_vector().append(&mut vec9);
//won't get linted because it is not a vec
let mut heap = BinaryHeap::from(vec![1, 3]);
let mut heap2 = BinaryHeap::from(vec![]);
heap2.extend(heap.drain())
}
fn return_vector() -> Vec<u8> {
let mut new_vector = vec![];
for i in 1..10 {
new_vector.push(i)
}
new_vector
}

View File

@ -0,0 +1,55 @@
// run-rustfix
#![warn(clippy::append_instead_of_extend)]
use std::collections::BinaryHeap;
fn main() {
//gets linted
let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.extend(vec1.drain(..));
let mut vec3 = vec![0u8; 1024];
let mut vec4: std::vec::Vec<u8> = Vec::new();
vec4.extend(vec3.drain(..));
let mut vec11: std::vec::Vec<u8> = Vec::new();
vec11.extend(return_vector().drain(..));
//won't get linted it dosen't move the entire content of a vec into another
let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new();
test2.extend(test1.drain(4..10));
let mut vec3 = vec![0u8; 104];
let mut vec7: std::vec::Vec<u8> = Vec::new();
vec3.append(&mut vec7);
let mut vec5 = vec![0u8; 1024];
let mut vec6: std::vec::Vec<u8> = Vec::new();
vec5.extend(vec6.drain(..4));
let mut vec9: std::vec::Vec<u8> = Vec::new();
return_vector().append(&mut vec9);
//won't get linted because it is not a vec
let mut heap = BinaryHeap::from(vec![1, 3]);
let mut heap2 = BinaryHeap::from(vec![]);
heap2.extend(heap.drain())
}
fn return_vector() -> Vec<u8> {
let mut new_vector = vec![];
for i in 1..10 {
new_vector.push(i)
}
new_vector
}

View File

@ -0,0 +1,22 @@
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:9:5
|
LL | vec2.extend(vec1.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)`
|
= note: `-D clippy::append-instead-of-extend` implied by `-D warnings`
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:14:5
|
LL | vec4.extend(vec3.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)`
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:18:5
|
LL | vec11.extend(return_vector().drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())`
error: aborting due to 3 previous errors

View File

@ -28,4 +28,7 @@ fn main() {
debug_assert!(false); // #3948
assert_const!(3);
assert_const!(-1);
// Don't lint on this:
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
}

View File

@ -106,3 +106,10 @@ fn lint() {
}
};
}
#[macro_export]
macro_rules! default_numeric_fallback {
() => {
let x = 22;
};
}

View File

@ -0,0 +1,8 @@
// Stripped down version of the ErrorKind enum of std
#[non_exhaustive]
pub enum ErrorKind {
NotFound,
PermissionDenied,
#[doc(hidden)]
Uncategorized,
}

View File

@ -43,3 +43,15 @@ fn issue_1647_ref_mut() {
let ref mut baz = 0;
if let Some(ref mut quux) = Some(42) {}
}
mod tests {
fn issue_7305() {
// `blackisted_name` lint should not be triggered inside of the test code.
let foo = 0;
// Check that even in nested functions warning is still not triggere.
fn nested() {
let foo = 0;
}
}
}

View File

@ -1,3 +1,5 @@
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
#![allow(unused)]
#![allow(clippy::never_loop)]
@ -5,6 +7,9 @@
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::branches_sharing_code)]
#[macro_use]
extern crate macro_rules;
mod basic_expr {
fn test() {
// Should lint unsuffixed literals typed `i32`.
@ -133,4 +138,22 @@ fn test() {
}
}
mod in_macro {
macro_rules! internal_macro {
() => {
let x = 22;
};
}
// Should lint in internal macro.
fn internal() {
internal_macro!();
}
// Should NOT lint in external macro.
fn external() {
default_numeric_fallback!();
}
}
fn main() {}

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