Skip to content

Commit 2604a06

Browse files
committed
new lint iter_skip_zero
1 parent ea4ca22 commit 2604a06

File tree

8 files changed

+156
-2
lines changed

8 files changed

+156
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4902,6 +4902,7 @@ Released 2018-09-13
49024902
[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
49034903
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
49044904
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
4905+
[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
49054906
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
49064907
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
49074908
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits

clippy_lints/src/casts/unnecessary_cast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
257257
// Will this work for more complex types? Probably not!
258258
if !snippet
259259
.split("->")
260-
.skip(0)
260+
.skip(1)
261261
.map(|s| {
262262
s.trim() == cast_from.to_string()
263263
|| s.split("where").any(|ty| ty.trim() == cast_from.to_string())

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
356356
crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
357357
crate::methods::ITER_OVEREAGER_CLONED_INFO,
358358
crate::methods::ITER_SKIP_NEXT_INFO,
359+
crate::methods::ITER_SKIP_ZERO_INFO,
359360
crate::methods::ITER_WITH_DRAIN_INFO,
360361
crate::methods::MANUAL_FILTER_MAP_INFO,
361362
crate::methods::MANUAL_FIND_MAP_INFO,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use clippy_utils::{
2+
consts::{constant, Constant},
3+
diagnostics::span_lint_and_sugg,
4+
is_from_proc_macro, is_trait_method,
5+
};
6+
use rustc_errors::Applicability;
7+
use rustc_hir::Expr;
8+
use rustc_lint::LateContext;
9+
use rustc_span::sym;
10+
11+
use super::ITER_SKIP_ZERO;
12+
13+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) {
14+
if !expr.span.from_expansion()
15+
&& is_trait_method(cx, expr, sym::Iterator)
16+
&& let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| {
17+
if let Constant::Int(arg) = constant {
18+
Some(arg)
19+
} else {
20+
None
21+
}
22+
})
23+
&& arg == 0
24+
&& !is_from_proc_macro(cx, expr)
25+
{
26+
span_lint_and_sugg(
27+
cx,
28+
ITER_SKIP_ZERO,
29+
arg_expr.span,
30+
"usage of `.skip(0)`",
31+
"if you meant to skip the first element, use",
32+
"1".to_owned(),
33+
Applicability::MaybeIncorrect,
34+
);
35+
}
36+
}

clippy_lints/src/methods/mod.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod iter_nth_zero;
4444
mod iter_on_single_or_empty_collections;
4545
mod iter_overeager_cloned;
4646
mod iter_skip_next;
47+
mod iter_skip_zero;
4748
mod iter_with_drain;
4849
mod iterator_step_by_zero;
4950
mod manual_next_back;
@@ -3316,6 +3317,27 @@ declare_clippy_lint! {
33163317
"checks for usage of `Iterator::fold` with a type that implements `Try`"
33173318
}
33183319

3320+
declare_clippy_lint! {
3321+
/// ### What it does
3322+
/// Checks for usage of `.skip(0)` on iterators.
3323+
///
3324+
/// ### Why is this bad?
3325+
/// This was likely intended to be `.skip(1)` to skip the first element, as `.skip(0)` does
3326+
/// nothing.
3327+
///
3328+
/// ### Example
3329+
/// ```rust
3330+
/// let v = vec![1, 2, 3];
3331+
/// let x = v.iter().skip(0).collect::<Vec<_>>();
3332+
/// let y = v.iter().collect::<Vec<_>>();
3333+
/// assert_eq!(x, y);
3334+
/// ```
3335+
#[clippy::version = "1.72.0"]
3336+
pub ITER_SKIP_ZERO,
3337+
correctness,
3338+
"disallows `.skip(0)`"
3339+
}
3340+
33193341
pub struct Methods {
33203342
avoid_breaking_exported_api: bool,
33213343
msrv: Msrv,
@@ -3448,6 +3470,7 @@ impl_lint_pass!(Methods => [
34483470
UNNECESSARY_LITERAL_UNWRAP,
34493471
DRAIN_COLLECT,
34503472
MANUAL_TRY_FOLD,
3473+
ITER_SKIP_ZERO,
34513474
]);
34523475

34533476
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3769,7 +3792,16 @@ impl Methods {
37693792
unnecessary_join::check(cx, expr, recv, join_arg, span);
37703793
}
37713794
},
3772-
("last", []) | ("skip", [_]) => {
3795+
("skip", [arg]) => {
3796+
iter_skip_zero::check(cx, expr, arg);
3797+
3798+
if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
3799+
if let ("cloned", []) = (name2, args2) {
3800+
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
3801+
}
3802+
}
3803+
}
3804+
("last", []) => {
37733805
if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
37743806
if let ("cloned", []) = (name2, args2) {
37753807
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);

tests/ui/iter_skip_zero.fixed

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![allow(clippy::useless_vec, unused)]
4+
#![warn(clippy::iter_skip_zero)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
use std::iter::once;
10+
11+
fn main() {
12+
let _ = [1, 2, 3].iter().skip(1);
13+
let _ = vec![1, 2, 3].iter().skip(1);
14+
let _ = once([1, 2, 3]).skip(1);
15+
let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(1)).skip(1);
16+
// Don't lint
17+
let _ = [1, 2, 3].iter().skip(1);
18+
let _ = vec![1, 2, 3].iter().skip(1);
19+
external! {
20+
let _ = [1, 2, 3].iter().skip(0);
21+
}
22+
with_span! {
23+
let _ = [1, 2, 3].iter().skip(0);
24+
}
25+
}

tests/ui/iter_skip_zero.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![allow(clippy::useless_vec, unused)]
4+
#![warn(clippy::iter_skip_zero)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
use std::iter::once;
10+
11+
fn main() {
12+
let _ = [1, 2, 3].iter().skip(0);
13+
let _ = vec![1, 2, 3].iter().skip(0);
14+
let _ = once([1, 2, 3]).skip(0);
15+
let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0);
16+
// Don't lint
17+
let _ = [1, 2, 3].iter().skip(1);
18+
let _ = vec![1, 2, 3].iter().skip(1);
19+
external! {
20+
let _ = [1, 2, 3].iter().skip(0);
21+
}
22+
with_span! {
23+
let _ = [1, 2, 3].iter().skip(0);
24+
}
25+
}

tests/ui/iter_skip_zero.stderr

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: usage of `.skip(0)`
2+
--> $DIR/iter_skip_zero.rs:12:35
3+
|
4+
LL | let _ = [1, 2, 3].iter().skip(0);
5+
| ^ help: if you meant to skip the first element, use: `1`
6+
|
7+
= note: `-D clippy::iter-skip-zero` implied by `-D warnings`
8+
9+
error: usage of `.skip(0)`
10+
--> $DIR/iter_skip_zero.rs:13:39
11+
|
12+
LL | let _ = vec![1, 2, 3].iter().skip(0);
13+
| ^ help: if you meant to skip the first element, use: `1`
14+
15+
error: usage of `.skip(0)`
16+
--> $DIR/iter_skip_zero.rs:14:34
17+
|
18+
LL | let _ = once([1, 2, 3]).skip(0);
19+
| ^ help: if you meant to skip the first element, use: `1`
20+
21+
error: usage of `.skip(0)`
22+
--> $DIR/iter_skip_zero.rs:15:71
23+
|
24+
LL | let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0);
25+
| ^ help: if you meant to skip the first element, use: `1`
26+
27+
error: usage of `.skip(0)`
28+
--> $DIR/iter_skip_zero.rs:15:62
29+
|
30+
LL | let _ = vec![1, 2, 3].iter().chain([1, 2, 3].iter().skip(0)).skip(0);
31+
| ^ help: if you meant to skip the first element, use: `1`
32+
33+
error: aborting due to 5 previous errors
34+

0 commit comments

Comments
 (0)