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 working-directory: clippy_workspace_tests
- name: Test cargo-clippy --fix - 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 working-directory: clippy_workspace_tests
- name: Test clippy-driver - name: Test clippy-driver

View file

@ -90,11 +90,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2.3.3 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 - name: Install toolchain
run: rustup show active-toolchain run: rustup show active-toolchain
@ -139,7 +134,7 @@ jobs:
working-directory: clippy_workspace_tests working-directory: clippy_workspace_tests
- name: Test cargo-clippy --fix - 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 working-directory: clippy_workspace_tests
- name: Test clippy-driver - name: Test clippy-driver

View file

@ -22,7 +22,7 @@ jobs:
uses: actions/setup-node@v1.4.4 uses: actions/setup-node@v1.4.4
- name: Install remark - 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 # Run
- name: Check *.md files - name: Check *.md files

View file

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

View file

@ -6,11 +6,139 @@ document.
## Unreleased / In Rust Nightly ## 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 ## 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) [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 ## 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) [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 --> <!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`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 [`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 [`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 [`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 [`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_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 [`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_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 [`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 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_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 [`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_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_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_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_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 [`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 [`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 [`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 [`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 [`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 [`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 [`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/`. `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 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. 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. you just cloned.
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to 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. 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] [package]
name = "clippy" name = "clippy"
version = "0.1.54" version = "0.1.55"
authors = ["The Rust Clippy Developers"] authors = ["The Rust Clippy Developers"]
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" 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). 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. You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
| Category | Description | Default level | | Category | Description | Default level |
| --------------------- | ----------------------------------------------------------------------- | ------------- | | --------------------- | ----------------------------------------------------------------------------------- | ------------- |
| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** | | `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or very useless | **deny** | | `clippy::correctness` | code that is outright wrong or useless | **deny** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** | | `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** | | `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** | | `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | | `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::nursery` | new lints that are still under development | allow | | `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::cargo` | lints for the cargo manifest | 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! 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 #### Automatically applying Clippy suggestions
Clippy can automatically apply some lint suggestions. Clippy can automatically apply some lint suggestions, just like the compiler.
Note that this is still experimental and only supported on the nightly channel:
```terminal ```terminal
cargo clippy --fix -Z unstable-options cargo clippy --fix
``` ```
#### Workspaces #### Workspaces

View file

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

View file

@ -60,11 +60,7 @@ pub fn run(check: bool, verbose: bool) {
let entry = entry?; let entry = entry?;
let path = entry.path(); let path = entry.path();
if path.extension() != Some("rs".as_ref()) if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
|| entry.file_name() == "ice-3891.rs"
// Avoid rustfmt bug rust-lang/rustfmt#1873
|| cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
{
continue; continue;
} }
@ -90,7 +86,7 @@ pub fn run(check: bool, verbose: bool) {
}, },
CliError::RaSetupActive => { CliError::RaSetupActive => {
eprintln!( 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! Not formatting because that would format the local repo as well!
Please revert the changes to Cargo.tomls first." 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 @@ use walkdir::WalkDir;
pub mod bless; pub mod bless;
pub mod fmt; pub mod fmt;
pub mod ide_setup;
pub mod new_lint; pub mod new_lint;
pub mod serve; pub mod serve;
pub mod setup;
pub mod stderr_length_check; pub mod stderr_length_check;
pub mod update_lints; pub mod update_lints;

View file

@ -2,8 +2,8 @@
// warn on lints, that are included in `rust-lang/rust`s bootstrap // warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints}; use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
fn main() { fn main() {
let matches = get_clap_config(); let matches = get_clap_config();
@ -36,7 +36,22 @@ fn main() {
("limit_stderr_length", _) => { ("limit_stderr_length", _) => {
stderr_length_check::check(); 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)) => { ("serve", Some(matches)) => {
let port = matches.value_of("port").unwrap().parse().unwrap(); let port = matches.value_of("port").unwrap().parse().unwrap();
let lint = matches.value_of("lint"); let lint = matches.value_of("lint");
@ -48,6 +63,7 @@ fn main() {
fn get_clap_config<'a>() -> ArgMatches<'a> { fn get_clap_config<'a>() -> ArgMatches<'a> {
App::new("Clippy developer tooling") App::new("Clippy developer tooling")
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand( .subcommand(
SubCommand::with_name("bless") SubCommand::with_name("bless")
.about("bless the test output changes") .about("bless the test output changes")
@ -123,6 +139,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.possible_values(&[ .possible_values(&[
"style", "style",
"correctness", "correctness",
"suspicious",
"complexity", "complexity",
"perf", "perf",
"pedantic", "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."), .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
) )
.subcommand( .subcommand(
SubCommand::with_name("ide_setup") SubCommand::with_name("setup")
.about("Alter dependencies so Intellij Rust can find rustc internals") .about("Support for setting up your personal development environment")
.arg( .setting(AppSettings::ArgRequiredElseHelp)
Arg::with_name("rustc-repo-path") .subcommand(
.long("repo-path") SubCommand::with_name("intellij")
.short("r") .about("Alter dependencies so Intellij Rust can find rustc internals")
.help("The path to a rustc repo that will be used for setting the dependencies") .arg(
.takes_value(true) Arg::with_name("rustc-repo-path")
.value_name("path") .long("repo-path")
.required(true), .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( .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: // clippy::all should only include the following lint groups:
let all_group_lints = usable_lints.iter().filter(|l| { 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) gen_lint_group_list(all_group_lints)

View file

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

View file

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

View file

@ -173,7 +173,7 @@ declare_clippy_lint! {
/// #![deny(clippy::as_conversions)] /// #![deny(clippy::as_conversions)]
/// ``` /// ```
pub BLANKET_CLIPPY_RESTRICTION_LINTS, pub BLANKET_CLIPPY_RESTRICTION_LINTS,
style, suspicious,
"enabling the complete restriction group" "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_data_structures::fx::FxHashSet;
use rustc_hir::{Pat, PatKind}; use rustc_hir::{Item, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -25,18 +25,37 @@ declare_clippy_lint! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BlacklistedName { pub struct BlacklistedName {
blacklist: FxHashSet<String>, blacklist: FxHashSet<String>,
test_modules_deep: u32,
} }
impl BlacklistedName { impl BlacklistedName {
pub fn new(blacklist: FxHashSet<String>) -> Self { 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_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
impl<'tcx> LateLintPass<'tcx> for BlacklistedName { 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<'_>) { 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 let PatKind::Binding(.., ident, _) = pat.kind {
if self.blacklist.contains(&ident.name.to_string()) { if self.blacklist.contains(&ident.name.to_string()) {
span_lint( span_lint(
@ -48,4 +67,10 @@ impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
} }
} }
} }
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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::match_type; 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 if_chain::if_chain;
use rustc_errors::Applicability; 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_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, UintTy}; use rustc_middle::ty::{self, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
use rustc_span::Symbol;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for naive byte counts /// **What it does:** Checks for naive byte counts
@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
impl<'tcx> LateLintPass<'tcx> for ByteCount { impl<'tcx> LateLintPass<'tcx> for ByteCount {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { 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.ident.name == sym!(count);
if count_args.len() == 1; if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
if filter.ident.name == sym!(filter); if filter.ident.name == sym!(filter);
if filter_args.len() == 2; if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
let body = cx.tcx.hir().body(body_id); let body = cx.tcx.hir().body(body_id);
if body.params.len() == 1; if let [param] = body.params;
if let Some(argname) = get_pat_name(body.params[0].pat); if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind; if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq; if op.node == BinOpKind::Eq;
if match_type(cx, 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); &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 { 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, _) = let haystack = if let ExprKind::MethodCall(path, _, args, _) =
filter_args[0].kind { filter_recv.kind {
let p = path.ident.name; let p = path.ident.name;
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0] &args[0]
} else { } else {
&filter_args[0] &filter_recv
} }
} else { } else {
&filter_args[0] &filter_recv
}; };
let mut applicability = Applicability::MaybeIncorrect; let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg( span_lint_and_sugg(
@ -91,24 +92,3 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
}; };
} }
} }
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::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor; 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 if_chain::if_chain;
use rustc_hir::LangItem::OptionNone; 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_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span}; 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()); if arms_inner.iter().all(|arm| arm.guard.is_none());
// match expression must be a local binding // match expression must be a local binding
// match <local> { .. } // 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" // 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)); 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) = let (wild_inner_arm, non_wild_inner_arm) =
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
}); });
result 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_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
@ -122,7 +121,7 @@ impl LateLintPass<'_> for Default {
if let StmtKind::Local(local) = stmt.kind; if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init; if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); 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 // only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()` // only when assigning `... = Default::default()`

View file

@ -7,9 +7,10 @@ use rustc_hir::{
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::{ use rustc_middle::{
hir::map::Map, hir::map::Map,
lint::in_external_macro,
ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
}; };
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -73,6 +74,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
/// Check whether a passed literal has potential to cause fallback or not. /// Check whether a passed literal has potential to cause fallback or not.
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
if_chain! { if_chain! {
if !in_external_macro(self.cx.sess(), lit.span);
if let Some(ty_bound) = self.ty_bounds.last(); if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node, if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));

View file

@ -149,7 +149,7 @@ declare_deprecated_lint! {
/// enables the `enum_variant_names` lint for public items. /// enables the `enum_variant_names` lint for public items.
/// ``` /// ```
pub PUB_ENUM_VARIANT_NAMES, 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! { declare_deprecated_lint! {
@ -158,5 +158,5 @@ declare_deprecated_lint! {
/// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which
/// enables the `wrong_self_conversion` lint for public items. /// enables the `wrong_self_conversion` lint for public items.
pub WRONG_PUB_SELF_CONVENTION, 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 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
} }
if let ExprKind::Block(block, _) = expr.kind { if let ExprKind::Block(block, _) = expr.kind {
match block.rules { if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => { self.has_unsafe = true;
self.has_unsafe = true;
},
_ => {},
} }
} }

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::fn_def_id; use clippy_utils::fn_def_id;
use rustc_data_structures::fx::FxHashSet; 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_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol; use rustc_span::Symbol;
@ -13,21 +13,14 @@ declare_clippy_lint! {
/// **Why is this bad?** Some methods are undesirable in certain contexts, /// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it's beneficial to lint for them as needed. /// and it's beneficial to lint for them as needed.
/// ///
/// **Known problems:** Currently, you must write each function as a /// **Known problems:** None.
/// 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.
/// ///
/// **Example:** /// **Example:**
/// ///
/// An example clippy.toml configuration: /// An example clippy.toml configuration:
/// ```toml /// ```toml
/// # clippy.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 /// ```rust,ignore
@ -52,6 +45,7 @@ declare_clippy_lint! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisallowedMethod { pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>, disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
} }
impl DisallowedMethod { impl DisallowedMethod {
@ -61,6 +55,7 @@ impl DisallowedMethod {
.iter() .iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>()) .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(), .collect(),
def_ids: FxHashSet::default(),
} }
} }
} }
@ -68,10 +63,20 @@ impl DisallowedMethod {
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(def_id) = fn_def_id(cx, expr) { if let Some(def_id) = fn_def_id(cx, expr) {
let func_path = cx.get_def_path(def_id); if self.def_ids.iter().any(|(id, _)| def_id == *id) {
if self.disallowed.contains(&func_path) { let func_path = cx.get_def_path(def_id);
let func_path_string = func_path let func_path_string = func_path
.into_iter() .into_iter()
.map(Symbol::to_ident_string) .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::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 clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
use if_chain::if_chain; use if_chain::if_chain;
@ -37,7 +38,8 @@ declare_clippy_lint! {
/// consider that. /// consider that.
/// ///
/// **Known problems:** Lots of bad docs wont be fixed, what the lint checks /// **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 /// In addition, when writing documentation comments, including `[]` brackets
/// inside a link text would trip the parser. Therfore, documenting link with /// 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)], spans: &[(usize, Span)],
) -> DocHeaders { ) -> DocHeaders {
// true if a safety header was found // true if a safety header was found
use pulldown_cmark::CodeBlockKind;
use pulldown_cmark::Event::{ use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, 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 { let mut headers = DocHeaders {
safety: false, 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 in_heading = false;
let mut is_rust = false; let mut is_rust = false;
let mut edition = None; 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 { for (event, range) in events {
match event { match event {
Start(CodeBlock(ref kind)) => { 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), Start(Link(_, url, _)) => in_link = Some(url),
End(Link(..)) => in_link = None, End(Link(..)) => in_link = None,
Start(Heading(_)) => in_heading = true, Start(Heading(_) | Paragraph | Item) => {
End(Heading(_)) => in_heading = false, 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 Start(_tag) | End(_tag) => (), // We don't care about other tags
Html(_html) => (), // HTML is weird, just ignore it Html(_html) => (), // HTML is weird, just ignore it
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
FootnoteReference(text) | Text(text) => { 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>` // Probably a link of the form `<http://example.com>`
// Which are represented as a link to "http://example.com" with // Which are represented as a link to "http://example.com" with
// text "http://example.com" by pulldown-cmark // 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.safety |= in_heading && text.trim() == "Safety";
headers.errors |= in_heading && text.trim() == "Errors"; headers.errors |= in_heading && text.trim() == "Errors";
headers.panics |= in_heading && text.trim() == "Panics"; 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 in_code {
if is_rust { if is_rust {
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition()); 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 { } else {
// Adjust for the beginning of the current `Event` // Adjust for the beginning of the current `Event`
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
text_to_check.push((text, span));
check_text(cx, valid_idents, &text, span);
} }
}, },
} }
@ -547,6 +575,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers 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 check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
fn has_needless_main(code: &str, edition: Edition) -> bool { fn has_needless_main(code: &str, edition: Edition) -> bool {
rustc_driver::catch_fatal_errors(|| { rustc_driver::catch_fatal_errors(|| {

View file

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

View file

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

View file

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

View file

@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen {
// LHS of subtraction is "x.len()" // LHS of subtraction is "x.len()"
if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args, _) = &lhs.kind; 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); if let Some(arg_lhs_struct) = lhs_args.get(0);
// The two vectors referenced (x in x.get(...) and in x.len()) // The two vectors referenced (x in x.get(...) and in x.len())

View file

@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
} }
} }
/// 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> { pub struct OppVisitor<'a, 'tcx> {
mutex_lock_called: bool, mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>, found_mutex: Option<&'tcx Expr<'tcx>>,

View file

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

View file

@ -60,9 +60,9 @@ use rustc_session::Session;
/// 4. The `description` that contains a short explanation on what's wrong with code where the /// 4. The `description` that contains a short explanation on what's wrong with code where the
/// lint is triggered. /// lint is triggered.
/// ///
/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. /// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
/// As said in the README.md of this repository, if the lint level mapping changes, please update /// enabled by default. As said in the README.md of this repository, if the lint level mapping
/// README.md. /// changes, please update README.md.
/// ///
/// # Example /// # Example
/// ///
@ -106,6 +106,11 @@ macro_rules! declare_clippy_lint {
$(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true $(#[$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 } => { { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
declare_tool_lint! { declare_tool_lint! {
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
@ -187,6 +192,8 @@ mod default_numeric_fallback;
mod dereference; mod dereference;
mod derive; mod derive;
mod disallowed_method; mod disallowed_method;
mod disallowed_script_idents;
mod disallowed_type;
mod doc; mod doc;
mod double_comparison; mod double_comparison;
mod double_parens; mod double_parens;
@ -254,7 +261,6 @@ mod manual_strip;
mod manual_unwrap_or; mod manual_unwrap_or;
mod map_clone; mod map_clone;
mod map_err_ignore; mod map_err_ignore;
mod map_identity;
mod map_unit_fn; mod map_unit_fn;
mod match_on_vec_items; mod match_on_vec_items;
mod matches; mod matches;
@ -267,6 +273,7 @@ mod misc;
mod misc_early; mod misc_early;
mod missing_const_for_fn; mod missing_const_for_fn;
mod missing_doc; mod missing_doc;
mod missing_enforced_import_rename;
mod missing_inline; mod missing_inline;
mod modulo_arithmetic; mod modulo_arithmetic;
mod multiple_crate_versions; mod multiple_crate_versions;
@ -293,6 +300,7 @@ mod no_effect;
mod non_copy_const; mod non_copy_const;
mod non_expressive_names; mod non_expressive_names;
mod non_octal_unix_permissions; mod non_octal_unix_permissions;
mod nonstandard_macro_braces;
mod open_options; mod open_options;
mod option_env_unwrap; mod option_env_unwrap;
mod option_if_let_else; mod option_if_let_else;
@ -483,11 +491,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
); );
store.register_removed( store.register_removed(
"clippy::pub_enum_variant_names", "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( store.register_removed(
"clippy::wrong_pub_self_convention", "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` // 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::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE, derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_method::DISALLOWED_METHOD, disallowed_method::DISALLOWED_METHOD,
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
disallowed_type::DISALLOWED_TYPE,
doc::DOC_MARKDOWN, doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC, doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_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, manual_unwrap_or::MANUAL_UNWRAP_OR,
map_clone::MAP_CLONE, map_clone::MAP_CLONE,
map_err_ignore::MAP_ERR_IGNORE, map_err_ignore::MAP_ERR_IGNORE,
map_identity::MAP_IDENTITY,
map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS, 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_OPTION_WITH_NONE,
mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT, mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::APPEND_INSTEAD_OF_EXTEND,
methods::BIND_INSTEAD_OF_MAP, methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_NTH, methods::BYTES_NTH,
methods::CHARS_LAST_CMP, 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::MANUAL_STR_REPEAT,
methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_FLATTEN, methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR, methods::MAP_UNWRAP_OR,
methods::NEW_RET_NO_SELF, methods::NEW_RET_NO_SELF,
methods::OK_EXPECT, methods::OK_EXPECT,
@ -810,6 +821,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
misc_early::ZERO_PREFIXED_LITERAL, misc_early::ZERO_PREFIXED_LITERAL,
missing_const_for_fn::MISSING_CONST_FOR_FN, missing_const_for_fn::MISSING_CONST_FOR_FN,
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
modulo_arithmetic::MODULO_ARITHMETIC, modulo_arithmetic::MODULO_ARITHMETIC,
multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, 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::MANY_SINGLE_CHAR_NAMES,
non_expressive_names::SIMILAR_NAMES, non_expressive_names::SIMILAR_NAMES,
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
open_options::NONSENSICAL_OPEN_OPTIONS, open_options::NONSENSICAL_OPEN_OPTIONS,
option_env_unwrap::OPTION_ENV_UNWRAP, option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE, 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(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO), LintId::of(dbg_macro::DBG_MACRO),
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), LintId::of(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(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
@ -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::FLOAT_CMP_CONST),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), 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(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), 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::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION), LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_FLATTEN), 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_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_clone::MAP_CLONE), 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::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), 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_OPTION_WITH_NONE),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), 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::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_NTH), LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP), 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_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT), LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO), 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_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT), LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF), 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::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), 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(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), 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![ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(assign_ops::ASSIGN_OP_PATTERN), LintId::of(assign_ops::ASSIGN_OP_PATTERN),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blacklisted_name::BLACKLISTED_NAME),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), 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(eq_op::OP_REF),
LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(eta_reduction::REDUNDANT_CLOSURE),
LintId::of(float_literal::EXCESSIVE_PRECISION), LintId::of(float_literal::EXCESSIVE_PRECISION),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_over_into::FROM_OVER_INTO),
LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(functions::DOUBLE_MUST_USE), 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(len_zero::LEN_ZERO),
LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(loops::EMPTY_LOOP),
LintId::of(loops::FOR_KV_MAP), LintId::of(loops::FOR_KV_MAP),
LintId::of(loops::NEEDLESS_RANGE_LOOP), LintId::of(loops::NEEDLESS_RANGE_LOOP),
LintId::of(loops::SAME_ITEM_PUSH), 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::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_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::INTO_ITER_ON_REF),
LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE), 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_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), 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::CMP_NULL),
LintId::of(ptr::PTR_ARG), LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ), 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![ 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(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(casts::CHAR_LIT_AS_U8), 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(double_parens::DOUBLE_PARENS),
LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(duration_subsec::DURATION_SUBSEC),
LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(explicit_write::EXPLICIT_WRITE),
LintId::of(format::USELESS_FORMAT), LintId::of(format::USELESS_FORMAT),
LintId::of(functions::TOO_MANY_ARGUMENTS), 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(lifetimes::NEEDLESS_LIFETIMES),
LintId::of(loops::EXPLICIT_COUNTER_LOOP), LintId::of(loops::EXPLICIT_COUNTER_LOOP),
LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::SINGLE_ELEMENT_LOOP),
LintId::of(loops::WHILE_LET_LOOP), LintId::of(loops::WHILE_LET_LOOP),
LintId::of(manual_strip::MANUAL_STRIP), LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_identity::MAP_IDENTITY),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::MATCH_AS_REF), 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::ITER_COUNT),
LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::USELESS_ASREF), LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT), 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(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(eq_op::EQ_OP), LintId::of(eq_op::EQ_OP),
LintId::of(erasing_op::ERASING_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(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(if_let_mutex::IF_LET_MUTEX), 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(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(let_underscore::LET_UNDERSCORE_LOCK), LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::ITER_NEXT_LOOP),
LintId::of(loops::NEVER_LOOP), LintId::of(loops::NEVER_LOOP),
LintId::of(loops::WHILE_IMMUTABLE_CONDITION), 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::CMP_NAN),
LintId::of(misc::FLOAT_CMP), LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE), LintId::of(misc::MODULO_ONE),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), 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(self_assignment::SELF_ASSIGNMENT),
LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), 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(swap::ALMOST_SWAPPED),
LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), 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), 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![ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(entry::MAP_ENTRY), LintId::of(entry::MAP_ENTRY),
LintId::of(escape::BOXED_LOCAL), 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(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::NEEDLESS_COLLECT), LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH),
LintId::of(methods::MANUAL_STR_REPEAT), 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(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(disallowed_method::DISALLOWED_METHOD), LintId::of(disallowed_method::DISALLOWED_METHOD),
LintId::of(disallowed_type::DISALLOWED_TYPE),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_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 { store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
single_char_binding_names_threshold, 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 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 pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
store.register_late_pass(|| box repeat_once::RepeatOnce); 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_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_late_pass(|| box unused_async::UnusedAsync); 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] #[rustfmt::skip]

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,9 @@
use super::WHILE_LET_ON_ITERATOR; use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; 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 if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
} }
} }
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration unless it is declared within the outer // The iterator expression will be used on the next iteration (for loops), or on the next call (for
// loop. // 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 { let local_id = match iter_expr.path {
Res::Local(id) => id, Res::Local(id) => id,
_ => return true, _ => return true,

View file

@ -3,19 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; 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::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use clippy_utils::{ 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_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome}; 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_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{ use rustc_span::{sym, SyntaxContext};
symbol::{sym, Ident},
SyntaxContext,
};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usages of `match` which could be implemented using `map` /// **What it does:** Checks for usages of `match` which could be implemented using `map`
@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap {
scrutinee_str.into() scrutinee_str.into()
}; };
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
match can_pass_as_func(cx, some_binding, some_expr) { match can_pass_as_func(cx, id, some_expr) {
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() 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) && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some() && binding_ref.is_some()
{ {
@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap {
// Checks whether the expression could be passed as a function, or whether a closure is needed. // 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. // 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 { match expr.kind {
ExprKind::Call(func, [arg]) 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) 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 @@ impl CommonPrefixSearcher<'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); 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)] #[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 // Accumulate the variants which should be put in place of the wildcard because they're not
// already covered. // 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; let mut path_prefix = CommonPrefixSearcher::None;
for arm in arms { for arm in arms {
@ -1118,7 +1119,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
match missing_variants.as_slice() { 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, cx,
MATCH_WILDCARD_FOR_SINGLE_VARIANTS, MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
wildcard_span, wildcard_span,
@ -1129,7 +1130,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
), ),
variants => { variants => {
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); 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()); suggestions.push("_".into());
"wildcard matches known variants and will also match future added variants" "wildcard matches known variants and will also match future added variants"
} else { } else {
@ -2266,7 +2267,8 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
), ),
); );
} else { } 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::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; use clippy_utils::{is_expr_identity_function, is_trait_method};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym};
use super::FILTER_MAP_IDENTITY; use super::FILTER_MAP_IDENTITY;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { 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) { if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
let apply_lint = |message: &str| { span_lint_and_sugg(
span_lint_and_sugg( cx,
cx, FILTER_MAP_IDENTITY,
FILTER_MAP_IDENTITY, filter_map_span.with_hi(expr.span.hi()),
filter_map_span.with_hi(expr.span.hi()), "use of `filter_map` with an identity function",
message, "try",
"try", "flatten()".to_string(),
"flatten()".to_string(), Applicability::MachineApplicable,
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`");
}
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; use clippy_utils::{is_expr_identity_function, is_trait_method};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
flat_map_arg: &'tcx hir::Expr<'_>, flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span, flat_map_span: Span,
) { ) {
if is_trait_method(cx, expr, sym::Iterator) { if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
let apply_lint = |message: &str| { span_lint_and_sugg(
span_lint_and_sugg( cx,
cx, FLAT_MAP_IDENTITY,
FLAT_MAP_IDENTITY, flat_map_span.with_hi(expr.span.hi()),
flat_map_span.with_hi(expr.span.hi()), "use of `flat_map` with an identity function",
message, "try",
"try", "flatten()".to_string(),
"flatten()".to_string(), Applicability::MachineApplicable,
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`");
}
} }
} }

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 bind_instead_of_map;
mod bytes_nth; mod bytes_nth;
mod chars_cmp; mod chars_cmp;
@ -35,6 +36,7 @@ mod manual_saturating_arithmetic;
mod manual_str_repeat; mod manual_str_repeat;
mod map_collect_result_unit; mod map_collect_result_unit;
mod map_flatten; mod map_flatten;
mod map_identity;
mod map_unwrap_or; mod map_unwrap_or;
mod ok_expect; mod ok_expect;
mod option_as_ref_deref; mod option_as_ref_deref;
@ -1031,6 +1033,30 @@ declare_clippy_lint! {
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead" "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! { declare_clippy_lint! {
/// **What it does:** Checks for the use of `.extend(s.chars())` where s is a /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
/// `&str` or `String`. /// `&str` or `String`.
@ -1222,7 +1248,7 @@ declare_clippy_lint! {
/// let _ = (0..3).map(|x| x + 2).count(); /// let _ = (0..3).map(|x| x + 2).count();
/// ``` /// ```
pub SUSPICIOUS_MAP, pub SUSPICIOUS_MAP,
complexity, suspicious,
"suspicious usage of map" "suspicious usage of map"
} }
@ -1504,7 +1530,7 @@ declare_clippy_lint! {
/// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ``` /// ```
pub FROM_ITER_INSTEAD_OF_COLLECT, pub FROM_ITER_INSTEAD_OF_COLLECT,
style, pedantic,
"use `.collect()` instead of `::from_iter()`" "use `.collect()` instead of `::from_iter()`"
} }
@ -1561,6 +1587,29 @@ declare_clippy_lint! {
"call to `filter_map` where `flatten` is sufficient" "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! { declare_clippy_lint! {
/// **What it does:** Checks for the use of `.bytes().nth()`. /// **What it does:** Checks for the use of `.bytes().nth()`.
/// ///
@ -1728,6 +1777,7 @@ impl_lint_pass!(Methods => [
FILTER_NEXT, FILTER_NEXT,
SKIP_WHILE_NEXT, SKIP_WHILE_NEXT,
FILTER_MAP_IDENTITY, FILTER_MAP_IDENTITY,
MAP_IDENTITY,
MANUAL_FILTER_MAP, MANUAL_FILTER_MAP,
MANUAL_FIND_MAP, MANUAL_FIND_MAP,
OPTION_FILTER_MAP, OPTION_FILTER_MAP,
@ -1760,7 +1810,8 @@ impl_lint_pass!(Methods => [
INSPECT_FOR_EACH, INSPECT_FOR_EACH,
IMPLICIT_CLONE, IMPLICIT_CLONE,
SUSPICIOUS_SPLITN, SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT MANUAL_STR_REPEAT,
APPEND_INSTEAD_OF_EXTEND
]); ]);
/// Extracts a method call name, args, and `Span` of the method name. /// Extracts a method call name, args, and `Span` of the method name.
@ -1985,7 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
match (name, args) { 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); zst_offset::check(cx, expr, recv);
}, },
("and_then", [arg]) => { ("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), Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::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]) => { ("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg); unnecessary_filter_map::check(cx, expr, arg);
filter_map_identity::check(cx, expr, arg, span); 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), ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
("next", []) => { ("next", []) => {

View file

@ -87,7 +87,7 @@ pub(super) fn check<'tcx>(
]; ];
if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { 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(); let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
match ty.kind() { 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 @@ declare_clippy_lint! {
/// } /// }
/// ``` /// ```
pub MUTABLE_KEY_TYPE, pub MUTABLE_KEY_TYPE,
correctness, suspicious,
"Check for mutable `Map`/`Set` key type" "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::UnsafeBlock(UnsafeSource::UserProvided) => None,
BlockCheckMode::DefaultBlock => Some(vec![&**e]), BlockCheckMode::DefaultBlock => Some(vec![&**e]),
// in case of compiler-inserted signaling blocks // in case of compiler-inserted signaling blocks
_ => reduce_expression(cx, e), BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
} }
}) })
} else { } 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); if is_integer_const(cx, start, 0);
// `.len()` call // `.len()` call
if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind; 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` // `.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(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;

View file

@ -132,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
} }
} }
// `{ 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 (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
let loc = mir::Location { 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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{in_macro, sugg}; use clippy_utils::{in_macro, sugg};
@ -45,6 +46,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned {
if t_expr.is_unit(); if t_expr.is_unit();
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
if !snippet.ends_with('}'); if !snippet.ends_with('}');
if cx.sess().source_map().is_multiline(block.span);
then { then {
// filter out the desugared `for` loop // filter out the desugared `for` loop
if let ExprKind::DropTemps(..) = &expr.kind { if let ExprKind::DropTemps(..) = &expr.kind {

View file

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

View file

@ -1,6 +1,6 @@
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] #![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::diagnostics::span_lint_and_then;
use clippy_utils::{meets_msrv, msrvs, over}; use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*; 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, ps1, start, alternatives,
|k, ps1, idx| matches!( |k, ps1, idx| matches!(
k, 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), |k| always_pat!(k, TupleStruct(_, _, ps) => ps),
), ),

View file

@ -1,23 +1,22 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::ty::same_type_and_consts;
use clippy_utils::{in_macro, meets_msrv, msrvs}; use clippy_utils::{in_macro, meets_msrv, msrvs};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{ use rustc_hir::{
self as hir, self as hir,
def::{self, DefKind}, def::{CtorOf, DefKind, Res},
def_id::LocalDefId, def_id::LocalDefId,
intravisit::{walk_ty, NestedVisitorMap, Visitor}, intravisit::{walk_ty, NestedVisitorMap, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
QPath, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::ty::{AssocKind, Ty}; use rustc_middle::ty::AssocKind;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Span}; use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;
declare_clippy_lint! { declare_clippy_lint! {
@ -75,10 +74,9 @@ impl UseSelf {
#[derive(Debug)] #[derive(Debug)]
enum StackItem { enum StackItem {
Check { Check {
hir_id: HirId, impl_id: LocalDefId,
impl_trait_ref_def_id: Option<LocalDefId>, in_body: u32,
types_to_skip: Vec<HirId>, types_to_skip: FxHashSet<HirId>,
types_to_lint: Vec<HirId>,
}, },
NoCheck, NoCheck,
} }
@ -88,60 +86,41 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
impl<'tcx> LateLintPass<'tcx> for UseSelf { 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 // 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 // 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 // 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 // we're in an `impl` or nested item, that we don't want to lint
// let stack_item = if_chain! {
// NB: If you push something on the stack in this method, remember to also pop it in the if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
// `check_item_post` method. if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
match &item.kind { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
ItemKind::Impl(Impl { if parameters.as_ref().map_or(true, |params| {
self_ty: hir_self_ty, !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
of_trait, });
.. then {
}) => { StackItem::Check {
let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind { impl_id: item.def_id,
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; in_body: 0,
parameters.as_ref().map_or(true, |params| { types_to_skip: std::iter::once(self_ty.hir_id).collect(),
!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);
} }
}, } else {
ItemKind::Static(..) StackItem::NoCheck
| ItemKind::Const(..) }
| ItemKind::Fn(..) };
| ItemKind::Enum(..) self.stack.push(stack_item);
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..) => {
self.stack.push(StackItem::NoCheck);
},
_ => (),
}
} }
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union}; if is_item_interesting(item) {
match item.kind { self.stack.pop();
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
self.stack.pop();
},
_ => (),
} }
} }
@ -151,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if_chain! { if_chain! {
if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
if let Some(&mut StackItem::Check { if let Some(&mut StackItem::Check {
impl_trait_ref_def_id: Some(def_id), impl_id,
ref mut types_to_skip, ref mut types_to_skip,
.. ..
}) = self.stack.last_mut(); }) = 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 { then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`. // `Self`.
@ -203,142 +182,76 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
} }
} }
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 // `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`. // 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. // However the `node_type()` method can *only* be called in bodies.
// if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
// This method implementation determines which types should get linted in a `Body` and *in_body = in_body.saturating_add(1);
// which shouldn't, with a visitor. We could directly lint in the visitor, but then we }
// could only allow this lint on item scope. And we would have to check if those types are }
// already dealt with in `check_ty` anyway.
if let Some(StackItem::Check {
hir_id,
types_to_lint,
types_to_skip,
..
}) = self.stack.last_mut()
{
let self_ty = ty_from_hir_id(cx, *hir_id);
let mut visitor = LintTyCollector { fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
cx, if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
self_ty, *in_body = in_body.saturating_sub(1);
types_to_lint: vec![],
types_to_skip: vec![],
};
visitor.visit_expr(&body.value);
types_to_lint.extend(visitor.types_to_lint);
types_to_skip.extend(visitor.types_to_skip);
} }
} }
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
if in_macro(hir_ty.span) if_chain! {
|| in_impl(cx, hir_ty) if !in_macro(hir_ty.span);
|| !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
{ if let Some(&StackItem::Check {
return; impl_id,
} in_body,
ref types_to_skip,
let lint_dependend_on_expr_kind = if let Some(StackItem::Check { }) = self.stack.last();
hir_id, if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
types_to_lint, if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
types_to_skip, if !types_to_skip.contains(&hir_ty.hir_id);
.. let ty = if in_body > 0 {
}) = self.stack.last() cx.typeck_results().node_type(hir_ty.hir_id)
{
if types_to_skip.contains(&hir_ty.hir_id) {
false
} else if types_to_lint.contains(&hir_ty.hir_id) {
true
} else { } else {
let self_ty = ty_from_hir_id(cx, *hir_id); hir_ty_to_ty(cx.tcx, hir_ty)
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty) };
} if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
} 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
let hir = cx.tcx.hir(); let hir = cx.tcx.hir();
let id = hir.get_parent_node(hir_ty.hir_id); let id = hir.get_parent_node(hir_ty.hir_id);
if !hir.opt_span(id).map_or(false, in_macro);
if !hir.opt_span(id).map_or(false, in_macro) { then {
match hir.find(id) { span_lint(cx, hir_ty.span);
Some(Node::Expr(Expr {
kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
..
})) => span_lint_until_last_segment(cx, hir_ty.span, segment),
_ => span_lint(cx, hir_ty.span),
}
} }
} }
} }
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool { if_chain! {
let def_id = expr.hir_id.owner; if !in_macro(expr.span);
if cx.tcx.has_typeck_results(def_id) { if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty) if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
} else { if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
false then {} else { return; }
}
} }
match expr.kind {
if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) { ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
return; Res::SelfTy(..) => (),
} Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
_ => span_lint(cx, path.span),
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() { },
let self_ty = ty_from_hir_id(cx, *hir_id); // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
ExprKind::Call(fun, _) => {
match &expr.kind { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
if expr_ty_matches(cx, expr, self_ty) { match ctor_of {
match path.res { CtorOf::Variant => lint_path_to_variant(cx, path),
def::Res::SelfTy(..) => (), CtorOf::Struct => span_lint(cx, path.span),
def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
_ => {
span_lint(cx, path.span);
},
} }
} }
}, }
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) },
ExprKind::Call(fun, _) => { // unit enum variants (`Enum::A`)
if let Expr { ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
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);
}
},
_ => (),
}
} }
} }
@ -364,35 +277,6 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector {
} }
} }
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) { fn span_lint(cx: &LateContext<'_>, span: Span) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -405,66 +289,19 @@ fn span_lint(cx: &LateContext<'_>, span: Span) {
); );
} }
#[allow(clippy::cast_possible_truncation)] fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) { if let [.., self_seg, _variant] = path.segments {
let sp = span.with_hi(segment.ident.span.lo()); let span = path
// remove the trailing :: .span
let span_without_last_segment = match snippet_opt(cx, sp) { .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
Some(snippet) => match snippet.rfind("::") { span_lint(cx, span);
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 span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) { fn is_item_interesting(item: &Item<'_>) -> bool {
if let QPath::Resolved(_, path) = qpath { use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
if until_last_segment { matches!(
span_lint_on_path_until_last_segment(cx, path); item.kind,
} else { Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
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
}
}
} }

View file

@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
} }
} }
if_chain! { 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 a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(&args[0]); let b = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, a, sym::result_type); if is_type_diagnostic_item(cx, a, sym::result_type);

View file

@ -8,6 +8,13 @@ use std::error::Error;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{env, fmt, fs, io}; 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 /// Conf with parse errors
#[derive(Default)] #[derive(Default)]
pub struct TryConf { pub struct TryConf {
@ -24,6 +31,9 @@ impl TryConf {
} }
} }
/// 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 { macro_rules! define_Conf {
($( ($(
#[doc = $doc:literal] #[doc = $doc:literal]
@ -149,7 +159,7 @@ define_Conf! {
"WebGL", "WebGL",
"TensorFlow", "TensorFlow",
"TrueType", "TrueType",
"iOS", "macOS", "iOS", "macOS", "FreeBSD",
"TeX", "LaTeX", "BibTeX", "BibLaTeX", "TeX", "LaTeX", "BibTeX", "BibLaTeX",
"MinGW", "MinGW",
"CamelCase", "CamelCase",
@ -182,20 +192,28 @@ define_Conf! {
(vec_box_size_threshold: u64 = 4096), (vec_box_size_threshold: u64 = 4096),
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
(max_trait_bounds: u64 = 3), (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), (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), (max_fn_params_bools: u64 = 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports: bool = false), (warn_on_all_wildcard_imports: bool = false),
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<String> = Vec::new()), (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. /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
(unreadable_literal_lint_fractions: bool = true), (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 /// 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), (upper_case_acronyms_aggressive: bool = false),
/// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
(cargo_ignore_publish: bool = false), (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. /// Search for the configuration file.

View file

@ -9,6 +9,7 @@
//! a simple mistake) //! a simple mistake)
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{ use rustc_hir::{
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
@ -46,8 +47,9 @@ const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
const DEPRECATED_LINT_LEVEL: &str = "none"; const DEPRECATED_LINT_LEVEL: &str = "none";
/// This array holds Clippy's lint groups with their corresponding default lint level. The /// 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`. /// 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"), ("correctness", "deny"),
("suspicious", "warn"),
("restriction", "allow"), ("restriction", "allow"),
("style", "warn"), ("style", "warn"),
("pedantic", "allow"), ("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` /// Would result in `Hello world!\n=^.^=\n`
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> { fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
cx.tcx let attrs = cx.tcx.hir().attrs(item.hir_id());
.hir() let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
.attrs(item.hir_id()) let mut docs = String::from(&*lines.next()?.as_str());
.iter() let mut in_code_block = false;
.filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string())) for line in lines {
.reduce(|mut acc, sym| { docs.push('\n');
acc.push_str(&sym); let line = line.as_str();
acc.push('\n'); let line = &*line;
acc 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( 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::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{in_macro, is_test_module_or_function};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{ use rustc_hir::{
@ -106,7 +106,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
impl LateLintPass<'_> for WildcardImports { impl LateLintPass<'_> for WildcardImports {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { 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); self.test_modules_deep = self.test_modules_deep.saturating_add(1);
} }
if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
@ -183,8 +183,8 @@ impl LateLintPass<'_> for WildcardImports {
} }
} }
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { fn check_item_post(&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_sub(1); 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 { fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
segments.len() == 1 && segments[0].ident.name == kw::Super 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] [package]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.54" version = "0.1.55"
authors = ["The Rust Clippy Developers"] authors = ["The Rust Clippy Developers"]
edition = "2018" edition = "2018"
publish = false 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), | (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)), (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), (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)) => { (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)), (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
(MacCall(l), MacCall(r)) => eq_mac_call(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) { match (l, r) {
(Some(l), Some(r)) => eq_qself(l, r), (Some(l), Some(r)) => eq_qself(l, r),
(None, None) => true, (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) .filter_map(ast::Attribute::meta_item_list)
.any(|l| attr::list_contains_name(&l, sym::hidden)) .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)] #![allow(clippy::float_cmp)]
use crate::{clip, sext, unsext}; use crate::{clip, is_direct_expn_of, sext, unsext};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
@ -230,7 +230,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
match e.kind { match e.kind {
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(block, _) => self.block(block), 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::Array(vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(value, _) => { ExprKind::Repeat(value, _) => {

View file

@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, 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, 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_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export; 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)) .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> { pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
match *path { match *path {
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), 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 struct ContainsName {
pub name: Symbol, pub name: Symbol,
pub result: bool, 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. /// Gets the loop or closure enclosing the given expression, if any.
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
let map = tcx.hir(); let map = tcx.hir();
for (_, node) in map.parent_iter(expr.hir_id) { for (_, node) in map.parent_iter(expr.hir_id) {
match node { match node {
Node::Expr( Node::Expr(
e @ Expr { e
kind: ExprKind::Loop(..), @
Expr {
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
.. ..
}, },
) => return Some(e), ) => 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()) 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. /// 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>> { pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let map = tcx.hir(); let map = tcx.hir();
@ -1654,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
(e, count) (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_export]
macro_rules! unwrap_cargo_metadata { macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{ ($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::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::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_lint::LateContext;
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_span::{Span, Symbol}; use rustc_span::Span;
use std::borrow::Cow; use std::borrow::Cow;
pub fn get_spans( pub fn get_spans(
@ -14,10 +14,11 @@ pub fn get_spans(
replacements: &[(&'static str, &'static str)], replacements: &[(&'static str, &'static str)],
) -> Option<Vec<(Span, Cow<'static, str>)>> { ) -> Option<Vec<(Span, Cow<'static, str>)>> {
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) { if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
get_binding_name(&body.params[idx]).map_or_else( if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
|| Some(vec![]), extract_clone_suggestions(cx, binding_id, replacements, body)
|name| extract_clone_suggestions(cx, name, replacements, body), } else {
) Some(vec![])
}
} else { } else {
Some(vec![]) Some(vec![])
} }
@ -25,13 +26,13 @@ pub fn get_spans(
fn extract_clone_suggestions<'tcx>( fn extract_clone_suggestions<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
name: Symbol, id: HirId,
replace: &[(&'static str, &'static str)], replace: &[(&'static str, &'static str)],
body: &'tcx Body<'_>, body: &'tcx Body<'_>,
) -> Option<Vec<(Span, Cow<'static, str>)>> { ) -> Option<Vec<(Span, Cow<'static, str>)>> {
let mut visitor = PtrCloneVisitor { let mut visitor = PtrCloneVisitor {
cx, cx,
name, id,
replace, replace,
spans: vec![], spans: vec![],
abort: false, abort: false,
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
struct PtrCloneVisitor<'a, 'tcx> { struct PtrCloneVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
name: Symbol, id: HirId,
replace: &'a [(&'static str, &'static str)], replace: &'a [(&'static str, &'static str)],
spans: Vec<(Span, Cow<'static, str>)>, spans: Vec<(Span, Cow<'static, str>)>,
abort: bool, abort: bool,
@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
if self.abort { if self.abort {
return; return;
} }
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind { if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
if args.len() == 1 && match_var(&args[0], self.name) { if path_to_local_id(recv, self.id) {
if seg.ident.name.as_str() == "capacity" { if seg.ident.name.as_str() == "capacity" {
self.abort = true; self.abort = true;
return; return;
} }
for &(fn_name, suffix) in self.replace { for &(fn_name, suffix) in self.replace {
if seg.ident.name.as_str() == fn_name { if seg.ident.name.as_str() == fn_name {
self.spans self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
return; return;
} }
} }
@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
NestedVisitorMap::None 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 cargo dev update_lints
# create a new lint and register it # create a new lint and register it
cargo dev new_lint cargo dev new_lint
# automatically formatting all code before each commit
cargo dev setup git-hook
# (experimental) Setup Clippy to work with IntelliJ-Rust # (experimental) Setup Clippy to work with IntelliJ-Rust
cargo dev ide_setup cargo dev setup intellij
``` ```
## lintcheck ## 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) - [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 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 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) - [Dealing with macros](#dealing-with-macros)
Useful Rustc dev guide links: 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 [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` ## Update `CHANGELOG.md`

View file

@ -69,7 +69,7 @@ is checked.
is explicitly specified in the options. is explicitly specified in the options.
### Fix mode ### 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). 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. This lets us spot bad suggestions or false positives automatically in some cases.

View file

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

View file

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

View file

@ -70,7 +70,6 @@ impl ClippyCmd {
I: Iterator<Item = String>, I: Iterator<Item = String>,
{ {
let mut cargo_subcommand = "check"; let mut cargo_subcommand = "check";
let mut unstable_options = false;
let mut args = vec![]; let mut args = vec![];
for arg in old_args.by_ref() { for arg in old_args.by_ref() {
@ -80,18 +79,12 @@ impl ClippyCmd {
continue; continue;
}, },
"--" => break, "--" => break,
// Cover -Zunstable-options and -Z unstable-options
s if s.ends_with("unstable-options") => unstable_options = true,
_ => {}, _ => {},
} }
args.push(arg); 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(); let mut clippy_args: Vec<String> = old_args.collect();
if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
clippy_args.push("--no-deps".into()); clippy_args.push("--no-deps".into());
@ -176,34 +169,23 @@ mod tests {
use super::ClippyCmd; use super::ClippyCmd;
#[test] #[test]
#[should_panic] fn fix() {
fn fix_without_unstable() {
let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string); 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); let cmd = ClippyCmd::new(args);
assert_eq!("fix", cmd.cargo_subcommand); 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] #[test]
fn fix_implies_no_deps() { fn fix_implies_no_deps() {
let args = "cargo clippy --fix -Zunstable-options" let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
.split_whitespace()
.map(ToString::to_string);
let cmd = ClippyCmd::new(args); let cmd = ClippyCmd::new(args);
assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
} }
#[test] #[test]
fn no_deps_not_duplicated_with_fix() { 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() .split_whitespace()
.map(ToString::to_string); .map(ToString::to_string);
let cmd = ClippyCmd::new(args); 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) && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
{ {
if let Some(old) = crates.insert(dep, path.clone()) { 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; 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 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 debug_assert!(false); // #3948
assert_const!(3); assert_const!(3);
assert_const!(-1); assert_const!(-1);
// Don't lint on this:
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
} }

View file

@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default {
} }
}; };
} }
#[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; let ref mut baz = 0;
if let Some(ref mut quux) = Some(42) {} 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)] #![warn(clippy::default_numeric_fallback)]
#![allow(unused)] #![allow(unused)]
#![allow(clippy::never_loop)] #![allow(clippy::never_loop)]
@ -5,6 +7,9 @@
#![allow(clippy::unnecessary_operation)] #![allow(clippy::unnecessary_operation)]
#![allow(clippy::branches_sharing_code)] #![allow(clippy::branches_sharing_code)]
#[macro_use]
extern crate macro_rules;
mod basic_expr { mod basic_expr {
fn test() { fn test() {
// Should lint unsuffixed literals typed `i32`. // Should lint unsuffixed literals typed `i32`.
@ -133,4 +138,22 @@ mod method_calls {
} }
} }
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() {} fn main() {}

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