Add `skip_while_next` lint
This commit is contained in:
parent
f7b3e4f29c
commit
95c369fa91
|
@ -1285,6 +1285,7 @@ Released 2018-09-13
|
|||
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
|
||||
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
|
||||
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
|
||||
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
||||
|
||||
[There are 347 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
[There are 348 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
|
||||
|
||||
|
|
|
@ -645,6 +645,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&methods::SEARCH_IS_SOME,
|
||||
&methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
&methods::SINGLE_CHAR_PATTERN,
|
||||
&methods::SKIP_WHILE_NEXT,
|
||||
&methods::STRING_EXTEND_CHARS,
|
||||
&methods::SUSPICIOUS_MAP,
|
||||
&methods::TEMPORARY_CSTRING_AS_PTR,
|
||||
|
@ -1223,6 +1224,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&methods::SEARCH_IS_SOME),
|
||||
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
LintId::of(&methods::SINGLE_CHAR_PATTERN),
|
||||
LintId::of(&methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(&methods::STRING_EXTEND_CHARS),
|
||||
LintId::of(&methods::SUSPICIOUS_MAP),
|
||||
LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
|
||||
|
@ -1475,6 +1477,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(&methods::OPTION_AND_THEN_SOME),
|
||||
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),
|
||||
|
|
|
@ -375,6 +375,29 @@ declare_clippy_lint! {
|
|||
"using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.skip_while(condition).next()`.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as
|
||||
/// `_.find(!condition)`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().skip_while(|x| **x == 0).next();
|
||||
/// ```
|
||||
/// Could be written as
|
||||
/// ```rust
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().find(|x| **x != 0);
|
||||
/// ```
|
||||
pub SKIP_WHILE_NEXT,
|
||||
complexity,
|
||||
"using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.map(_).flatten(_)`,
|
||||
///
|
||||
|
@ -1192,6 +1215,7 @@ declare_lint_pass!(Methods => [
|
|||
SEARCH_IS_SOME,
|
||||
TEMPORARY_CSTRING_AS_PTR,
|
||||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
FILTER_MAP,
|
||||
FILTER_MAP_NEXT,
|
||||
FLAT_MAP_IDENTITY,
|
||||
|
@ -1237,6 +1261,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
|||
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
|
||||
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
|
||||
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
|
||||
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
|
||||
|
@ -2530,6 +2555,20 @@ fn lint_filter_next<'a, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// lint use of `skip_while().next()` for `Iterators`
|
||||
fn lint_skip_while_next<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
_skip_while_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.skip_while().next()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `skip_while(p).next()` on an `Iterator`. \
|
||||
This is more succinctly expressed by calling `.find(!p)` instead.";
|
||||
span_lint(cx, SKIP_WHILE_NEXT, expr.span, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter().map()` for `Iterators`
|
||||
fn lint_filter_map<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
|
|
|
@ -6,7 +6,7 @@ pub use lint::Lint;
|
|||
pub use lint::LINT_LEVELS;
|
||||
|
||||
// begin lint list, do not remove this comment, it’s used in `update_lints`
|
||||
pub const ALL_LINTS: [Lint; 347] = [
|
||||
pub const ALL_LINTS: [Lint; 348] = [
|
||||
Lint {
|
||||
name: "absurd_extreme_comparisons",
|
||||
group: "correctness",
|
||||
|
@ -1862,6 +1862,13 @@ pub const ALL_LINTS: [Lint; 347] = [
|
|||
deprecation: None,
|
||||
module: "matches",
|
||||
},
|
||||
Lint {
|
||||
name: "skip_while_next",
|
||||
group: "complexity",
|
||||
desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
Lint {
|
||||
name: "slow_vector_initialization",
|
||||
group: "perf",
|
||||
|
|
|
@ -44,4 +44,8 @@ impl IteratorFalsePositives {
|
|||
pub fn skip(self, _: usize) -> IteratorFalsePositives {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn skip_while(self) -> IteratorFalsePositives {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// aux-build:option_helpers.rs
|
||||
|
||||
#![warn(clippy::skip_while_next)]
|
||||
#![allow(clippy::blacklisted_name)]
|
||||
|
||||
extern crate option_helpers;
|
||||
use option_helpers::IteratorFalsePositives;
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn skip_while_next() {
|
||||
let v = vec![3, 2, 1, 0, -1, -2, -3];
|
||||
|
||||
// Single-line case.
|
||||
let _ = v.iter().skip_while(|&x| *x < 0).next();
|
||||
|
||||
// Multi-line case.
|
||||
let _ = v.iter().skip_while(|&x| {
|
||||
*x < 0
|
||||
}
|
||||
).next();
|
||||
|
||||
// Check that hat we don't lint if the caller is not an `Iterator`.
|
||||
let foo = IteratorFalsePositives { foo: 0 };
|
||||
let _ = foo.skip_while().next();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
skip_while_next();
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
error: called `skip_while(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(!p)` instead.
|
||||
--> $DIR/skip_while_next.rs:14:13
|
||||
|
|
||||
LL | let _ = v.iter().skip_while(|&x| *x < 0).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::skip-while-next` implied by `-D warnings`
|
||||
|
||||
error: called `skip_while(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(!p)` instead.
|
||||
--> $DIR/skip_while_next.rs:17:13
|
||||
|
|
||||
LL | let _ = v.iter().skip_while(|&x| {
|
||||
| _____________^
|
||||
LL | | *x < 0
|
||||
LL | | }
|
||||
LL | | ).next();
|
||||
| |___________________________^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue