Skip to content

Commit cbe7b39

Browse files
authored
Merge pull request #1022 from devonhollowood/extend-iter-nth
Extend iter nth
2 parents f3397af + 0e04153 commit cbe7b39

File tree

3 files changed

+59
-22
lines changed

3 files changed

+59
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ name
7878
[invalid_upcast_comparisons](https://github.com/Manishearth/rust-clippy/wiki#invalid_upcast_comparisons) | allow | a comparison involving an upcast which is always true or false
7979
[items_after_statements](https://github.com/Manishearth/rust-clippy/wiki#items_after_statements) | allow | finds blocks where an item comes after a statement
8080
[iter_next_loop](https://github.com/Manishearth/rust-clippy/wiki#iter_next_loop) | warn | for-looping over `_.next()` which is probably not intended
81-
[iter_nth](https://github.com/Manishearth/rust-clippy/wiki#iter_nth) | warn | using `.iter().nth()` on a slice or Vec
81+
[iter_nth](https://github.com/Manishearth/rust-clippy/wiki#iter_nth) | warn | using `.iter().nth()` on a standard library type with O(1) element access
8282
[len_without_is_empty](https://github.com/Manishearth/rust-clippy/wiki#len_without_is_empty) | warn | traits and impls that have `.len()` but not `.is_empty()`
8383
[len_zero](https://github.com/Manishearth/rust-clippy/wiki#len_zero) | warn | checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead
8484
[let_and_return](https://github.com/Manishearth/rust-clippy/wiki#let_and_return) | warn | creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block

clippy_lints/src/methods.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,10 @@ declare_lint! {
312312
"getting the inner pointer of a temporary `CString`"
313313
}
314314

315-
/// **What it does:** This lint checks for use of `.iter().nth()` on a slice or Vec.
315+
/// **What it does:** This lint checks for use of `.iter().nth()` (and the related
316+
/// `.iter_mut().nth()`) on standard library types with O(1) element access.
316317
///
317-
/// **Why is this bad?** `.get()` is more efficient and more readable.
318+
/// **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more readable.
318319
///
319320
/// **Known problems:** None.
320321
///
@@ -333,7 +334,7 @@ declare_lint! {
333334
declare_lint! {
334335
pub ITER_NTH,
335336
Warn,
336-
"using `.iter().nth()` on a slice or Vec"
337+
"using `.iter().nth()` on a standard library type with O(1) element access"
337338
}
338339

339340
impl LintPass for Pass {
@@ -389,7 +390,9 @@ impl LateLintPass for Pass {
389390
} else if let Some(arglists) = method_chain_args(expr, &["unwrap", "as_ptr"]) {
390391
lint_cstring_as_ptr(cx, expr, &arglists[0][0], &arglists[1][0]);
391392
} else if let Some(arglists) = method_chain_args(expr, &["iter", "nth"]) {
392-
lint_iter_nth(cx, expr, arglists[0]);
393+
lint_iter_nth(cx, expr, arglists[0], false);
394+
} else if let Some(arglists) = method_chain_args(expr, &["iter_mut", "nth"]) {
395+
lint_iter_nth(cx, expr, arglists[0], true);
393396
}
394397

395398
lint_or_fun_call(cx, expr, &name.node.as_str(), args);
@@ -645,21 +648,28 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
645648

646649
#[allow(ptr_arg)]
647650
// Type of MethodArgs is potentially a Vec
648-
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs){
649-
// lint if the caller of `.iter().nth()` is a `slice`
651+
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_mut: bool){
652+
let caller_type;
653+
let mut_str = if is_mut { "_mut" } else {""};
650654
if let Some(_) = derefs_to_slice(cx, &iter_args[0], &cx.tcx.expr_ty(&iter_args[0])) {
651-
span_lint(cx,
652-
ITER_NTH,
653-
expr.span,
654-
"called `.iter().nth()` on a slice. Calling `.get()` is both faster and more readable");
655+
caller_type = "slice";
655656
}
656-
// lint if the caller of `.iter().nth()` is a `Vec`
657657
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC) {
658-
span_lint(cx,
659-
ITER_NTH,
660-
expr.span,
661-
"called `.iter().nth()` on a Vec. Calling `.get()` is both faster and more readable");
658+
caller_type = "Vec";
659+
}
660+
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
661+
caller_type = "VecDeque";
662+
}
663+
else {
664+
return; // caller is not a type that we want to lint
662665
}
666+
span_lint(
667+
cx,
668+
ITER_NTH,
669+
expr.span,
670+
&format!("called `.iter{0}().nth()` on a {1}. Calling `.get{0}()` is both faster and more readable",
671+
mut_str, caller_type)
672+
);
663673
}
664674

665675
fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: &ty::Ty) -> Option<(Span, &'static str)> {

tests/compile-fail/methods.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use std::collections::BTreeMap;
99
use std::collections::HashMap;
1010
use std::collections::HashSet;
11+
use std::collections::VecDeque;
1112
use std::ops::Mul;
1213

1314
struct T;
@@ -136,6 +137,10 @@ impl HasIter {
136137
fn iter(self) -> IteratorFalsePositives {
137138
IteratorFalsePositives { foo: 0 }
138139
}
140+
141+
fn iter_mut(self) -> IteratorFalsePositives {
142+
IteratorFalsePositives { foo: 0 }
143+
}
139144
}
140145

141146
/// Struct to generate false positive for Iterator-based lints
@@ -325,15 +330,37 @@ fn or_fun_call() {
325330

326331
/// Checks implementation of `ITER_NTH` lint
327332
fn iter_nth() {
328-
let some_vec = vec![0, 1, 2, 3];
329-
let bad_vec = some_vec.iter().nth(3);
330-
//~^ERROR called `.iter().nth()` on a Vec.
331-
let bad_slice = &some_vec[..].iter().nth(3);
332-
//~^ERROR called `.iter().nth()` on a slice.
333+
let mut some_vec = vec![0, 1, 2, 3];
334+
let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect();
335+
336+
{
337+
// Make sure we lint `.iter()` for relevant types
338+
let bad_vec = some_vec.iter().nth(3);
339+
//~^ERROR called `.iter().nth()` on a Vec. Calling `.get()` is both faster and more readable
340+
let bad_slice = &some_vec[..].iter().nth(3);
341+
//~^ERROR called `.iter().nth()` on a slice. Calling `.get()` is both faster and more readable
342+
let bad_vec_deque = some_vec_deque.iter().nth(3);
343+
//~^ERROR called `.iter().nth()` on a VecDeque. Calling `.get()` is both faster and more readable
344+
}
345+
346+
{
347+
// Make sure we lint `.iter_mut()` for relevant types
348+
let bad_vec = some_vec.iter_mut().nth(3);
349+
//~^ERROR called `.iter_mut().nth()` on a Vec. Calling `.get_mut()` is both faster and more readable
350+
}
351+
{
352+
let bad_slice = &some_vec[..].iter_mut().nth(3);
353+
//~^ERROR called `.iter_mut().nth()` on a slice. Calling `.get_mut()` is both faster and more readable
354+
}
355+
{
356+
let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
357+
//~^ERROR called `.iter_mut().nth()` on a VecDeque. Calling `.get_mut()` is both faster and more readable
358+
}
333359

360+
// Make sure we don't lint for non-relevant types
334361
let false_positive = HasIter;
335362
let ok = false_positive.iter().nth(3);
336-
// ^This should be okay, because false_positive is not a slice or Vec
363+
let ok_mut = false_positive.iter_mut().nth(3);
337364
}
338365

339366
#[allow(similar_names)]

0 commit comments

Comments
 (0)