Skip to content

Commit eca0d8e

Browse files
committed
Auto merge of #5067 - JohnTitor:lint-skip-while-next, r=flip1995
Add `skip_while_next` lint Fixes #4036 changelog: Add `skip_while_next` lint
2 parents 32949da + bec5b69 commit eca0d8e

File tree

9 files changed

+117
-7
lines changed

9 files changed

+117
-7
lines changed

CHANGELOG.md

+1
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

+1-1
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

+3
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

+43
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
///
@@ -1193,6 +1216,7 @@ declare_lint_pass!(Methods => [
11931216
SEARCH_IS_SOME,
11941217
TEMPORARY_CSTRING_AS_PTR,
11951218
FILTER_NEXT,
1219+
SKIP_WHILE_NEXT,
11961220
FILTER_MAP,
11971221
FILTER_MAP_NEXT,
11981222
FLAT_MAP_IDENTITY,
@@ -1238,6 +1262,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
12381262
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
12391263
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
12401264
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
1265+
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
12411266
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
12421267
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
12431268
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
@@ -2531,6 +2556,24 @@ fn lint_filter_next<'a, 'tcx>(
25312556
}
25322557
}
25332558

2559+
/// lint use of `skip_while().next()` for `Iterators`
2560+
fn lint_skip_while_next<'a, 'tcx>(
2561+
cx: &LateContext<'a, 'tcx>,
2562+
expr: &'tcx hir::Expr<'_>,
2563+
_skip_while_args: &'tcx [hir::Expr<'_>],
2564+
) {
2565+
// lint if caller of `.skip_while().next()` is an Iterator
2566+
if match_trait_method(cx, expr, &paths::ITERATOR) {
2567+
span_help_and_lint(
2568+
cx,
2569+
SKIP_WHILE_NEXT,
2570+
expr.span,
2571+
"called `skip_while(p).next()` on an `Iterator`",
2572+
"this is more succinctly expressed by calling `.find(!p)` instead",
2573+
);
2574+
}
2575+
}
2576+
25342577
/// lint use of `filter().map()` for `Iterators`
25352578
fn lint_filter_map<'a, 'tcx>(
25362579
cx: &LateContext<'a, 'tcx>,

doc/adding_lints.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ we want to check. The output of Clippy is compared against a `.stderr` file.
5555
Note that you don't have to create this file yourself, we'll get to
5656
generating the `.stderr` files further down.
5757

58-
We start by opening the test file created at `tests/ui/foo_functions.rs`.
58+
We start by opening the test file created at `tests/ui/foo_functions.rs`.
5959

6060
Update the file with some examples to get started:
6161

@@ -102,7 +102,7 @@ Once we are satisfied with the output, we need to run
102102
`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
103103
Please note that, we should run `TESTNAME=foo_functions cargo uitest`
104104
every time before running `tests/ui/update-all-references.sh`.
105-
Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
105+
Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
106106
our lint, we need to commit the generated `.stderr` files, too.
107107

108108
### Rustfix tests
@@ -133,7 +133,7 @@ With tests in place, let's have a look at implementing our lint now.
133133

134134
### Lint declaration
135135

136-
Let's start by opening the new file created in the `clippy_lints` crate
136+
Let's start by opening the new file created in the `clippy_lints` crate
137137
at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
138138
lint code is. This file has already imported some initial things we will need:
139139

@@ -178,7 +178,7 @@ state the thing that is being checked for and read well when used with
178178
* The last part should be a text that explains what exactly is wrong with the
179179
code
180180

181-
The rest of this file contains an empty implementation for our lint pass,
181+
The rest of this file contains an empty implementation for our lint pass,
182182
which in this case is `EarlyLintPass` and should look like this:
183183

184184
```rust
@@ -194,7 +194,7 @@ impl EarlyLintPass for FooFunctions {}
194194
Don't worry about the `name` method here. As long as it includes the name of the
195195
lint pass it should be fine.
196196

197-
The new lint automation runs `update_lints`, which automates some things, but it
197+
The new lint automation runs `update_lints`, which automates some things, but it
198198
doesn't automate everything. We will have to register our lint pass manually in
199199
the `register_plugins` function in `clippy_lints/src/lib.rs`:
200200

src/lintlist/mod.rs

+8-1
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

+4
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

+29
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

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: called `skip_while(p).next()` on an `Iterator`
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+
= help: this is more succinctly expressed by calling `.find(!p)` instead
9+
10+
error: called `skip_while(p).next()` on an `Iterator`
11+
--> $DIR/skip_while_next.rs:17:13
12+
|
13+
LL | let _ = v.iter().skip_while(|&x| {
14+
| _____________^
15+
LL | | *x < 0
16+
LL | | }
17+
LL | | ).next();
18+
| |___________________________^
19+
|
20+
= help: this is more succinctly expressed by calling `.find(!p)` instead
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)