Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 95c369f

Browse files
committed
Add skip_while_next lint
1 parent f7b3e4f commit 95c369f

File tree

8 files changed

+105
-2
lines changed

8 files changed

+105
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,7 @@ Released 2018-09-13
12851285
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
12861286
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
12871287
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
1288+
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
12881289
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
12891290
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
12901291
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 347 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 348 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
645645
&methods::SEARCH_IS_SOME,
646646
&methods::SHOULD_IMPLEMENT_TRAIT,
647647
&methods::SINGLE_CHAR_PATTERN,
648+
&methods::SKIP_WHILE_NEXT,
648649
&methods::STRING_EXTEND_CHARS,
649650
&methods::SUSPICIOUS_MAP,
650651
&methods::TEMPORARY_CSTRING_AS_PTR,
@@ -1223,6 +1224,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12231224
LintId::of(&methods::SEARCH_IS_SOME),
12241225
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
12251226
LintId::of(&methods::SINGLE_CHAR_PATTERN),
1227+
LintId::of(&methods::SKIP_WHILE_NEXT),
12261228
LintId::of(&methods::STRING_EXTEND_CHARS),
12271229
LintId::of(&methods::SUSPICIOUS_MAP),
12281230
LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
@@ -1475,6 +1477,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14751477
LintId::of(&methods::FLAT_MAP_IDENTITY),
14761478
LintId::of(&methods::OPTION_AND_THEN_SOME),
14771479
LintId::of(&methods::SEARCH_IS_SOME),
1480+
LintId::of(&methods::SKIP_WHILE_NEXT),
14781481
LintId::of(&methods::SUSPICIOUS_MAP),
14791482
LintId::of(&methods::UNNECESSARY_FILTER_MAP),
14801483
LintId::of(&methods::USELESS_ASREF),

clippy_lints/src/methods/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,29 @@ declare_clippy_lint! {
375375
"using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
376376
}
377377

378+
declare_clippy_lint! {
379+
/// **What it does:** Checks for usage of `_.skip_while(condition).next()`.
380+
///
381+
/// **Why is this bad?** Readability, this can be written more concisely as
382+
/// `_.find(!condition)`.
383+
///
384+
/// **Known problems:** None.
385+
///
386+
/// **Example:**
387+
/// ```rust
388+
/// # let vec = vec![1];
389+
/// vec.iter().skip_while(|x| **x == 0).next();
390+
/// ```
391+
/// Could be written as
392+
/// ```rust
393+
/// # let vec = vec![1];
394+
/// vec.iter().find(|x| **x != 0);
395+
/// ```
396+
pub SKIP_WHILE_NEXT,
397+
complexity,
398+
"using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
399+
}
400+
378401
declare_clippy_lint! {
379402
/// **What it does:** Checks for usage of `_.map(_).flatten(_)`,
380403
///
@@ -1192,6 +1215,7 @@ declare_lint_pass!(Methods => [
11921215
SEARCH_IS_SOME,
11931216
TEMPORARY_CSTRING_AS_PTR,
11941217
FILTER_NEXT,
1218+
SKIP_WHILE_NEXT,
11951219
FILTER_MAP,
11961220
FILTER_MAP_NEXT,
11971221
FLAT_MAP_IDENTITY,
@@ -1237,6 +1261,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
12371261
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
12381262
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
12391263
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
1264+
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
12401265
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
12411266
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
12421267
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
@@ -2530,6 +2555,20 @@ fn lint_filter_next<'a, 'tcx>(
25302555
}
25312556
}
25322557

2558+
/// lint use of `skip_while().next()` for `Iterators`
2559+
fn lint_skip_while_next<'a, 'tcx>(
2560+
cx: &LateContext<'a, 'tcx>,
2561+
expr: &'tcx hir::Expr<'_>,
2562+
_skip_while_args: &'tcx [hir::Expr<'_>],
2563+
) {
2564+
// lint if caller of `.skip_while().next()` is an Iterator
2565+
if match_trait_method(cx, expr, &paths::ITERATOR) {
2566+
let msg = "called `skip_while(p).next()` on an `Iterator`. \
2567+
This is more succinctly expressed by calling `.find(!p)` instead.";
2568+
span_lint(cx, SKIP_WHILE_NEXT, expr.span, msg);
2569+
}
2570+
}
2571+
25332572
/// lint use of `filter().map()` for `Iterators`
25342573
fn lint_filter_map<'a, 'tcx>(
25352574
cx: &LateContext<'a, 'tcx>,

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 347] = [
9+
pub const ALL_LINTS: [Lint; 348] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1862,6 +1862,13 @@ pub const ALL_LINTS: [Lint; 347] = [
18621862
deprecation: None,
18631863
module: "matches",
18641864
},
1865+
Lint {
1866+
name: "skip_while_next",
1867+
group: "complexity",
1868+
desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`",
1869+
deprecation: None,
1870+
module: "methods",
1871+
},
18651872
Lint {
18661873
name: "slow_vector_initialization",
18671874
group: "perf",

tests/ui/auxiliary/option_helpers.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ impl IteratorFalsePositives {
4444
pub fn skip(self, _: usize) -> IteratorFalsePositives {
4545
self
4646
}
47+
48+
pub fn skip_while(self) -> IteratorFalsePositives {
49+
self
50+
}
4751
}

tests/ui/skip_while_next.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// aux-build:option_helpers.rs
2+
3+
#![warn(clippy::skip_while_next)]
4+
#![allow(clippy::blacklisted_name)]
5+
6+
extern crate option_helpers;
7+
use option_helpers::IteratorFalsePositives;
8+
9+
#[rustfmt::skip]
10+
fn skip_while_next() {
11+
let v = vec![3, 2, 1, 0, -1, -2, -3];
12+
13+
// Single-line case.
14+
let _ = v.iter().skip_while(|&x| *x < 0).next();
15+
16+
// Multi-line case.
17+
let _ = v.iter().skip_while(|&x| {
18+
*x < 0
19+
}
20+
).next();
21+
22+
// Check that hat we don't lint if the caller is not an `Iterator`.
23+
let foo = IteratorFalsePositives { foo: 0 };
24+
let _ = foo.skip_while().next();
25+
}
26+
27+
fn main() {
28+
skip_while_next();
29+
}

tests/ui/skip_while_next.stderr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: called `skip_while(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(!p)` instead.
2+
--> $DIR/skip_while_next.rs:14:13
3+
|
4+
LL | let _ = v.iter().skip_while(|&x| *x < 0).next();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::skip-while-next` implied by `-D warnings`
8+
9+
error: called `skip_while(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(!p)` instead.
10+
--> $DIR/skip_while_next.rs:17:13
11+
|
12+
LL | let _ = v.iter().skip_while(|&x| {
13+
| _____________^
14+
LL | | *x < 0
15+
LL | | }
16+
LL | | ).next();
17+
| |___________________________^
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)