Skip to content

Commit d9d2013

Browse files
committed
Auto merge of #4966 - bradsherman:iter-nth-zero, r=flip1995
New Lint: Iter nth zero Check for the use of `iter.nth(0)` and encourage `iter.next()` instead as it is more readable changelog: add new lint when `iter.nth(0)` is used Fixes #4957
2 parents 05b4603 + ab5ff03 commit d9d2013

File tree

9 files changed

+154
-8
lines changed

9 files changed

+154
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,7 @@ Released 2018-09-13
11411141
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
11421142
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
11431143
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
1144+
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
11441145
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
11451146
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
11461147
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits

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 343 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 344 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/inherent_impl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl {
6161
}
6262

6363
fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx Crate<'_>) {
64-
if let Some(item) = krate.items.values().nth(0) {
64+
if let Some(item) = krate.items.values().next() {
6565
// Retrieve all inherent implementations from the crate, grouped by type
6666
for impls in cx
6767
.tcx
@@ -71,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl {
7171
{
7272
// Filter out implementations that have generic params (type or lifetime)
7373
let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def));
74-
if let Some(initial_span) = impl_spans.nth(0) {
74+
if let Some(initial_span) = impl_spans.next() {
7575
impl_spans.for_each(|additional_span| {
7676
span_lint_and_then(
7777
cx,

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
618618
&methods::ITERATOR_STEP_BY_ZERO,
619619
&methods::ITER_CLONED_COLLECT,
620620
&methods::ITER_NTH,
621+
&methods::ITER_NTH_ZERO,
621622
&methods::ITER_SKIP_NEXT,
622623
&methods::MANUAL_SATURATING_ARITHMETIC,
623624
&methods::MAP_FLATTEN,
@@ -1197,6 +1198,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
11971198
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
11981199
LintId::of(&methods::ITER_CLONED_COLLECT),
11991200
LintId::of(&methods::ITER_NTH),
1201+
LintId::of(&methods::ITER_NTH_ZERO),
12001202
LintId::of(&methods::ITER_SKIP_NEXT),
12011203
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
12021204
LintId::of(&methods::NEW_RET_NO_SELF),
@@ -1375,6 +1377,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13751377
LintId::of(&methods::CHARS_LAST_CMP),
13761378
LintId::of(&methods::INTO_ITER_ON_REF),
13771379
LintId::of(&methods::ITER_CLONED_COLLECT),
1380+
LintId::of(&methods::ITER_NTH_ZERO),
13781381
LintId::of(&methods::ITER_SKIP_NEXT),
13791382
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
13801383
LintId::of(&methods::NEW_RET_NO_SELF),

clippy_lints/src/methods/mod.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_span::source_map::Span;
2020
use rustc_span::symbol::{sym, Symbol, SymbolStr};
2121
use syntax::ast;
2222

23+
use crate::consts::{constant, Constant};
2324
use crate::utils::usage::mutated_variables;
2425
use crate::utils::{
2526
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
@@ -756,6 +757,33 @@ declare_clippy_lint! {
756757
"using `Iterator::step_by(0)`, which will panic at runtime"
757758
}
758759

760+
declare_clippy_lint! {
761+
/// **What it does:** Checks for the use of `iter.nth(0)`.
762+
///
763+
/// **Why is this bad?** `iter.nth(0)` is unnecessary, and `iter.next()`
764+
/// is more readable.
765+
///
766+
/// **Known problems:** None.
767+
///
768+
/// **Example:**
769+
///
770+
/// ```rust
771+
/// # use std::collections::HashSet;
772+
/// // Bad
773+
/// # let mut s = HashSet::new();
774+
/// # s.insert(1);
775+
/// let x = s.iter().nth(0);
776+
///
777+
/// // Good
778+
/// # let mut s = HashSet::new();
779+
/// # s.insert(1);
780+
/// let x = s.iter().next();
781+
/// ```
782+
pub ITER_NTH_ZERO,
783+
style,
784+
"replace `iter.nth(0)` with `iter.next()`"
785+
}
786+
759787
declare_clippy_lint! {
760788
/// **What it does:** Checks for use of `.iter().nth()` (and the related
761789
/// `.iter_mut().nth()`) on standard library types with O(1) element access.
@@ -1136,6 +1164,7 @@ declare_lint_pass!(Methods => [
11361164
MAP_FLATTEN,
11371165
ITERATOR_STEP_BY_ZERO,
11381166
ITER_NTH,
1167+
ITER_NTH_ZERO,
11391168
ITER_SKIP_NEXT,
11401169
GET_UNWRAP,
11411170
STRING_EXTEND_CHARS,
@@ -1191,8 +1220,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
11911220
["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
11921221
lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
11931222
},
1194-
["nth", "iter"] => lint_iter_nth(cx, expr, arg_lists[1], false),
1195-
["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true),
1223+
["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
1224+
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
1225+
["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
11961226
["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
11971227
["next", "skip"] => lint_iter_skip_next(cx, expr),
11981228
["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
@@ -1983,7 +2013,6 @@ fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, fold_ar
19832013

19842014
fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
19852015
if match_trait_method(cx, expr, &paths::ITERATOR) {
1986-
use crate::consts::{constant, Constant};
19872016
if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &args[1]) {
19882017
span_lint(
19892018
cx,
@@ -1998,9 +2027,10 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args
19982027
fn lint_iter_nth<'a, 'tcx>(
19992028
cx: &LateContext<'a, 'tcx>,
20002029
expr: &hir::Expr<'_>,
2001-
iter_args: &'tcx [hir::Expr<'_>],
2030+
nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]],
20022031
is_mut: bool,
20032032
) {
2033+
let iter_args = nth_and_iter_args[1];
20042034
let mut_str = if is_mut { "_mut" } else { "" };
20052035
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
20062036
"slice"
@@ -2009,6 +2039,8 @@ fn lint_iter_nth<'a, 'tcx>(
20092039
} else if match_type(cx, cx.tables.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
20102040
"VecDeque"
20112041
} else {
2042+
let nth_args = nth_and_iter_args[0];
2043+
lint_iter_nth_zero(cx, expr, &nth_args);
20122044
return; // caller is not a type that we want to lint
20132045
};
20142046

@@ -2023,6 +2055,25 @@ fn lint_iter_nth<'a, 'tcx>(
20232055
);
20242056
}
20252057

2058+
fn lint_iter_nth_zero<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
2059+
if_chain! {
2060+
if match_trait_method(cx, expr, &paths::ITERATOR);
2061+
if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &nth_args[1]);
2062+
then {
2063+
let mut applicability = Applicability::MachineApplicable;
2064+
span_lint_and_sugg(
2065+
cx,
2066+
ITER_NTH_ZERO,
2067+
expr.span,
2068+
"called `.nth(0)` on a `std::iter::Iterator`",
2069+
"try calling",
2070+
format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)),
2071+
applicability,
2072+
);
2073+
}
2074+
}
2075+
}
2076+
20262077
fn lint_get_unwrap<'a, 'tcx>(
20272078
cx: &LateContext<'a, 'tcx>,
20282079
expr: &hir::Expr<'_>,

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; 343] = [
9+
pub const ALL_LINTS: [Lint; 344] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -875,6 +875,13 @@ pub const ALL_LINTS: [Lint; 343] = [
875875
deprecation: None,
876876
module: "methods",
877877
},
878+
Lint {
879+
name: "iter_nth_zero",
880+
group: "style",
881+
desc: "replace `iter.nth(0)` with `iter.next()`",
882+
deprecation: None,
883+
module: "methods",
884+
},
878885
Lint {
879886
name: "iter_skip_next",
880887
group: "style",

tests/ui/iter_nth_zero.fixed

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::iter_nth_zero)]
4+
use std::collections::HashSet;
5+
6+
struct Foo {}
7+
8+
impl Foo {
9+
fn nth(&self, index: usize) -> usize {
10+
index + 1
11+
}
12+
}
13+
14+
fn main() {
15+
let f = Foo {};
16+
f.nth(0); // lint does not apply here
17+
18+
let mut s = HashSet::new();
19+
s.insert(1);
20+
let _x = s.iter().next();
21+
22+
let mut s2 = HashSet::new();
23+
s2.insert(2);
24+
let mut iter = s2.iter();
25+
let _y = iter.next();
26+
27+
let mut s3 = HashSet::new();
28+
s3.insert(3);
29+
let mut iter2 = s3.iter();
30+
let _unwrapped = iter2.next().unwrap();
31+
}

tests/ui/iter_nth_zero.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::iter_nth_zero)]
4+
use std::collections::HashSet;
5+
6+
struct Foo {}
7+
8+
impl Foo {
9+
fn nth(&self, index: usize) -> usize {
10+
index + 1
11+
}
12+
}
13+
14+
fn main() {
15+
let f = Foo {};
16+
f.nth(0); // lint does not apply here
17+
18+
let mut s = HashSet::new();
19+
s.insert(1);
20+
let _x = s.iter().nth(0);
21+
22+
let mut s2 = HashSet::new();
23+
s2.insert(2);
24+
let mut iter = s2.iter();
25+
let _y = iter.nth(0);
26+
27+
let mut s3 = HashSet::new();
28+
s3.insert(3);
29+
let mut iter2 = s3.iter();
30+
let _unwrapped = iter2.nth(0).unwrap();
31+
}

tests/ui/iter_nth_zero.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: called `.nth(0)` on a `std::iter::Iterator`
2+
--> $DIR/iter_nth_zero.rs:20:14
3+
|
4+
LL | let _x = s.iter().nth(0);
5+
| ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()`
6+
|
7+
= note: `-D clippy::iter-nth-zero` implied by `-D warnings`
8+
9+
error: called `.nth(0)` on a `std::iter::Iterator`
10+
--> $DIR/iter_nth_zero.rs:25:14
11+
|
12+
LL | let _y = iter.nth(0);
13+
| ^^^^^^^^^^^ help: try calling: `iter.next()`
14+
15+
error: called `.nth(0)` on a `std::iter::Iterator`
16+
--> $DIR/iter_nth_zero.rs:30:22
17+
|
18+
LL | let _unwrapped = iter2.nth(0).unwrap();
19+
| ^^^^^^^^^^^^ help: try calling: `iter2.next()`
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)