mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 07:00:55 +00:00
Auto merge of #86782 - flip1995:clippyup, r=Manishearth
Update Clippy Biweekly Clippy Update r? `@Manishearth`
This commit is contained in:
commit
7cd0643eb2
142 changed files with 3185 additions and 1086 deletions
2
.github/workflows/clippy.yml
vendored
2
.github/workflows/clippy.yml
vendored
|
@ -71,7 +71,7 @@ jobs:
|
|||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test cargo-clippy --fix
|
||||
run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
|
||||
run: ../target/debug/cargo-clippy clippy --fix
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test clippy-driver
|
||||
|
|
7
.github/workflows/clippy_bors.yml
vendored
7
.github/workflows/clippy_bors.yml
vendored
|
@ -90,11 +90,6 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
# FIXME: should not be necessary once 1.24.2 is the default version on the windows runner
|
||||
- name: Update rustup
|
||||
run: rustup self update
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
|
@ -139,7 +134,7 @@ jobs:
|
|||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test cargo-clippy --fix
|
||||
run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options
|
||||
run: ../target/debug/cargo-clippy clippy --fix
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test clippy-driver
|
||||
|
|
2
.github/workflows/remark.yml
vendored
2
.github/workflows/remark.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
uses: actions/setup-node@v1.4.4
|
||||
|
||||
- name: Install remark
|
||||
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended
|
||||
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
|
||||
|
||||
# Run
|
||||
- name: Check *.md files
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"plugins": [
|
||||
"remark-preset-lint-recommended",
|
||||
"remark-gfm",
|
||||
["remark-lint-list-item-indent", false],
|
||||
["remark-lint-no-literal-urls", false],
|
||||
["remark-lint-no-shortcut-reference-link", false],
|
||||
|
|
139
CHANGELOG.md
139
CHANGELOG.md
|
@ -6,11 +6,139 @@ document.
|
|||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[7c7683c...master](https://github.com/rust-lang/rust-clippy/compare/7c7683c...master)
|
||||
[3ae8faf...master](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...master)
|
||||
|
||||
## Rust 1.54
|
||||
|
||||
Current beta, release 2021-07-29
|
||||
|
||||
[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
|
||||
|
||||
### New Lints
|
||||
|
||||
- [`ref_binding_to_reference`]
|
||||
[#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
|
||||
- [`needless_bitwise_bool`]
|
||||
[#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
|
||||
- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
|
||||
- [`manual_str_repeat`]
|
||||
[#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
|
||||
- [`suspicious_splitn`]
|
||||
[#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
|
||||
the new `avoid_breaking_exported_api` config option (see
|
||||
[Enhancements](#1-54-enhancements))
|
||||
[#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
|
||||
- Move [`inconsistent_struct_constructor`] to `pedantic`
|
||||
[#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
|
||||
- Move [`needless_borrow`] to `style` (now warn-by-default)
|
||||
[#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
|
||||
- Move [`suspicious_operation_groupings`] to `nursery`
|
||||
[#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
|
||||
- Move [`semicolon_if_nothing_returned`] to `pedantic`
|
||||
[#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
|
||||
|
||||
### Enhancements <a name="1-54-enhancements"></a>
|
||||
|
||||
- [`while_let_on_iterator`]: Now also lints in nested loops
|
||||
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
|
||||
- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
|
||||
[#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
|
||||
- [`needless_collect`]: Now also lints on assignments with type annotations
|
||||
[#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
|
||||
- [`if_then_some_else_none`]: Now works with the MSRV config
|
||||
[#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
|
||||
- Add `avoid_breaking_exported_api` config option for the lints
|
||||
[`enum_variant_names`], [`large_types_passed_by_value`],
|
||||
[`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
|
||||
[`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
|
||||
this configuration option to `false` before a major release (1.0/2.0/...) to
|
||||
clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
|
||||
- [`needless_collect`]: Now lints on even more data structures
|
||||
[#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
|
||||
- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
|
||||
attributes as sufficient documentation
|
||||
[#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
|
||||
- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
|
||||
Now work as expected when used with `allow`
|
||||
[#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
- [`implicit_return`]: Now takes all diverging functions in account to avoid
|
||||
false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
|
||||
- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
|
||||
and the struct is used in the loop
|
||||
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
|
||||
- [`multiple_inherent_impl`]: No longer lints with generic arguments
|
||||
[#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
|
||||
- [`comparison_chain`]: No longer lints in a `const` context
|
||||
[#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
|
||||
- [`while_immutable_condition`]: Fix false positive where mutation in the loop
|
||||
variable wasn't picked up
|
||||
[#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
|
||||
- [`default_trait_access`]: No longer lints in macros
|
||||
[#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
|
||||
- [`needless_question_mark`]: No longer lints when the inner value is implicitly
|
||||
dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
|
||||
- [`unused_unit`]: No longer lints when multiple macro contexts are involved
|
||||
[#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
|
||||
- [`eval_order_dependence`]: Fix false positive in async context
|
||||
[#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
|
||||
- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
|
||||
type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
|
||||
- [`wrong_self_convention`]: No longer lints in trait implementations of
|
||||
non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
|
||||
- [`suboptimal_flops`]: No longer lints on `powi(2)`
|
||||
[#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
|
||||
- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
|
||||
[#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
|
||||
- [`option_if_let_else`]: No longer lints on `else if let` pattern
|
||||
[#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
|
||||
- [`use_self`], [`useless_conversion`]: Fix false positives when generic
|
||||
arguments are involved
|
||||
[#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
|
||||
- [`manual_unwrap_or`]: Fix false positive with deref coercion
|
||||
[#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
|
||||
- [`similar_names`]: No longer lints on `wparam`/`lparam`
|
||||
[#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
|
||||
- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
|
||||
closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
- [`implicit_return`]
|
||||
[#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
|
||||
- Fix suggestion for async functions
|
||||
- Improve suggestion with macros
|
||||
- Suggest to change `break` to `return` when appropriate
|
||||
- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
|
||||
[#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
|
||||
- [`match_single_binding`]: Improve suggestion when match scrutinee has side
|
||||
effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
|
||||
- [`needless_borrow`]: Now suggests to also change usage sites as needed
|
||||
[#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
|
||||
- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
|
||||
buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
|
||||
- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
|
||||
when a `<_ as Trait>::_` is involved
|
||||
[#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
|
||||
- [`not_unsafe_ptr_arg_deref`]: Improved error message
|
||||
[#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
- Fix ICE when running Clippy on `libstd`
|
||||
[#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
|
||||
- [`implicit_return`]
|
||||
[#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
|
||||
|
||||
## Rust 1.53
|
||||
|
||||
Current beta, release 2021-06-17
|
||||
Current stable, released 2021-06-17
|
||||
|
||||
[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
|
||||
|
||||
|
@ -194,7 +322,7 @@ Current beta, release 2021-06-17
|
|||
|
||||
## Rust 1.52
|
||||
|
||||
Current stable, released 2021-05-06
|
||||
Released 2021-05-06
|
||||
|
||||
[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
|
||||
|
||||
|
@ -2295,6 +2423,7 @@ Released 2018-09-13
|
|||
<!-- begin autogenerated links to lint list -->
|
||||
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
|
||||
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
|
||||
[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend
|
||||
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
|
||||
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
|
||||
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
|
||||
|
@ -2358,6 +2487,8 @@ Released 2018-09-13
|
|||
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
|
||||
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
|
||||
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
|
||||
[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
|
||||
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
|
||||
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
|
||||
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||
|
@ -2527,6 +2658,7 @@ Released 2018-09-13
|
|||
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
|
||||
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
|
||||
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
|
||||
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
|
||||
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
|
||||
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
|
||||
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
|
||||
|
@ -2574,6 +2706,7 @@ Released 2018-09-13
|
|||
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
|
||||
[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
|
||||
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
|
||||
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
|
||||
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
|
||||
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
|
||||
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
|
||||
|
|
|
@ -115,7 +115,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava
|
|||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which `IntelliJ Rust` will be able to understand.
|
||||
Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
|
||||
Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.54"
|
||||
version = "0.1.55"
|
||||
authors = ["The Rust Clippy Developers"]
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
26
README.md
26
README.md
|
@ -10,16 +10,17 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
|
|||
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
||||
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
||||
|
||||
| Category | Description | Default level |
|
||||
| --------------------- | ----------------------------------------------------------------------- | ------------- |
|
||||
| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
|
||||
| `clippy::correctness` | code that is outright wrong or very useless | **deny** |
|
||||
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
|
||||
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
|
||||
| `clippy::perf` | code that can be written to run faster | **warn** |
|
||||
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
|
||||
| `clippy::nursery` | new lints that are still under development | allow |
|
||||
| `clippy::cargo` | lints for the cargo manifest | allow |
|
||||
| Category | Description | Default level |
|
||||
| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
|
||||
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
|
||||
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
|
||||
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
|
||||
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
|
||||
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
|
||||
| `clippy::perf` | code that can be written to run faster | **warn** |
|
||||
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
|
||||
| `clippy::nursery` | new lints that are still under development | allow |
|
||||
| `clippy::cargo` | lints for the cargo manifest | allow |
|
||||
|
||||
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
|
||||
|
||||
|
@ -75,11 +76,10 @@ cargo clippy
|
|||
|
||||
#### Automatically applying Clippy suggestions
|
||||
|
||||
Clippy can automatically apply some lint suggestions.
|
||||
Note that this is still experimental and only supported on the nightly channel:
|
||||
Clippy can automatically apply some lint suggestions, just like the compiler.
|
||||
|
||||
```terminal
|
||||
cargo clippy --fix -Z unstable-options
|
||||
cargo clippy --fix
|
||||
```
|
||||
|
||||
#### Workspaces
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2018"
|
|||
bytecount = "0.6"
|
||||
clap = "2.33"
|
||||
itertools = "0.9"
|
||||
opener = "0.4"
|
||||
opener = "0.5"
|
||||
regex = "1"
|
||||
shell-escape = "0.1"
|
||||
walkdir = "2"
|
||||
|
|
|
@ -60,11 +60,7 @@ pub fn run(check: bool, verbose: bool) {
|
|||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.extension() != Some("rs".as_ref())
|
||||
|| entry.file_name() == "ice-3891.rs"
|
||||
// Avoid rustfmt bug rust-lang/rustfmt#1873
|
||||
|| cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
|
||||
{
|
||||
if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -90,7 +86,7 @@ pub fn run(check: bool, verbose: bool) {
|
|||
},
|
||||
CliError::RaSetupActive => {
|
||||
eprintln!(
|
||||
"error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
|
||||
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
|
||||
Not formatting because that would format the local repo as well!
|
||||
Please revert the changes to Cargo.tomls first."
|
||||
);
|
||||
|
|
|
@ -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(())
|
||||
}
|
|
@ -14,9 +14,9 @@ use walkdir::WalkDir;
|
|||
|
||||
pub mod bless;
|
||||
pub mod fmt;
|
||||
pub mod ide_setup;
|
||||
pub mod new_lint;
|
||||
pub mod serve;
|
||||
pub mod setup;
|
||||
pub mod stderr_length_check;
|
||||
pub mod update_lints;
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
|
||||
fn main() {
|
||||
let matches = get_clap_config();
|
||||
|
||||
|
@ -36,7 +36,22 @@ fn main() {
|
|||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")),
|
||||
("setup", Some(sub_command)) => match sub_command.subcommand() {
|
||||
("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
|
||||
matches
|
||||
.value_of("rustc-repo-path")
|
||||
.expect("this field is mandatory and therefore always valid"),
|
||||
),
|
||||
("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
|
||||
("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
|
||||
_ => {},
|
||||
},
|
||||
("remove", Some(sub_command)) => match sub_command.subcommand() {
|
||||
("git-hook", Some(_)) => setup::git_hook::remove_hook(),
|
||||
("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
|
||||
("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
|
||||
_ => {},
|
||||
},
|
||||
("serve", Some(matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
|
@ -48,6 +63,7 @@ fn main() {
|
|||
|
||||
fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
App::new("Clippy developer tooling")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("bless")
|
||||
.about("bless the test output changes")
|
||||
|
@ -123,6 +139,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
|||
.possible_values(&[
|
||||
"style",
|
||||
"correctness",
|
||||
"suspicious",
|
||||
"complexity",
|
||||
"perf",
|
||||
"pedantic",
|
||||
|
@ -140,16 +157,54 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
|||
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("ide_setup")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short("r")
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.takes_value(true)
|
||||
.value_name("path")
|
||||
.required(true),
|
||||
SubCommand::with_name("setup")
|
||||
.about("Support for setting up your personal development environment")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("intellij")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short("r")
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.takes_value(true)
|
||||
.value_name("path")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("git-hook")
|
||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
.long("force-override")
|
||||
.short("f")
|
||||
.help("Forces the override of an existing git pre-commit hook")
|
||||
.required(false),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vscode-tasks")
|
||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
.long("force-override")
|
||||
.short("f")
|
||||
.help("Forces the override of existing vscode tasks")
|
||||
.required(false),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("remove")
|
||||
.about("Support for undoing changes done by the setup command")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
|
||||
.subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("intellij")
|
||||
.about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
|
|
85
clippy_dev/src/setup/git_hook.rs
Normal file
85
clippy_dev/src/setup/git_hook.rs
Normal 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
|
||||
}
|
||||
}
|
223
clippy_dev/src/setup/intellij.rs
Normal file
223
clippy_dev/src/setup/intellij.rs
Normal 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
|
||||
},
|
||||
}
|
||||
}
|
23
clippy_dev/src/setup/mod.rs
Normal file
23
clippy_dev/src/setup/mod.rs
Normal 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
|
||||
}
|
||||
}
|
104
clippy_dev/src/setup/vscode.rs
Normal file
104
clippy_dev/src/setup/vscode.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -92,7 +92,10 @@ pub fn run(update_mode: UpdateMode) {
|
|||
|| {
|
||||
// clippy::all should only include the following lint groups:
|
||||
let all_group_lints = usable_lints.iter().filter(|l| {
|
||||
l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
|
||||
matches!(
|
||||
&*l.group,
|
||||
"correctness" | "suspicious" | "style" | "complexity" | "perf"
|
||||
)
|
||||
});
|
||||
|
||||
gen_lint_group_list(all_group_lints)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin automatic update
|
||||
version = "0.1.54"
|
||||
version = "0.1.55"
|
||||
# end automatic update
|
||||
authors = ["The Rust Clippy Developers"]
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
|
@ -23,6 +23,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = { version = "1.0", optional = true }
|
||||
toml = "0.5.3"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5.3", default-features = false }
|
||||
semver = "0.11"
|
||||
rustc-semver = "1.1.0"
|
||||
# NOTE: cargo requires serde feat in its url dep
|
||||
|
|
|
@ -55,7 +55,7 @@ declare_clippy_lint! {
|
|||
/// a += a + b;
|
||||
/// ```
|
||||
pub MISREFACTORED_ASSIGN_OP,
|
||||
complexity,
|
||||
suspicious,
|
||||
"having a variable on both sides of an assign op"
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ declare_clippy_lint! {
|
|||
/// #![deny(clippy::as_conversions)]
|
||||
/// ```
|
||||
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
style,
|
||||
suspicious,
|
||||
"enabling the complete restriction group"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{diagnostics::span_lint, is_test_module_or_function};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{Pat, PatKind};
|
||||
use rustc_hir::{Item, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
|
@ -25,18 +25,37 @@ declare_clippy_lint! {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct BlacklistedName {
|
||||
blacklist: FxHashSet<String>,
|
||||
test_modules_deep: u32,
|
||||
}
|
||||
|
||||
impl BlacklistedName {
|
||||
pub fn new(blacklist: FxHashSet<String>) -> Self {
|
||||
Self { blacklist }
|
||||
Self {
|
||||
blacklist,
|
||||
test_modules_deep: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn in_test_module(&self) -> bool {
|
||||
self.test_modules_deep != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(cx.tcx, item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
// Check whether we are under the `test` attribute.
|
||||
if self.in_test_module() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let PatKind::Binding(.., ident, _) = pat.kind {
|
||||
if self.blacklist.contains(&ident.name.to_string()) {
|
||||
span_lint(
|
||||
|
@ -48,4 +67,10 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for naive byte counts
|
||||
|
@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
|
|||
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
|
||||
if count.ident.name == sym!(count);
|
||||
if count_args.len() == 1;
|
||||
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
|
||||
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
|
||||
if filter.ident.name == sym!(filter);
|
||||
if filter_args.len() == 2;
|
||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
|
||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
if body.params.len() == 1;
|
||||
if let Some(argname) = get_pat_name(body.params[0].pat);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, remove_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
|
||||
then {
|
||||
let needle = match get_path_name(l) {
|
||||
Some(name) if check_arg(name, argname, r) => r,
|
||||
_ => match get_path_name(r) {
|
||||
Some(name) if check_arg(name, argname, l) => l,
|
||||
_ => { return; }
|
||||
}
|
||||
};
|
||||
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
|
||||
return;
|
||||
}
|
||||
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
|
||||
filter_args[0].kind {
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
&filter_args[0]
|
||||
&filter_recv
|
||||
}
|
||||
} else {
|
||||
&filter_args[0]
|
||||
&filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
|
@ -91,24 +92,3 @@ 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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
|
@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
|||
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||
// match expression must be a local binding
|
||||
// match <local> { .. }
|
||||
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
|
||||
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
|
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
|||
});
|
||||
result
|
||||
}
|
||||
|
||||
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
|
||||
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
|
||||
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => expr = e,
|
||||
ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
|
@ -122,7 +121,7 @@ impl LateLintPass<'_> for Default {
|
|||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let Some(expr) = local.init;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if !in_external_macro(cx.tcx.sess, expr.span);
|
||||
if !in_macro(expr.span);
|
||||
// only take bindings to identifiers
|
||||
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
|
||||
// only when assigning `... = Default::default()`
|
||||
|
|
|
@ -7,9 +7,10 @@ use rustc_hir::{
|
|||
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
|
||||
Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::{
|
||||
hir::map::Map,
|
||||
lint::in_external_macro,
|
||||
ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -73,6 +74,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
/// Check whether a passed literal has potential to cause fallback or not.
|
||||
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(self.cx.sess(), lit.span);
|
||||
if let Some(ty_bound) = self.ty_bounds.last();
|
||||
if matches!(lit.node,
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
|
||||
|
|
|
@ -149,7 +149,7 @@ declare_deprecated_lint! {
|
|||
/// enables the `enum_variant_names` lint for public items.
|
||||
/// ```
|
||||
pub PUB_ENUM_VARIANT_NAMES,
|
||||
"set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items"
|
||||
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
|
@ -158,5 +158,5 @@ declare_deprecated_lint! {
|
|||
/// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which
|
||||
/// enables the `wrong_self_conversion` lint for public items.
|
||||
pub WRONG_PUB_SELF_CONVENTION,
|
||||
"set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items"
|
||||
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
|
||||
}
|
||||
|
|
|
@ -410,11 +410,8 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
match block.rules {
|
||||
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => {
|
||||
self.has_unsafe = true;
|
||||
},
|
||||
_ => {},
|
||||
if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
|
||||
self.has_unsafe = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
|
|||
use clippy_utils::fn_def_id;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Symbol;
|
||||
|
@ -13,21 +13,14 @@ declare_clippy_lint! {
|
|||
/// **Why is this bad?** Some methods are undesirable in certain contexts,
|
||||
/// and it's beneficial to lint for them as needed.
|
||||
///
|
||||
/// **Known problems:** Currently, you must write each function as a
|
||||
/// fully-qualified path. This lint doesn't support aliases or reexported
|
||||
/// names; be aware that many types in `std` are actually reexports.
|
||||
///
|
||||
/// For example, if you want to disallow `Duration::as_secs`, your clippy.toml
|
||||
/// configuration would look like
|
||||
/// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
|
||||
/// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// An example clippy.toml configuration:
|
||||
/// ```toml
|
||||
/// # clippy.toml
|
||||
/// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
|
||||
/// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,ignore
|
||||
|
@ -52,6 +45,7 @@ declare_clippy_lint! {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedMethod {
|
||||
disallowed: FxHashSet<Vec<Symbol>>,
|
||||
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
|
||||
}
|
||||
|
||||
impl DisallowedMethod {
|
||||
|
@ -61,6 +55,7 @@ impl DisallowedMethod {
|
|||
.iter()
|
||||
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
|
||||
.collect(),
|
||||
def_ids: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,10 +63,20 @@ impl DisallowedMethod {
|
|||
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
|
||||
for path in &self.disallowed {
|
||||
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
|
||||
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
|
||||
{
|
||||
self.def_ids.insert((id, path.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(def_id) = fn_def_id(cx, expr) {
|
||||
let func_path = cx.get_def_path(def_id);
|
||||
if self.disallowed.contains(&func_path) {
|
||||
if self.def_ids.iter().any(|(id, _)| def_id == *id) {
|
||||
let func_path = cx.get_def_path(def_id);
|
||||
let func_path_string = func_path
|
||||
.into_iter()
|
||||
.map(Symbol::to_ident_string)
|
||||
|
|
112
clippy_lints/src/disallowed_script_idents.rs
Normal file
112
clippy_lints/src/disallowed_script_idents.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
clippy_lints/src/disallowed_type.rs
Normal file
126
clippy_lints/src/disallowed_type.rs
Normal 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),
|
||||
);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
|
||||
use clippy_utils::source::first_line_of_span;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
|
||||
use if_chain::if_chain;
|
||||
|
@ -37,7 +38,8 @@ declare_clippy_lint! {
|
|||
/// consider that.
|
||||
///
|
||||
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
|
||||
/// for is limited, and there are still false positives.
|
||||
/// for is limited, and there are still false positives. HTML elements and their
|
||||
/// content are not linted.
|
||||
///
|
||||
/// In addition, when writing documentation comments, including `[]` brackets
|
||||
/// inside a link text would trip the parser. Therfore, documenting link with
|
||||
|
@ -469,11 +471,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
spans: &[(usize, Span)],
|
||||
) -> DocHeaders {
|
||||
// true if a safety header was found
|
||||
use pulldown_cmark::CodeBlockKind;
|
||||
use pulldown_cmark::Event::{
|
||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||
};
|
||||
use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
|
||||
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
|
||||
use pulldown_cmark::{CodeBlockKind, CowStr};
|
||||
|
||||
let mut headers = DocHeaders {
|
||||
safety: false,
|
||||
|
@ -485,6 +487,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
let mut in_heading = false;
|
||||
let mut is_rust = false;
|
||||
let mut edition = None;
|
||||
let mut ticks_unbalanced = false;
|
||||
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
|
||||
let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
|
||||
for (event, range) in events {
|
||||
match event {
|
||||
Start(CodeBlock(ref kind)) => {
|
||||
|
@ -510,13 +515,42 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
},
|
||||
Start(Link(_, url, _)) => in_link = Some(url),
|
||||
End(Link(..)) => in_link = None,
|
||||
Start(Heading(_)) => in_heading = true,
|
||||
End(Heading(_)) => in_heading = false,
|
||||
Start(Heading(_) | Paragraph | Item) => {
|
||||
if let Start(Heading(_)) = event {
|
||||
in_heading = true;
|
||||
}
|
||||
ticks_unbalanced = false;
|
||||
let (_, span) = get_current_span(spans, range.start);
|
||||
paragraph_span = first_line_of_span(cx, span);
|
||||
},
|
||||
End(Heading(_) | Paragraph | Item) => {
|
||||
if let End(Heading(_)) = event {
|
||||
in_heading = false;
|
||||
}
|
||||
if ticks_unbalanced {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
paragraph_span,
|
||||
"backticks are unbalanced",
|
||||
None,
|
||||
"a backtick may be missing a pair",
|
||||
);
|
||||
} else {
|
||||
for (text, span) in text_to_check {
|
||||
check_text(cx, valid_idents, &text, span);
|
||||
}
|
||||
}
|
||||
text_to_check = Vec::new();
|
||||
},
|
||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||
Html(_html) => (), // HTML is weird, just ignore it
|
||||
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
|
||||
FootnoteReference(text) | Text(text) => {
|
||||
if Some(&text) == in_link.as_ref() {
|
||||
let (begin, span) = get_current_span(spans, range.start);
|
||||
paragraph_span = paragraph_span.with_hi(span.hi());
|
||||
ticks_unbalanced |= text.contains('`');
|
||||
if Some(&text) == in_link.as_ref() || ticks_unbalanced {
|
||||
// Probably a link of the form `<http://example.com>`
|
||||
// Which are represented as a link to "http://example.com" with
|
||||
// text "http://example.com" by pulldown-cmark
|
||||
|
@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers.safety |= in_heading && text.trim() == "Safety";
|
||||
headers.errors |= in_heading && text.trim() == "Errors";
|
||||
headers.panics |= in_heading && text.trim() == "Panics";
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
|
||||
Ok(o) => o,
|
||||
Err(e) => e - 1,
|
||||
};
|
||||
let (begin, span) = spans[index];
|
||||
if in_code {
|
||||
if is_rust {
|
||||
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
|
||||
|
@ -538,8 +567,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
} else {
|
||||
// Adjust for the beginning of the current `Event`
|
||||
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
|
||||
|
||||
check_text(cx, valid_idents, &text, span);
|
||||
text_to_check.push((text, span));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -547,6 +575,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers
|
||||
}
|
||||
|
||||
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
|
||||
Ok(o) => o,
|
||||
Err(e) => e - 1,
|
||||
};
|
||||
spans[index]
|
||||
}
|
||||
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
||||
fn has_needless_main(code: &str, edition: Edition) -> bool {
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
|
|
|
@ -38,7 +38,7 @@ declare_clippy_lint! {
|
|||
/// let a = tmp + x;
|
||||
/// ```
|
||||
pub EVAL_ORDER_DEPENDENCE,
|
||||
complexity,
|
||||
suspicious,
|
||||
"whether a variable read occurs before a write depends on sub-expression evaluation order"
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub FLOAT_EQUALITY_WITHOUT_ABS,
|
||||
correctness,
|
||||
suspicious,
|
||||
"float equality check without `.abs()`"
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ declare_clippy_lint! {
|
|||
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
|
||||
/// ```
|
||||
pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
style,
|
||||
suspicious,
|
||||
"suspicious formatting of `*=`, `-=` or `!=`"
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
style,
|
||||
suspicious,
|
||||
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_ELSE_FORMATTING,
|
||||
style,
|
||||
suspicious,
|
||||
"suspicious formatting of `else`"
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen {
|
|||
|
||||
// LHS of subtraction is "x.len()"
|
||||
if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args, _) = &lhs.kind;
|
||||
if arg_lhs_path.ident.name == sym!(len);
|
||||
if arg_lhs_path.ident.name == sym::len;
|
||||
if let Some(arg_lhs_struct) = lhs_args.get(0);
|
||||
|
||||
// The two vectors referenced (x in x.get(...) and in x.len())
|
||||
|
|
|
@ -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> {
|
||||
mutex_lock_called: bool,
|
||||
found_mutex: Option<&'tcx Expr<'tcx>>,
|
||||
|
|
|
@ -128,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
|||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||
if_chain! {
|
||||
if item.ident.as_str() == "len";
|
||||
if item.ident.name == sym::len;
|
||||
if let ImplItemKind::Fn(sig, _) = &item.kind;
|
||||
if sig.decl.implicit_self.has_implicit_self();
|
||||
if cx.access_levels.is_exported(item.hir_id());
|
||||
|
@ -189,8 +189,8 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
|||
}
|
||||
|
||||
fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
|
||||
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: &str) -> bool {
|
||||
item.ident.name.as_str() == name
|
||||
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
|
||||
item.ident.name == name
|
||||
&& if let AssocItemKind::Fn { has_self } = item.kind {
|
||||
has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 }
|
||||
} else {
|
||||
|
@ -207,7 +207,9 @@ fn 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();
|
||||
fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
|
||||
|
||||
|
@ -401,7 +403,7 @@ fn check_len(
|
|||
return;
|
||||
}
|
||||
|
||||
if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
|
||||
if method_name == sym::len && args.len() == 1 && has_is_empty(cx, &args[0]) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -60,9 +60,9 @@ use rustc_session::Session;
|
|||
/// 4. The `description` that contains a short explanation on what's wrong with code where the
|
||||
/// lint is triggered.
|
||||
///
|
||||
/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default.
|
||||
/// As said in the README.md of this repository, if the lint level mapping changes, please update
|
||||
/// README.md.
|
||||
/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
|
||||
/// enabled by default. As said in the README.md of this repository, if the lint level mapping
|
||||
/// changes, please update README.md.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -106,6 +106,11 @@ macro_rules! declare_clippy_lint {
|
|||
$(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
|
@ -187,6 +192,8 @@ mod default_numeric_fallback;
|
|||
mod dereference;
|
||||
mod derive;
|
||||
mod disallowed_method;
|
||||
mod disallowed_script_idents;
|
||||
mod disallowed_type;
|
||||
mod doc;
|
||||
mod double_comparison;
|
||||
mod double_parens;
|
||||
|
@ -254,7 +261,6 @@ mod manual_strip;
|
|||
mod manual_unwrap_or;
|
||||
mod map_clone;
|
||||
mod map_err_ignore;
|
||||
mod map_identity;
|
||||
mod map_unit_fn;
|
||||
mod match_on_vec_items;
|
||||
mod matches;
|
||||
|
@ -267,6 +273,7 @@ mod misc;
|
|||
mod misc_early;
|
||||
mod missing_const_for_fn;
|
||||
mod missing_doc;
|
||||
mod missing_enforced_import_rename;
|
||||
mod missing_inline;
|
||||
mod modulo_arithmetic;
|
||||
mod multiple_crate_versions;
|
||||
|
@ -293,6 +300,7 @@ mod no_effect;
|
|||
mod non_copy_const;
|
||||
mod non_expressive_names;
|
||||
mod non_octal_unix_permissions;
|
||||
mod nonstandard_macro_braces;
|
||||
mod open_options;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
|
@ -483,11 +491,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
);
|
||||
store.register_removed(
|
||||
"clippy::pub_enum_variant_names",
|
||||
"set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items",
|
||||
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::wrong_pub_self_convention",
|
||||
"set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items",
|
||||
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
|
@ -583,6 +591,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
derive::UNSAFE_DERIVE_DESERIALIZE,
|
||||
disallowed_method::DISALLOWED_METHOD,
|
||||
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
|
||||
disallowed_type::DISALLOWED_TYPE,
|
||||
doc::DOC_MARKDOWN,
|
||||
doc::MISSING_ERRORS_DOC,
|
||||
doc::MISSING_PANICS_DOC,
|
||||
|
@ -705,7 +715,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
manual_unwrap_or::MANUAL_UNWRAP_OR,
|
||||
map_clone::MAP_CLONE,
|
||||
map_err_ignore::MAP_ERR_IGNORE,
|
||||
map_identity::MAP_IDENTITY,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
match_on_vec_items::MATCH_ON_VEC_ITEMS,
|
||||
|
@ -730,6 +739,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
|
||||
mem_replace::MEM_REPLACE_WITH_DEFAULT,
|
||||
mem_replace::MEM_REPLACE_WITH_UNINIT,
|
||||
methods::APPEND_INSTEAD_OF_EXTEND,
|
||||
methods::BIND_INSTEAD_OF_MAP,
|
||||
methods::BYTES_NTH,
|
||||
methods::CHARS_LAST_CMP,
|
||||
|
@ -765,6 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
methods::MANUAL_STR_REPEAT,
|
||||
methods::MAP_COLLECT_RESULT_UNIT,
|
||||
methods::MAP_FLATTEN,
|
||||
methods::MAP_IDENTITY,
|
||||
methods::MAP_UNWRAP_OR,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::OK_EXPECT,
|
||||
|
@ -810,6 +821,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
misc_early::ZERO_PREFIXED_LITERAL,
|
||||
missing_const_for_fn::MISSING_CONST_FOR_FN,
|
||||
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
|
||||
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
|
||||
modulo_arithmetic::MODULO_ARITHMETIC,
|
||||
multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
|
||||
|
@ -843,6 +855,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
non_expressive_names::MANY_SINGLE_CHAR_NAMES,
|
||||
non_expressive_names::SIMILAR_NAMES,
|
||||
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
|
||||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
option_env_unwrap::OPTION_ENV_UNWRAP,
|
||||
option_if_let_else::OPTION_IF_LET_ELSE,
|
||||
|
@ -989,6 +1002,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(create_dir::CREATE_DIR),
|
||||
LintId::of(dbg_macro::DBG_MACRO),
|
||||
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
|
||||
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
|
||||
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
|
||||
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
|
||||
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
|
||||
|
@ -1013,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(misc::FLOAT_CMP_CONST),
|
||||
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
|
||||
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
|
||||
LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
|
||||
LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
|
||||
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
|
||||
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
|
||||
|
@ -1090,6 +1105,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
|
||||
LintId::of(methods::FILTER_MAP_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_OPTION),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
|
@ -1260,7 +1276,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(map_identity::MAP_IDENTITY),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
|
@ -1276,6 +1291,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_NTH),
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
|
@ -1286,7 +1302,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
|
@ -1301,6 +1316,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
|
@ -1359,6 +1375,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
|
@ -1447,7 +1464,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(assign_ops::ASSIGN_OP_PATTERN),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
|
@ -1465,9 +1481,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(eq_op::OP_REF),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE),
|
||||
LintId::of(float_literal::EXCESSIVE_PRECISION),
|
||||
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
|
||||
LintId::of(from_over_into::FROM_OVER_INTO),
|
||||
LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
|
||||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
|
@ -1480,7 +1493,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(len_zero::LEN_ZERO),
|
||||
LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||
LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
|
||||
LintId::of(loops::EMPTY_LOOP),
|
||||
LintId::of(loops::FOR_KV_MAP),
|
||||
LintId::of(loops::NEEDLESS_RANGE_LOOP),
|
||||
LintId::of(loops::SAME_ITEM_PUSH),
|
||||
|
@ -1501,7 +1513,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(methods::BYTES_NTH),
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(methods::ITER_NEXT_SLICE),
|
||||
|
@ -1535,6 +1546,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
|
||||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(ptr::CMP_NULL),
|
||||
LintId::of(ptr::PTR_ARG),
|
||||
LintId::of(ptr_eq::PTR_EQ),
|
||||
|
@ -1560,7 +1572,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
]);
|
||||
|
||||
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
|
@ -1570,7 +1581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(double_parens::DOUBLE_PARENS),
|
||||
LintId::of(duration_subsec::DURATION_SUBSEC),
|
||||
LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
|
||||
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
|
||||
LintId::of(explicit_write::EXPLICIT_WRITE),
|
||||
LintId::of(format::USELESS_FORMAT),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
|
@ -1581,12 +1591,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(lifetimes::NEEDLESS_LIFETIMES),
|
||||
LintId::of(loops::EXPLICIT_COUNTER_LOOP),
|
||||
LintId::of(loops::MANUAL_FLATTEN),
|
||||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(loops::SINGLE_ELEMENT_LOOP),
|
||||
LintId::of(loops::WHILE_LET_LOOP),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||
LintId::of(map_identity::MAP_IDENTITY),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(matches::MATCH_AS_REF),
|
||||
|
@ -1601,11 +1609,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(methods::ITER_COUNT),
|
||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(methods::MANUAL_FIND_MAP),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
|
||||
|
@ -1674,7 +1682,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
|
||||
LintId::of(eq_op::EQ_OP),
|
||||
LintId::of(erasing_op::ERASING_OP),
|
||||
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
|
||||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
|
@ -1684,7 +1691,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
|
||||
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
|
||||
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
|
||||
LintId::of(loops::ITER_NEXT_LOOP),
|
||||
LintId::of(loops::NEVER_LOOP),
|
||||
LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
|
||||
|
@ -1699,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(misc::CMP_NAN),
|
||||
LintId::of(misc::FLOAT_CMP),
|
||||
LintId::of(misc::MODULO_ONE),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
|
@ -1710,8 +1715,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
|
@ -1728,6 +1731,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::suspicious", None, vec![
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
|
||||
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
|
||||
LintId::of(loops::EMPTY_LOOP),
|
||||
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
|
||||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(entry::MAP_ENTRY),
|
||||
LintId::of(escape::BOXED_LOCAL),
|
||||
|
@ -1735,6 +1755,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
|
||||
LintId::of(loops::MANUAL_MEMCPY),
|
||||
LintId::of(loops::NEEDLESS_COLLECT),
|
||||
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
|
||||
LintId::of(methods::EXPECT_FUN_CALL),
|
||||
LintId::of(methods::ITER_NTH),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
|
@ -1761,6 +1782,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
|
||||
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
|
||||
LintId::of(disallowed_method::DISALLOWED_METHOD),
|
||||
LintId::of(disallowed_type::DISALLOWED_TYPE),
|
||||
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
|
||||
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
|
||||
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
|
||||
|
@ -2038,8 +2060,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
|
||||
single_char_binding_names_threshold,
|
||||
});
|
||||
let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(¯o_matcher));
|
||||
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
||||
store.register_late_pass(|| box map_identity::MapIdentity);
|
||||
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
||||
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
|
||||
store.register_late_pass(|| box repeat_once::RepeatOnce);
|
||||
|
@ -2066,7 +2089,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
|
||||
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
|
||||
store.register_late_pass(|| box unused_async::UnusedAsync);
|
||||
|
||||
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));
|
||||
let import_renames = conf.enforced_import_renames.clone();
|
||||
store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone()));
|
||||
let scripts = conf.allowed_scripts.clone();
|
||||
store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
|
@ -118,7 +118,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, _, len_args, _) = end.kind;
|
||||
if method.ident.name == sym!(len);
|
||||
if method.ident.name == sym::len;
|
||||
if len_args.len() == 1;
|
||||
if let Some(arg) = len_args.get(0);
|
||||
if path_to_local(arg) == path_to_local(base);
|
||||
|
|
|
@ -199,7 +199,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub FOR_LOOPS_OVER_FALLIBLES,
|
||||
correctness,
|
||||
suspicious,
|
||||
"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
|
||||
}
|
||||
|
||||
|
@ -313,7 +313,7 @@ declare_clippy_lint! {
|
|||
/// loop {}
|
||||
/// ```
|
||||
pub EMPTY_LOOP,
|
||||
style,
|
||||
suspicious,
|
||||
"empty `loop {}`, which should block or sleep"
|
||||
}
|
||||
|
||||
|
@ -401,7 +401,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub MUT_RANGE_BOUND,
|
||||
complexity,
|
||||
suspicious,
|
||||
"for loop over a range where one of the bounds is a mutable variable"
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id};
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
||||
|
@ -24,10 +24,8 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
|||
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
|
||||
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
|
||||
if let Some(generic_args) = chain_method.args;
|
||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
||||
if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
|
||||
then {
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let is_empty_sugg = "next().is_none()".to_string();
|
||||
let method_name = &*method.ident.name.as_str();
|
||||
|
@ -72,40 +70,25 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
|||
}
|
||||
|
||||
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
||||
fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
|
||||
if let Some(ty) = ty {
|
||||
return Some(ty.hir_id);
|
||||
}
|
||||
|
||||
if let Some(generic_args) = method_args {
|
||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
|
||||
return Some(ty.hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
for stmt in block.stmts {
|
||||
if_chain! {
|
||||
if let StmtKind::Local(
|
||||
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
|
||||
init: Some(init_expr), ty, .. }
|
||||
) = stmt.kind;
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let PatKind::Binding(_, id, ..) = local.pat.kind;
|
||||
if let Some(init_expr) = local.init;
|
||||
if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
|
||||
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
|
||||
if let Some(hir_id) = get_hir_id(*ty, method_name.args);
|
||||
if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
|
||||
let ty = cx.typeck_results().expr_ty(init_expr);
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::LinkedList);
|
||||
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
|
||||
if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
|
||||
if let [iter_call] = &*iter_calls;
|
||||
then {
|
||||
let mut used_count_visitor = UsedCountVisitor {
|
||||
cx,
|
||||
id: *pat_id,
|
||||
id,
|
||||
count: 0,
|
||||
};
|
||||
walk_block(&mut used_count_visitor, block);
|
||||
|
@ -187,48 +170,40 @@ enum IterFunctionKind {
|
|||
struct IterFunctionVisitor {
|
||||
uses: Vec<IterFunction>,
|
||||
seen_other: bool,
|
||||
target: Ident,
|
||||
target: HirId,
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
// Check function calls on our collection
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
|
||||
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0);
|
||||
if let &[name] = &path.segments;
|
||||
if name.ident == self.target;
|
||||
then {
|
||||
let len = sym!(len);
|
||||
let is_empty = sym!(is_empty);
|
||||
let contains = sym!(contains);
|
||||
match method_name.ident.name {
|
||||
sym::into_iter => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
|
||||
),
|
||||
name if name == len => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::Len, span: expr.span }
|
||||
),
|
||||
name if name == is_empty => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
|
||||
),
|
||||
name if name == contains => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
|
||||
),
|
||||
if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
|
||||
if path_to_local_id(recv, self.target) {
|
||||
match &*method_name.ident.name.as_str() {
|
||||
"into_iter" => self.uses.push(IterFunction {
|
||||
func: IterFunctionKind::IntoIter,
|
||||
span: expr.span,
|
||||
}),
|
||||
"len" => self.uses.push(IterFunction {
|
||||
func: IterFunctionKind::Len,
|
||||
span: expr.span,
|
||||
}),
|
||||
"is_empty" => self.uses.push(IterFunction {
|
||||
func: IterFunctionKind::IsEmpty,
|
||||
span: expr.span,
|
||||
}),
|
||||
"contains" => self.uses.push(IterFunction {
|
||||
func: IterFunctionKind::Contains(args[0].span),
|
||||
span: expr.span,
|
||||
}),
|
||||
_ => self.seen_other = true,
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check if the collection is used for anything else
|
||||
if_chain! {
|
||||
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr;
|
||||
if let &[name] = &path.segments;
|
||||
if name.ident == self.target;
|
||||
then {
|
||||
self.seen_other = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
if path_to_local_id(expr, self.target) {
|
||||
self.seen_other = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,10 +237,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
|
|||
|
||||
/// Detect the occurrences of calls to `iter` or `into_iter` for the
|
||||
/// given identifier
|
||||
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
|
||||
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
|
||||
let mut visitor = IterFunctionVisitor {
|
||||
uses: Vec::new(),
|
||||
target: identifier,
|
||||
target: id,
|
||||
seen_other: false,
|
||||
};
|
||||
visitor.visit_block(block);
|
||||
|
|
|
@ -192,7 +192,7 @@ fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
|
|||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind;
|
||||
if len_args.len() == 1;
|
||||
if method.ident.name == sym!(len);
|
||||
if method.ident.name == sym::len;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.name == var;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use super::WHILE_LET_ON_ITERATOR;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
|
||||
use clippy_utils::{
|
||||
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||
|
@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
|
||||
// The iterator expression will be used on the next iteration unless it is declared within the outer
|
||||
// loop.
|
||||
if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
|
||||
// The iterator expression will be used on the next iteration (for loops), or on the next call (for
|
||||
// closures) unless it is declared within the enclosing expression. TODO: Check for closures
|
||||
// used where an `FnOnce` type is expected.
|
||||
let local_id = match iter_expr.path {
|
||||
Res::Local(id) => id,
|
||||
_ => return true,
|
||||
|
|
|
@ -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::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
|
||||
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
|
||||
peel_hir_expr_refs,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{
|
||||
symbol::{sym, Ident},
|
||||
SyntaxContext,
|
||||
};
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
|
||||
|
@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap {
|
|||
scrutinee_str.into()
|
||||
};
|
||||
|
||||
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
|
||||
match can_pass_as_func(cx, some_binding, some_expr) {
|
||||
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||
match can_pass_as_func(cx, id, some_expr) {
|
||||
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
|
||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
||||
},
|
||||
_ => {
|
||||
if match_var(some_expr, some_binding.name)
|
||||
if path_to_local_id(some_expr, id)
|
||||
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
|
||||
&& binding_ref.is_some()
|
||||
{
|
||||
|
@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap {
|
|||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, [arg])
|
||||
if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
|
||||
if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
|
||||
{
|
||||
Some(func)
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
clippy_utils::attrs::is_doc_hidden(attrs)
|
||||
clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
|
@ -1033,7 +1033,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
|
||||
// Accumulate the variants which should be put in place of the wildcard because they're not
|
||||
// already covered.
|
||||
let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
|
||||
let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
|
||||
let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
|
||||
|
||||
let mut path_prefix = CommonPrefixSearcher::None;
|
||||
for arm in arms {
|
||||
|
@ -1118,7 +1119,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
|
||||
match missing_variants.as_slice() {
|
||||
[] => (),
|
||||
[x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg(
|
||||
[x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
|
||||
wildcard_span,
|
||||
|
@ -1129,7 +1130,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
),
|
||||
variants => {
|
||||
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
|
||||
let message = if adt_def.is_variant_list_non_exhaustive() {
|
||||
let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
|
||||
suggestions.push("_".into());
|
||||
"wildcard matches known variants and will also match future added variants"
|
||||
} else {
|
||||
|
@ -2266,7 +2267,8 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
|
||||
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
|
||||
.help("...or consider changing the match arm bodies");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
41
clippy_lints/src/methods/append_instead_of_extend.rs
Normal file
41
clippy_lints/src/methods/append_instead_of_extend.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym};
|
|||
use super::FILTER_MAP_IDENTITY;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let apply_lint = |message: &str| {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FILTER_MAP_IDENTITY,
|
||||
filter_map_span.with_hi(expr.span.hi()),
|
||||
message,
|
||||
"try",
|
||||
"flatten()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
|
||||
if path_to_local_id(&body.value, binding_id);
|
||||
then {
|
||||
apply_lint("called `filter_map(|x| x)` on an `Iterator`");
|
||||
}
|
||||
}
|
||||
|
||||
if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
|
||||
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FILTER_MAP_IDENTITY,
|
||||
filter_map_span.with_hi(expr.span.hi()),
|
||||
"use of `filter_map` with an identity function",
|
||||
"try",
|
||||
"flatten()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
|
||||
use if_chain::if_chain;
|
||||
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
|
|||
flat_map_arg: &'tcx hir::Expr<'_>,
|
||||
flat_map_span: Span,
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let apply_lint = |message: &str| {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FLAT_MAP_IDENTITY,
|
||||
flat_map_span.with_hi(expr.span.hi()),
|
||||
message,
|
||||
"try",
|
||||
"flatten()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
|
||||
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.name == binding_ident.name;
|
||||
|
||||
then {
|
||||
apply_lint("called `flat_map(|x| x)` on an `Iterator`");
|
||||
}
|
||||
}
|
||||
|
||||
if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
|
||||
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FLAT_MAP_IDENTITY,
|
||||
flat_map_span.with_hi(expr.span.hi()),
|
||||
"use of `flat_map` with an identity function",
|
||||
"try",
|
||||
"flatten()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
38
clippy_lints/src/methods/map_identity.rs
Normal file
38
clippy_lints/src/methods/map_identity.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod append_instead_of_extend;
|
||||
mod bind_instead_of_map;
|
||||
mod bytes_nth;
|
||||
mod chars_cmp;
|
||||
|
@ -35,6 +36,7 @@ mod manual_saturating_arithmetic;
|
|||
mod manual_str_repeat;
|
||||
mod map_collect_result_unit;
|
||||
mod map_flatten;
|
||||
mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod ok_expect;
|
||||
mod option_as_ref_deref;
|
||||
|
@ -1031,6 +1033,30 @@ declare_clippy_lint! {
|
|||
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for occurrences where one vector gets extended instead of append
|
||||
///
|
||||
/// **Why is this bad?** Using `append` instead of `extend` is more concise and faster
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut a = vec![1, 2, 3];
|
||||
/// let mut b = vec![4, 5, 6];
|
||||
///
|
||||
/// // Bad
|
||||
/// a.extend(b.drain(..));
|
||||
///
|
||||
/// // Good
|
||||
/// a.append(&mut b);
|
||||
/// ```
|
||||
pub APPEND_INSTEAD_OF_EXTEND,
|
||||
perf,
|
||||
"using vec.append(&mut vec) to move the full range of a vecor to another"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
|
||||
/// `&str` or `String`.
|
||||
|
@ -1222,7 +1248,7 @@ declare_clippy_lint! {
|
|||
/// let _ = (0..3).map(|x| x + 2).count();
|
||||
/// ```
|
||||
pub SUSPICIOUS_MAP,
|
||||
complexity,
|
||||
suspicious,
|
||||
"suspicious usage of map"
|
||||
}
|
||||
|
||||
|
@ -1504,7 +1530,7 @@ declare_clippy_lint! {
|
|||
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
|
||||
/// ```
|
||||
pub FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
style,
|
||||
pedantic,
|
||||
"use `.collect()` instead of `::from_iter()`"
|
||||
}
|
||||
|
||||
|
@ -1561,6 +1587,29 @@ declare_clippy_lint! {
|
|||
"call to `filter_map` where `flatten` is sufficient"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
|
||||
///
|
||||
/// **Why is this bad?** It can be written more concisely without the call to `map`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = [1, 2, 3];
|
||||
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = [1, 2, 3];
|
||||
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
|
||||
/// ```
|
||||
pub MAP_IDENTITY,
|
||||
complexity,
|
||||
"using iterator.map(|x| x)"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of `.bytes().nth()`.
|
||||
///
|
||||
|
@ -1728,6 +1777,7 @@ impl_lint_pass!(Methods => [
|
|||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
FILTER_MAP_IDENTITY,
|
||||
MAP_IDENTITY,
|
||||
MANUAL_FILTER_MAP,
|
||||
MANUAL_FIND_MAP,
|
||||
OPTION_FILTER_MAP,
|
||||
|
@ -1760,7 +1810,8 @@ impl_lint_pass!(Methods => [
|
|||
INSPECT_FOR_EACH,
|
||||
IMPLICIT_CLONE,
|
||||
SUSPICIOUS_SPLITN,
|
||||
MANUAL_STR_REPEAT
|
||||
MANUAL_STR_REPEAT,
|
||||
APPEND_INSTEAD_OF_EXTEND
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
@ -1985,7 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
|
||||
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
|
||||
match (name, args) {
|
||||
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => {
|
||||
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
|
||||
zst_offset::check(cx, expr, recv);
|
||||
},
|
||||
("and_then", [arg]) => {
|
||||
|
@ -2022,7 +2073,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
|
||||
_ => expect_used::check(cx, expr, recv),
|
||||
},
|
||||
("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg),
|
||||
("extend", [arg]) => {
|
||||
string_extend_chars::check(cx, expr, recv, arg);
|
||||
append_instead_of_extend::check(cx, expr, recv, arg);
|
||||
},
|
||||
("filter_map", [arg]) => {
|
||||
unnecessary_filter_map::check(cx, expr, arg);
|
||||
filter_map_identity::check(cx, expr, arg, span);
|
||||
|
@ -2058,6 +2112,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
map_identity::check(cx, expr, recv, m_arg, span);
|
||||
},
|
||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||
("next", []) => {
|
||||
|
|
|
@ -87,7 +87,7 @@ pub(super) fn check<'tcx>(
|
|||
];
|
||||
|
||||
if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
|
||||
if path.ident.as_str() == "len" {
|
||||
if path.ident.name == sym::len {
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
|
||||
|
||||
match ty.kind() {
|
||||
|
|
102
clippy_lints/src/missing_enforced_import_rename.rs
Normal file
102
clippy_lints/src/missing_enforced_import_rename.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub MUTABLE_KEY_TYPE,
|
||||
correctness,
|
||||
suspicious,
|
||||
"Check for mutable `Map`/`Set` key type"
|
||||
}
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
|
|||
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
|
||||
BlockCheckMode::DefaultBlock => Some(vec![&**e]),
|
||||
// in case of compiler-inserted signaling blocks
|
||||
_ => reduce_expression(cx, e),
|
||||
BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
|
276
clippy_lints/src/nonstandard_macro_braces.rs
Normal file
276
clippy_lints/src/nonstandard_macro_braces.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -329,7 +329,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
|
|||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind;
|
||||
if len_path.ident.name == sym!(len) && len_args.len() == 1;
|
||||
if len_path.ident.name == sym::len && len_args.len() == 1;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
|
||||
|
|
|
@ -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 loc = mir::Location {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{in_macro, sugg};
|
||||
|
@ -45,6 +46,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned {
|
|||
if t_expr.is_unit();
|
||||
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
|
||||
if !snippet.ends_with('}');
|
||||
if cx.sess().source_map().is_multiline(block.span);
|
||||
then {
|
||||
// filter out the desugared `for` loop
|
||||
if let ExprKind::DropTemps(..) = &expr.kind {
|
||||
|
|
|
@ -26,7 +26,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_ARITHMETIC_IMPL,
|
||||
correctness,
|
||||
suspicious,
|
||||
"suspicious use of operators in impl of arithmetic trait"
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_OP_ASSIGN_IMPL,
|
||||
correctness,
|
||||
suspicious,
|
||||
"suspicious use of operators in impl of OpAssign trait"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path, eq_maybe_qself};
|
||||
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{meets_msrv, msrvs, over};
|
||||
use rustc_ast::mut_visit::*;
|
||||
|
@ -277,7 +277,8 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
|
|||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(
|
||||
k,
|
||||
TupleStruct(qself2, path2, ps2) if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
|
||||
TupleStruct(qself2, path2, ps2)
|
||||
if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
|
||||
),
|
||||
|k| always_pat!(k, TupleStruct(_, _, ps) => ps),
|
||||
),
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::same_type_and_consts;
|
||||
use clippy_utils::{in_macro, meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
self as hir,
|
||||
def::{self, DefKind},
|
||||
def::{CtorOf, DefKind, Res},
|
||||
def_id::LocalDefId,
|
||||
intravisit::{walk_ty, NestedVisitorMap, Visitor},
|
||||
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
|
||||
QPath, TyKind,
|
||||
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::{AssocKind, Ty};
|
||||
use rustc_middle::ty::AssocKind;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_span::Span;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -75,10 +74,9 @@ impl UseSelf {
|
|||
#[derive(Debug)]
|
||||
enum StackItem {
|
||||
Check {
|
||||
hir_id: HirId,
|
||||
impl_trait_ref_def_id: Option<LocalDefId>,
|
||||
types_to_skip: Vec<HirId>,
|
||||
types_to_lint: Vec<HirId>,
|
||||
impl_id: LocalDefId,
|
||||
in_body: u32,
|
||||
types_to_skip: FxHashSet<HirId>,
|
||||
},
|
||||
NoCheck,
|
||||
}
|
||||
|
@ -88,60 +86,41 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
|
|||
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if !is_item_interesting(item) {
|
||||
// This does two things:
|
||||
// 1) Reduce needless churn on `self.stack`
|
||||
// 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
|
||||
// in order to lint `foo() -> impl <..>`
|
||||
return;
|
||||
}
|
||||
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
|
||||
// relevant for linting, since this is the self type of the `impl` we're currently in. To
|
||||
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
|
||||
// we're in an `impl` or nested item, that we don't want to lint
|
||||
//
|
||||
// NB: If you push something on the stack in this method, remember to also pop it in the
|
||||
// `check_item_post` method.
|
||||
match &item.kind {
|
||||
ItemKind::Impl(Impl {
|
||||
self_ty: hir_self_ty,
|
||||
of_trait,
|
||||
..
|
||||
}) => {
|
||||
let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind {
|
||||
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
|
||||
parameters.as_ref().map_or(true, |params| {
|
||||
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
|
||||
if should_check {
|
||||
self.stack.push(StackItem::Check {
|
||||
hir_id: hir_self_ty.hir_id,
|
||||
impl_trait_ref_def_id,
|
||||
types_to_lint: Vec::new(),
|
||||
types_to_skip: Vec::new(),
|
||||
});
|
||||
} else {
|
||||
self.stack.push(StackItem::NoCheck);
|
||||
let stack_item = if_chain! {
|
||||
if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
|
||||
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
|
||||
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
|
||||
if parameters.as_ref().map_or(true, |params| {
|
||||
!params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
});
|
||||
then {
|
||||
StackItem::Check {
|
||||
impl_id: item.def_id,
|
||||
in_body: 0,
|
||||
types_to_skip: std::iter::once(self_ty.hir_id).collect(),
|
||||
}
|
||||
},
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::Fn(..)
|
||||
| ItemKind::Enum(..)
|
||||
| ItemKind::Struct(..)
|
||||
| ItemKind::Union(..)
|
||||
| ItemKind::Trait(..) => {
|
||||
self.stack.push(StackItem::NoCheck);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
StackItem::NoCheck
|
||||
}
|
||||
};
|
||||
self.stack.push(stack_item);
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
|
||||
use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
|
||||
match item.kind {
|
||||
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
|
||||
self.stack.pop();
|
||||
},
|
||||
_ => (),
|
||||
if is_item_interesting(item) {
|
||||
self.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
if_chain! {
|
||||
if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
|
||||
if let Some(&mut StackItem::Check {
|
||||
impl_trait_ref_def_id: Some(def_id),
|
||||
impl_id,
|
||||
ref mut types_to_skip,
|
||||
..
|
||||
}) = self.stack.last_mut();
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
|
||||
then {
|
||||
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
|
||||
// `Self`.
|
||||
|
@ -203,142 +182,76 @@ 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
|
||||
// we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
|
||||
// However the `node_type()` method can *only* be called in bodies.
|
||||
//
|
||||
// This method implementation determines which types should get linted in a `Body` and
|
||||
// which shouldn't, with a visitor. We could directly lint in the visitor, but then we
|
||||
// could only allow this lint on item scope. And we would have to check if those types are
|
||||
// already dealt with in `check_ty` anyway.
|
||||
if let Some(StackItem::Check {
|
||||
hir_id,
|
||||
types_to_lint,
|
||||
types_to_skip,
|
||||
..
|
||||
}) = self.stack.last_mut()
|
||||
{
|
||||
let self_ty = ty_from_hir_id(cx, *hir_id);
|
||||
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
|
||||
*in_body = in_body.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = LintTyCollector {
|
||||
cx,
|
||||
self_ty,
|
||||
types_to_lint: vec![],
|
||||
types_to_skip: vec![],
|
||||
};
|
||||
visitor.visit_expr(&body.value);
|
||||
types_to_lint.extend(visitor.types_to_lint);
|
||||
types_to_skip.extend(visitor.types_to_skip);
|
||||
fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
|
||||
if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
|
||||
*in_body = in_body.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
|
||||
if in_macro(hir_ty.span)
|
||||
|| in_impl(cx, hir_ty)
|
||||
|| !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let lint_dependend_on_expr_kind = if let Some(StackItem::Check {
|
||||
hir_id,
|
||||
types_to_lint,
|
||||
types_to_skip,
|
||||
..
|
||||
}) = self.stack.last()
|
||||
{
|
||||
if types_to_skip.contains(&hir_ty.hir_id) {
|
||||
false
|
||||
} else if types_to_lint.contains(&hir_ty.hir_id) {
|
||||
true
|
||||
if_chain! {
|
||||
if !in_macro(hir_ty.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if let Some(&StackItem::Check {
|
||||
impl_id,
|
||||
in_body,
|
||||
ref types_to_skip,
|
||||
}) = self.stack.last();
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
|
||||
if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
|
||||
if !types_to_skip.contains(&hir_ty.hir_id);
|
||||
let ty = if in_body > 0 {
|
||||
cx.typeck_results().node_type(hir_ty.hir_id)
|
||||
} else {
|
||||
let self_ty = ty_from_hir_id(cx, *hir_id);
|
||||
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if lint_dependend_on_expr_kind {
|
||||
// FIXME: this span manipulation should not be necessary
|
||||
// @flip1995 found an ast lowering issue in
|
||||
// https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162
|
||||
hir_ty_to_ty(cx.tcx, hir_ty)
|
||||
};
|
||||
if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
|
||||
let hir = cx.tcx.hir();
|
||||
let id = hir.get_parent_node(hir_ty.hir_id);
|
||||
|
||||
if !hir.opt_span(id).map_or(false, in_macro) {
|
||||
match hir.find(id) {
|
||||
Some(Node::Expr(Expr {
|
||||
kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
|
||||
..
|
||||
})) => span_lint_until_last_segment(cx, hir_ty.span, segment),
|
||||
_ => span_lint(cx, hir_ty.span),
|
||||
}
|
||||
if !hir.opt_span(id).map_or(false, in_macro);
|
||||
then {
|
||||
span_lint(cx, hir_ty.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool {
|
||||
let def_id = expr.hir_id.owner;
|
||||
if cx.tcx.has_typeck_results(def_id) {
|
||||
cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if_chain! {
|
||||
if !in_macro(expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
|
||||
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
|
||||
then {} else { return; }
|
||||
}
|
||||
|
||||
if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() {
|
||||
let self_ty = ty_from_hir_id(cx, *hir_id);
|
||||
|
||||
match &expr.kind {
|
||||
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
|
||||
if expr_ty_matches(cx, expr, self_ty) {
|
||||
match path.res {
|
||||
def::Res::SelfTy(..) => (),
|
||||
def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
|
||||
_ => {
|
||||
span_lint(cx, path.span);
|
||||
},
|
||||
match expr.kind {
|
||||
ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
|
||||
Res::SelfTy(..) => (),
|
||||
Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
|
||||
_ => span_lint(cx, path.span),
|
||||
},
|
||||
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
|
||||
ExprKind::Call(fun, _) => {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
|
||||
if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
|
||||
match ctor_of {
|
||||
CtorOf::Variant => lint_path_to_variant(cx, path),
|
||||
CtorOf::Struct => span_lint(cx, path.span),
|
||||
}
|
||||
}
|
||||
},
|
||||
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
|
||||
ExprKind::Call(fun, _) => {
|
||||
if let Expr {
|
||||
kind: ExprKind::Path(ref qpath),
|
||||
..
|
||||
} = fun
|
||||
{
|
||||
if expr_ty_matches(cx, expr, self_ty) {
|
||||
let res = cx.qpath_res(qpath, fun.hir_id);
|
||||
|
||||
if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
|
||||
match ctor_of {
|
||||
def::CtorOf::Variant => {
|
||||
span_lint_on_qpath_resolved(cx, qpath, true);
|
||||
},
|
||||
def::CtorOf::Struct => {
|
||||
span_lint_on_qpath_resolved(cx, qpath, false);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// unit enum variants (`Enum::A`)
|
||||
ExprKind::Path(qpath) => {
|
||||
if expr_ty_matches(cx, expr, self_ty) {
|
||||
span_lint_on_qpath_resolved(cx, qpath, true);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
// unit enum variants (`Enum::A`)
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,35 +277,6 @@ 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) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -405,66 +289,19 @@ fn span_lint(cx: &LateContext<'_>, span: Span) {
|
|||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
|
||||
let sp = span.with_hi(segment.ident.span.lo());
|
||||
// remove the trailing ::
|
||||
let span_without_last_segment = match snippet_opt(cx, sp) {
|
||||
Some(snippet) => match snippet.rfind("::") {
|
||||
Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
|
||||
None => sp,
|
||||
},
|
||||
None => sp,
|
||||
};
|
||||
span_lint(cx, span_without_last_segment);
|
||||
}
|
||||
|
||||
fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
|
||||
if path.segments.len() > 1 {
|
||||
span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
|
||||
fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
|
||||
if let [.., self_seg, _variant] = path.segments {
|
||||
let span = path
|
||||
.span
|
||||
.with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
|
||||
span_lint(cx, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) {
|
||||
if let QPath::Resolved(_, path) = qpath {
|
||||
if until_last_segment {
|
||||
span_lint_on_path_until_last_segment(cx, path);
|
||||
} else {
|
||||
span_lint(cx, path.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
|
||||
if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
|
||||
hir_ty_to_ty(cx.tcx, hir_ty)
|
||||
} else {
|
||||
unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
|
||||
}
|
||||
}
|
||||
|
||||
fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
|
||||
let map = cx.tcx.hir();
|
||||
let parent = map.get_parent_node(hir_ty.hir_id);
|
||||
if_chain! {
|
||||
if let Some(Node::Item(item)) = map.find(parent);
|
||||
if let ItemKind::Impl { .. } = item.kind;
|
||||
then {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if same_type_and_consts(ty, self_ty);
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
|
||||
then {
|
||||
!matches!(path.res, def::Res::SelfTy(..))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn is_item_interesting(item: &Item<'_>) -> bool {
|
||||
use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
|
||||
matches!(
|
||||
item.kind,
|
||||
Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
}
|
||||
}
|
||||
if_chain! {
|
||||
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into";
|
||||
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && name.ident.name == sym::try_into;
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, a, sym::result_type);
|
||||
|
|
|
@ -8,6 +8,13 @@ use std::error::Error;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fmt, fs, io};
|
||||
|
||||
/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Rename {
|
||||
pub path: String,
|
||||
pub rename: String,
|
||||
}
|
||||
|
||||
/// Conf with parse errors
|
||||
#[derive(Default)]
|
||||
pub struct TryConf {
|
||||
|
@ -24,6 +31,9 @@ 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 {
|
||||
($(
|
||||
#[doc = $doc:literal]
|
||||
|
@ -149,7 +159,7 @@ define_Conf! {
|
|||
"WebGL",
|
||||
"TensorFlow",
|
||||
"TrueType",
|
||||
"iOS", "macOS",
|
||||
"iOS", "macOS", "FreeBSD",
|
||||
"TeX", "LaTeX", "BibTeX", "BibLaTeX",
|
||||
"MinGW",
|
||||
"CamelCase",
|
||||
|
@ -182,20 +192,28 @@ define_Conf! {
|
|||
(vec_box_size_threshold: u64 = 4096),
|
||||
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
|
||||
(max_trait_bounds: u64 = 3),
|
||||
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
|
||||
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have
|
||||
(max_struct_bools: u64 = 3),
|
||||
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
|
||||
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have
|
||||
(max_fn_params_bools: u64 = 3),
|
||||
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
|
||||
(warn_on_all_wildcard_imports: bool = false),
|
||||
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
|
||||
(disallowed_methods: Vec<String> = Vec::new()),
|
||||
/// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
|
||||
(disallowed_types: Vec<String> = Vec::new()),
|
||||
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
|
||||
(unreadable_literal_lint_fractions: bool = true),
|
||||
/// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
|
||||
(upper_case_acronyms_aggressive: bool = false),
|
||||
/// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
|
||||
(cargo_ignore_publish: bool = false),
|
||||
/// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. <br> A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name.
|
||||
(standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
|
||||
/// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename.
|
||||
(enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
|
||||
/// Lint: RESTRICTED_SCRIPTS. The list of unicode scripts allowed to be used in the scope.
|
||||
(allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//! a simple mistake)
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{
|
||||
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
|
||||
|
@ -46,8 +47,9 @@ const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
|
|||
const DEPRECATED_LINT_LEVEL: &str = "none";
|
||||
/// This array holds Clippy's lint groups with their corresponding default lint level. The
|
||||
/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
|
||||
const DEFAULT_LINT_LEVELS: [(&str, &str); 8] = [
|
||||
const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
|
||||
("correctness", "deny"),
|
||||
("suspicious", "warn"),
|
||||
("restriction", "allow"),
|
||||
("style", "warn"),
|
||||
("pedantic", "allow"),
|
||||
|
@ -485,16 +487,32 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<St
|
|||
///
|
||||
/// Would result in `Hello world!\n=^.^=\n`
|
||||
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
|
||||
cx.tcx
|
||||
.hir()
|
||||
.attrs(item.hir_id())
|
||||
.iter()
|
||||
.filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string()))
|
||||
.reduce(|mut acc, sym| {
|
||||
acc.push_str(&sym);
|
||||
acc.push('\n');
|
||||
acc
|
||||
})
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
|
||||
let mut docs = String::from(&*lines.next()?.as_str());
|
||||
let mut in_code_block = false;
|
||||
for line in lines {
|
||||
docs.push('\n');
|
||||
let line = line.as_str();
|
||||
let line = &*line;
|
||||
if let Some(info) = line.trim_start().strip_prefix("```") {
|
||||
in_code_block = !in_code_block;
|
||||
if in_code_block {
|
||||
let lang = info
|
||||
.trim()
|
||||
.split(',')
|
||||
// remove rustdoc directives
|
||||
.find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
|
||||
// if no language is present, fill in "rust"
|
||||
.unwrap_or("rust");
|
||||
docs.push_str("```");
|
||||
docs.push_str(lang);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
docs.push_str(line);
|
||||
}
|
||||
Some(docs)
|
||||
}
|
||||
|
||||
fn get_lint_group_and_level_or_lint(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::in_macro;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::{in_macro, is_test_module_or_function};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
|
@ -106,7 +106,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
|
|||
|
||||
impl LateLintPass<'_> for WildcardImports {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(item) {
|
||||
if is_test_module_or_function(cx.tcx, item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
|
||||
}
|
||||
if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
|
||||
|
@ -183,8 +183,8 @@ impl LateLintPass<'_> for WildcardImports {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(item) {
|
||||
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(cx.tcx, item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,3 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
|
|||
fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
|
||||
segments.len() == 1 && segments[0].ident.name == kw::Super
|
||||
}
|
||||
|
||||
fn is_test_module_or_function(item: &Item<'_>) -> bool {
|
||||
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.54"
|
||||
version = "0.1.55"
|
||||
authors = ["The Rust Clippy Developers"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
|
|
@ -47,9 +47,14 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
|
|||
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
|
||||
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
|
||||
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
|
||||
eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
|
||||
},
|
||||
(Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
|
||||
lr == rr && eq_maybe_qself(lqself, rqself) &&eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
|
||||
lr == rr
|
||||
&& eq_maybe_qself(lqself, rqself)
|
||||
&& eq_path(lp, rp)
|
||||
&& unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
|
||||
},
|
||||
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
|
@ -82,7 +87,7 @@ pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
|
|||
match (l, r) {
|
||||
(Some(l), Some(r)) => eq_qself(l, r),
|
||||
(None, None) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,3 +157,8 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
|
|||
.filter_map(ast::Attribute::meta_item_list)
|
||||
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
|
||||
/// Return true if the attributes contain `#[unstable]`
|
||||
pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| attr.has_name(sym::unstable))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::float_cmp)]
|
||||
|
||||
use crate::{clip, sext, unsext};
|
||||
use crate::{clip, is_direct_expn_of, sext, unsext};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -230,7 +230,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
match e.kind {
|
||||
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
|
||||
ExprKind::Block(block, _) => self.block(block),
|
||||
ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
|
||||
ExprKind::Lit(ref lit) => {
|
||||
if is_direct_expn_of(e.span, "cfg").is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
|
||||
}
|
||||
},
|
||||
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
|
||||
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
|
||||
ExprKind::Repeat(value, _) => {
|
||||
|
|
|
@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
|
|||
use rustc_hir::{
|
||||
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
|
||||
ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
|
||||
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
|
||||
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::exports::Export;
|
||||
|
@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
|
|||
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
|
||||
}
|
||||
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
|
||||
if let [p] = path.segments {
|
||||
return p.ident.name == var;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
|
||||
match *path {
|
||||
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
|
||||
|
@ -707,16 +697,6 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the name of a `Pat`, if any.
|
||||
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
|
||||
match pat.kind {
|
||||
PatKind::Binding(.., ref spname, _) => Some(spname.name),
|
||||
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
|
||||
PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContainsName {
|
||||
pub name: Symbol,
|
||||
pub result: bool,
|
||||
|
@ -861,14 +841,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
|||
})
|
||||
}
|
||||
|
||||
/// Gets the loop enclosing the given expression, if any.
|
||||
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
/// Gets the loop or closure enclosing the given expression, if any.
|
||||
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
let map = tcx.hir();
|
||||
for (_, node) in map.parent_iter(expr.hir_id) {
|
||||
match node {
|
||||
Node::Expr(
|
||||
e @ Expr {
|
||||
kind: ExprKind::Loop(..),
|
||||
e
|
||||
@
|
||||
Expr {
|
||||
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
|
||||
..
|
||||
},
|
||||
) => return Some(e),
|
||||
|
@ -1399,6 +1381,55 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
|
||||
}
|
||||
|
||||
/// Checks if an expression represents the identity function
|
||||
/// Only examines closures and `std::convert::identity`
|
||||
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
|
||||
/// * `|x| x`
|
||||
/// * `|x| return x`
|
||||
/// * `|x| { return x }`
|
||||
/// * `|x| { return x; }`
|
||||
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
|
||||
let id = if_chain! {
|
||||
if let [param] = func.params;
|
||||
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
|
||||
then {
|
||||
id
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let mut expr = &func.value;
|
||||
loop {
|
||||
match expr.kind {
|
||||
#[rustfmt::skip]
|
||||
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
|
||||
| ExprKind::Ret(Some(e)) => expr = e,
|
||||
#[rustfmt::skip]
|
||||
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret_val)) = e.kind;
|
||||
then {
|
||||
expr = ret_val;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
||||
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
|
||||
let map = tcx.hir();
|
||||
|
@ -1654,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
|
|||
(e, count)
|
||||
}
|
||||
|
||||
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
|
||||
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
|
||||
pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => expr = e,
|
||||
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
@ -1683,3 +1727,15 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether item either has `test` attribute applied, or
|
||||
/// is a module with `test` in its name.
|
||||
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
|
||||
if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
|
||||
if tcx.has_attr(def_id.to_def_id(), sym::test) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::source::snippet;
|
||||
use crate::{get_pat_name, match_var};
|
||||
use crate::{path_to_local_id, strip_pat_refs};
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
|
||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub fn get_spans(
|
||||
|
@ -14,10 +14,11 @@ pub fn get_spans(
|
|||
replacements: &[(&'static str, &'static str)],
|
||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
|
||||
get_binding_name(&body.params[idx]).map_or_else(
|
||||
|| Some(vec![]),
|
||||
|name| extract_clone_suggestions(cx, name, replacements, body),
|
||||
)
|
||||
if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
|
||||
extract_clone_suggestions(cx, binding_id, replacements, body)
|
||||
} else {
|
||||
Some(vec![])
|
||||
}
|
||||
} else {
|
||||
Some(vec![])
|
||||
}
|
||||
|
@ -25,13 +26,13 @@ pub fn get_spans(
|
|||
|
||||
fn extract_clone_suggestions<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
name: Symbol,
|
||||
id: HirId,
|
||||
replace: &[(&'static str, &'static str)],
|
||||
body: &'tcx Body<'_>,
|
||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||
let mut visitor = PtrCloneVisitor {
|
||||
cx,
|
||||
name,
|
||||
id,
|
||||
replace,
|
||||
spans: vec![],
|
||||
abort: false,
|
||||
|
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
|
|||
|
||||
struct PtrCloneVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
name: Symbol,
|
||||
id: HirId,
|
||||
replace: &'a [(&'static str, &'static str)],
|
||||
spans: Vec<(Span, Cow<'static, str>)>,
|
||||
abort: bool,
|
||||
|
@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|||
if self.abort {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
|
||||
if args.len() == 1 && match_var(&args[0], self.name) {
|
||||
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
|
||||
if path_to_local_id(recv, self.id) {
|
||||
if seg.ident.name.as_str() == "capacity" {
|
||||
self.abort = true;
|
||||
return;
|
||||
}
|
||||
for &(fn_name, suffix) in self.replace {
|
||||
if seg.ident.name.as_str() == fn_name {
|
||||
self.spans
|
||||
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
|
||||
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
|
||||
get_pat_name(arg.pat)
|
||||
}
|
||||
|
|
|
@ -90,8 +90,10 @@ cargo dev fmt
|
|||
cargo dev update_lints
|
||||
# create a new lint and register it
|
||||
cargo dev new_lint
|
||||
# automatically formatting all code before each commit
|
||||
cargo dev setup git-hook
|
||||
# (experimental) Setup Clippy to work with IntelliJ-Rust
|
||||
cargo dev ide_setup
|
||||
cargo dev setup intellij
|
||||
```
|
||||
|
||||
## lintcheck
|
||||
|
|
|
@ -6,7 +6,7 @@ You may need following tooltips to catch up with common operations.
|
|||
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
|
||||
- [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
|
||||
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
|
||||
- [Checking if a type defines a method](#checking-if-a-type-defines-a-method)
|
||||
- [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
|
||||
- [Dealing with macros](#dealing-with-macros)
|
||||
|
||||
Useful Rustc dev guide links:
|
||||
|
|
|
@ -101,6 +101,21 @@ After this, the release should be available on the Clippy [release page].
|
|||
|
||||
[release page]: https://github.com/rust-lang/rust-clippy/releases
|
||||
|
||||
## Update the `stable` branch
|
||||
|
||||
At this step you should have already checked out the commit of the `rust-1.XX.0`
|
||||
tag. Updating the stable branch from here is as easy as:
|
||||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Clippy repository and the
|
||||
# commit of the just created rust-1.XX.0 tag is checked out.
|
||||
$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote
|
||||
```
|
||||
|
||||
_NOTE: Usually there are no stable backports for Clippy, so this update should
|
||||
be possible without force pushing or anything like this. If there should have
|
||||
happened a stable backport, make sure to re-merge those changes just as with the
|
||||
`beta` branch._
|
||||
|
||||
## Update `CHANGELOG.md`
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ is checked.
|
|||
is explicitly specified in the options.
|
||||
|
||||
### Fix mode
|
||||
You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `-Zunstable-options --fix` and
|
||||
You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
|
||||
print a warning if Clippys suggestions fail to apply (if the resulting code does not build).
|
||||
This lets us spot bad suggestions or false positives automatically in some cases.
|
||||
|
||||
|
|
|
@ -260,14 +260,7 @@ impl Crate {
|
|||
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
|
||||
|
||||
let mut args = if fix {
|
||||
vec![
|
||||
"-Zunstable-options",
|
||||
"--fix",
|
||||
"-Zunstable-options",
|
||||
"--allow-no-vcs",
|
||||
"--",
|
||||
"--cap-lints=warn",
|
||||
]
|
||||
vec!["--fix", "--allow-no-vcs", "--", "--cap-lints=warn"]
|
||||
} else {
|
||||
vec!["--", "--message-format=json", "--", "--cap-lints=warn"]
|
||||
};
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2021-06-03"
|
||||
channel = "nightly-2021-07-01"
|
||||
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
|
||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -70,7 +70,6 @@ impl ClippyCmd {
|
|||
I: Iterator<Item = String>,
|
||||
{
|
||||
let mut cargo_subcommand = "check";
|
||||
let mut unstable_options = false;
|
||||
let mut args = vec![];
|
||||
|
||||
for arg in old_args.by_ref() {
|
||||
|
@ -80,18 +79,12 @@ impl ClippyCmd {
|
|||
continue;
|
||||
},
|
||||
"--" => break,
|
||||
// Cover -Zunstable-options and -Z unstable-options
|
||||
s if s.ends_with("unstable-options") => unstable_options = true,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
args.push(arg);
|
||||
}
|
||||
|
||||
if cargo_subcommand == "fix" && !unstable_options {
|
||||
panic!("Usage of `--fix` requires `-Z unstable-options`");
|
||||
}
|
||||
|
||||
let mut clippy_args: Vec<String> = old_args.collect();
|
||||
if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
|
||||
clippy_args.push("--no-deps".into());
|
||||
|
@ -176,34 +169,23 @@ mod tests {
|
|||
use super::ClippyCmd;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fix_without_unstable() {
|
||||
fn fix() {
|
||||
let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
|
||||
ClippyCmd::new(args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_unstable() {
|
||||
let args = "cargo clippy --fix -Zunstable-options"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
assert_eq!("fix", cmd.cargo_subcommand);
|
||||
assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
|
||||
assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_implies_no_deps() {
|
||||
let args = "cargo clippy --fix -Zunstable-options"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_deps_not_duplicated_with_fix() {
|
||||
let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
|
||||
let args = "cargo clippy --fix -- --no-deps"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
|
|
@ -48,7 +48,24 @@ fn third_party_crates() -> String {
|
|||
&& name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
|
||||
{
|
||||
if let Some(old) = crates.insert(dep, path.clone()) {
|
||||
panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
|
||||
// Check which action should be done in order to remove compiled deps.
|
||||
// If pre-installed version of compiler is used, `cargo clean` will do.
|
||||
// Otherwise (for bootstrapped compiler), the dependencies directory
|
||||
// must be removed manually.
|
||||
let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
|
||||
"remove the stageN-tools directory"
|
||||
} else {
|
||||
"run `cargo clean`"
|
||||
};
|
||||
|
||||
panic!(
|
||||
"\n---------------------------------------------------\n\n \
|
||||
Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \
|
||||
Probably, you need to {} before running tests again.\n \
|
||||
\nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \
|
||||
\n---------------------------------------------------\n",
|
||||
dep, old, path, suggested_action
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
10
tests/ui-toml/missing_enforced_import_rename/clippy.toml
Normal file
10
tests/ui-toml/missing_enforced_import_rename/clippy.toml
Normal 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" }
|
||||
]
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
6
tests/ui-toml/nonstandard_macro_braces/clippy.toml
Normal file
6
tests/ui-toml/nonstandard_macro_braces/clippy.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
standard-macro-braces = [
|
||||
{ name = "quote", brace = "{" },
|
||||
{ name = "quote::quote", brace = "{" },
|
||||
{ name = "eprint", brace = "[" },
|
||||
{ name = "type_pos", brace = "[" },
|
||||
]
|
|
@ -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");
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
]
|
||||
|
|
9
tests/ui-toml/toml_disallowed_type/clippy.toml
Normal file
9
tests/ui-toml/toml_disallowed_type/clippy.toml
Normal 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",
|
||||
]
|
35
tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
Normal file
35
tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
Normal 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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
55
tests/ui/append_instead_of_extend.fixed
Normal file
55
tests/ui/append_instead_of_extend.fixed
Normal 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
|
||||
}
|
55
tests/ui/append_instead_of_extend.rs
Normal file
55
tests/ui/append_instead_of_extend.rs
Normal 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
|
||||
}
|
22
tests/ui/append_instead_of_extend.stderr
Normal file
22
tests/ui/append_instead_of_extend.stderr
Normal 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
|
||||
|
|
@ -28,4 +28,7 @@ fn main() {
|
|||
debug_assert!(false); // #3948
|
||||
assert_const!(3);
|
||||
assert_const!(-1);
|
||||
|
||||
// Don't lint on this:
|
||||
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
|
||||
}
|
||||
|
|
|
@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_numeric_fallback {
|
||||
() => {
|
||||
let x = 22;
|
||||
};
|
||||
}
|
||||
|
|
8
tests/ui/auxiliary/non-exhaustive-enum.rs
Normal file
8
tests/ui/auxiliary/non-exhaustive-enum.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Stripped down version of the ErrorKind enum of std
|
||||
#[non_exhaustive]
|
||||
pub enum ErrorKind {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
#[doc(hidden)]
|
||||
Uncategorized,
|
||||
}
|
|
@ -43,3 +43,15 @@ fn issue_1647_ref_mut() {
|
|||
let ref mut baz = 0;
|
||||
if let Some(ref mut quux) = Some(42) {}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
fn issue_7305() {
|
||||
// `blackisted_name` lint should not be triggered inside of the test code.
|
||||
let foo = 0;
|
||||
|
||||
// Check that even in nested functions warning is still not triggere.
|
||||
fn nested() {
|
||||
let foo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// aux-build:macro_rules.rs
|
||||
|
||||
#![warn(clippy::default_numeric_fallback)]
|
||||
#![allow(unused)]
|
||||
#![allow(clippy::never_loop)]
|
||||
|
@ -5,6 +7,9 @@
|
|||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::branches_sharing_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
|
||||
mod basic_expr {
|
||||
fn test() {
|
||||
// Should lint unsuffixed literals typed `i32`.
|
||||
|
@ -133,4 +138,22 @@ 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() {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue