Skip to content

Commit eb04beb

Browse files
committed
Auto merge of #6791 - TaKO8Ki:iter-count, r=matthiaskrgr
New lint: `iter_count` This pull request adds a new lint named `iter_count`. --- closes #6262 changelog: new lint `iter_count`
2 parents 3cd6ca0 + 6041365 commit eb04beb

File tree

10 files changed

+414
-2
lines changed

10 files changed

+414
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,7 @@ Released 2018-09-13
21352135
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
21362136
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
21372137
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
2138+
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
21382139
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
21392140
[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
21402141
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
775775
&methods::INTO_ITER_ON_REF,
776776
&methods::ITERATOR_STEP_BY_ZERO,
777777
&methods::ITER_CLONED_COLLECT,
778+
&methods::ITER_COUNT,
778779
&methods::ITER_NEXT_SLICE,
779780
&methods::ITER_NTH,
780781
&methods::ITER_NTH_ZERO,
@@ -1577,6 +1578,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15771578
LintId::of(&methods::INTO_ITER_ON_REF),
15781579
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
15791580
LintId::of(&methods::ITER_CLONED_COLLECT),
1581+
LintId::of(&methods::ITER_COUNT),
15801582
LintId::of(&methods::ITER_NEXT_SLICE),
15811583
LintId::of(&methods::ITER_NTH),
15821584
LintId::of(&methods::ITER_NTH_ZERO),
@@ -1881,6 +1883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
18811883
LintId::of(&methods::FILTER_NEXT),
18821884
LintId::of(&methods::FLAT_MAP_IDENTITY),
18831885
LintId::of(&methods::INSPECT_FOR_EACH),
1886+
LintId::of(&methods::ITER_COUNT),
18841887
LintId::of(&methods::MANUAL_FILTER_MAP),
18851888
LintId::of(&methods::MANUAL_FIND_MAP),
18861889
LintId::of(&methods::OPTION_AS_REF_DEREF),
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use crate::methods::derefs_to_slice;
2+
use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg};
3+
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_span::sym;
8+
9+
use super::ITER_COUNT;
10+
11+
pub(crate) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], iter_method: &str) {
12+
let ty = cx.typeck_results().expr_ty(&iter_args[0]);
13+
let caller_type = if derefs_to_slice(cx, &iter_args[0], ty).is_some() {
14+
"slice"
15+
} else if is_type_diagnostic_item(cx, ty, sym::vec_type) {
16+
"Vec"
17+
} else if is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) {
18+
"VecDeque"
19+
} else if is_type_diagnostic_item(cx, ty, sym!(hashset_type)) {
20+
"HashSet"
21+
} else if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
22+
"HashMap"
23+
} else if match_type(cx, ty, &paths::BTREEMAP) {
24+
"BTreeMap"
25+
} else if match_type(cx, ty, &paths::BTREESET) {
26+
"BTreeSet"
27+
} else if match_type(cx, ty, &paths::LINKED_LIST) {
28+
"LinkedList"
29+
} else if match_type(cx, ty, &paths::BINARY_HEAP) {
30+
"BinaryHeap"
31+
} else {
32+
return;
33+
};
34+
let mut applicability = Applicability::MachineApplicable;
35+
span_lint_and_sugg(
36+
cx,
37+
ITER_COUNT,
38+
expr.span,
39+
&format!("called `.{}().count()` on a `{}`", iter_method, caller_type),
40+
"try",
41+
format!(
42+
"{}.len()",
43+
snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability),
44+
),
45+
applicability,
46+
);
47+
}

clippy_lints/src/methods/mod.rs

+31
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod filter_map_identity;
44
mod implicit_clone;
55
mod inefficient_to_string;
66
mod inspect_for_each;
7+
mod iter_count;
78
mod manual_saturating_arithmetic;
89
mod option_map_unwrap_or;
910
mod unnecessary_filter_map;
@@ -1540,6 +1541,32 @@ declare_clippy_lint! {
15401541
"implicitly cloning a value by invoking a function on its dereferenced type"
15411542
}
15421543

1544+
declare_clippy_lint! {
1545+
/// **What it does:** Checks for the use of `.iter().count()`.
1546+
///
1547+
/// **Why is this bad?** `.len()` is more efficient and more
1548+
/// readable.
1549+
///
1550+
/// **Known problems:** None.
1551+
///
1552+
/// **Example:**
1553+
///
1554+
/// ```rust
1555+
/// // Bad
1556+
/// let some_vec = vec![0, 1, 2, 3];
1557+
/// let _ = some_vec.iter().count();
1558+
/// let _ = &some_vec[..].iter().count();
1559+
///
1560+
/// // Good
1561+
/// let some_vec = vec![0, 1, 2, 3];
1562+
/// let _ = some_vec.len();
1563+
/// let _ = &some_vec[..].len();
1564+
/// ```
1565+
pub ITER_COUNT,
1566+
complexity,
1567+
"replace `.iter().count()` with `.len()`"
1568+
}
1569+
15431570
pub struct Methods {
15441571
msrv: Option<RustcVersion>,
15451572
}
@@ -1585,6 +1612,7 @@ impl_lint_pass!(Methods => [
15851612
MAP_FLATTEN,
15861613
ITERATOR_STEP_BY_ZERO,
15871614
ITER_NEXT_SLICE,
1615+
ITER_COUNT,
15881616
ITER_NTH,
15891617
ITER_NTH_ZERO,
15901618
BYTES_NTH,
@@ -1664,6 +1692,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
16641692
lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
16651693
},
16661694
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
1695+
["count", "into_iter"] => iter_count::lints(cx, expr, &arg_lists[1], "into_iter"),
1696+
["count", "iter"] => iter_count::lints(cx, expr, &arg_lists[1], "iter"),
1697+
["count", "iter_mut"] => iter_count::lints(cx, expr, &arg_lists[1], "iter_mut"),
16671698
["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
16681699
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
16691700
["nth", "bytes"] => bytes_nth::lints(cx, expr, &arg_lists[1]),

tests/ui/auxiliary/option_helpers.rs

+4
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,8 @@ impl IteratorFalsePositives {
4848
pub fn skip_while(self) -> IteratorFalsePositives {
4949
self
5050
}
51+
52+
pub fn count(self) -> usize {
53+
self.foo as usize
54+
}
5155
}

tests/ui/iter_count.fixed

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// run-rustfix
2+
// aux-build:option_helpers.rs
3+
4+
#![warn(clippy::iter_count)]
5+
#![allow(
6+
unused_variables,
7+
array_into_iter,
8+
unused_mut,
9+
clippy::into_iter_on_ref,
10+
clippy::unnecessary_operation
11+
)]
12+
13+
extern crate option_helpers;
14+
15+
use option_helpers::IteratorFalsePositives;
16+
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
17+
18+
/// Struct to generate false positives for things with `.iter()`.
19+
#[derive(Copy, Clone)]
20+
struct HasIter;
21+
22+
impl HasIter {
23+
fn iter(self) -> IteratorFalsePositives {
24+
IteratorFalsePositives { foo: 0 }
25+
}
26+
27+
fn iter_mut(self) -> IteratorFalsePositives {
28+
IteratorFalsePositives { foo: 0 }
29+
}
30+
31+
fn into_iter(self) -> IteratorFalsePositives {
32+
IteratorFalsePositives { foo: 0 }
33+
}
34+
}
35+
36+
fn main() {
37+
let mut vec = vec![0, 1, 2, 3];
38+
let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
39+
let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect();
40+
let mut hash_set = HashSet::new();
41+
let mut hash_map = HashMap::new();
42+
let mut b_tree_map = BTreeMap::new();
43+
let mut b_tree_set = BTreeSet::new();
44+
let mut linked_list = LinkedList::new();
45+
let mut binary_heap = BinaryHeap::new();
46+
hash_set.insert(1);
47+
hash_map.insert(1, 2);
48+
b_tree_map.insert(1, 2);
49+
b_tree_set.insert(1);
50+
linked_list.push_back(1);
51+
binary_heap.push(1);
52+
53+
&vec[..].len();
54+
vec.len();
55+
boxed_slice.len();
56+
vec_deque.len();
57+
hash_set.len();
58+
hash_map.len();
59+
b_tree_map.len();
60+
b_tree_set.len();
61+
linked_list.len();
62+
binary_heap.len();
63+
64+
vec.len();
65+
&vec[..].len();
66+
vec_deque.len();
67+
hash_map.len();
68+
b_tree_map.len();
69+
linked_list.len();
70+
71+
&vec[..].len();
72+
vec.len();
73+
vec_deque.len();
74+
hash_set.len();
75+
hash_map.len();
76+
b_tree_map.len();
77+
b_tree_set.len();
78+
linked_list.len();
79+
binary_heap.len();
80+
81+
// Make sure we don't lint for non-relevant types.
82+
let false_positive = HasIter;
83+
false_positive.iter().count();
84+
false_positive.iter_mut().count();
85+
false_positive.into_iter().count();
86+
}

tests/ui/iter_count.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// run-rustfix
2+
// aux-build:option_helpers.rs
3+
4+
#![warn(clippy::iter_count)]
5+
#![allow(
6+
unused_variables,
7+
array_into_iter,
8+
unused_mut,
9+
clippy::into_iter_on_ref,
10+
clippy::unnecessary_operation
11+
)]
12+
13+
extern crate option_helpers;
14+
15+
use option_helpers::IteratorFalsePositives;
16+
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
17+
18+
/// Struct to generate false positives for things with `.iter()`.
19+
#[derive(Copy, Clone)]
20+
struct HasIter;
21+
22+
impl HasIter {
23+
fn iter(self) -> IteratorFalsePositives {
24+
IteratorFalsePositives { foo: 0 }
25+
}
26+
27+
fn iter_mut(self) -> IteratorFalsePositives {
28+
IteratorFalsePositives { foo: 0 }
29+
}
30+
31+
fn into_iter(self) -> IteratorFalsePositives {
32+
IteratorFalsePositives { foo: 0 }
33+
}
34+
}
35+
36+
fn main() {
37+
let mut vec = vec![0, 1, 2, 3];
38+
let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
39+
let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect();
40+
let mut hash_set = HashSet::new();
41+
let mut hash_map = HashMap::new();
42+
let mut b_tree_map = BTreeMap::new();
43+
let mut b_tree_set = BTreeSet::new();
44+
let mut linked_list = LinkedList::new();
45+
let mut binary_heap = BinaryHeap::new();
46+
hash_set.insert(1);
47+
hash_map.insert(1, 2);
48+
b_tree_map.insert(1, 2);
49+
b_tree_set.insert(1);
50+
linked_list.push_back(1);
51+
binary_heap.push(1);
52+
53+
&vec[..].iter().count();
54+
vec.iter().count();
55+
boxed_slice.iter().count();
56+
vec_deque.iter().count();
57+
hash_set.iter().count();
58+
hash_map.iter().count();
59+
b_tree_map.iter().count();
60+
b_tree_set.iter().count();
61+
linked_list.iter().count();
62+
binary_heap.iter().count();
63+
64+
vec.iter_mut().count();
65+
&vec[..].iter_mut().count();
66+
vec_deque.iter_mut().count();
67+
hash_map.iter_mut().count();
68+
b_tree_map.iter_mut().count();
69+
linked_list.iter_mut().count();
70+
71+
&vec[..].into_iter().count();
72+
vec.into_iter().count();
73+
vec_deque.into_iter().count();
74+
hash_set.into_iter().count();
75+
hash_map.into_iter().count();
76+
b_tree_map.into_iter().count();
77+
b_tree_set.into_iter().count();
78+
linked_list.into_iter().count();
79+
binary_heap.into_iter().count();
80+
81+
// Make sure we don't lint for non-relevant types.
82+
let false_positive = HasIter;
83+
false_positive.iter().count();
84+
false_positive.iter_mut().count();
85+
false_positive.into_iter().count();
86+
}

0 commit comments

Comments
 (0)