Skip to content

Commit afdf350

Browse files
committed
Add copy_iterator lint (#1534)
1 parent dda6566 commit afdf350

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed

clippy_lints/src/copy_iterator.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::utils::{is_copy, match_path, paths, span_note_and_lint};
2+
use rustc::hir::{Item, ItemKind};
3+
use rustc::lint::*;
4+
use rustc::{declare_lint, lint_array};
5+
6+
/// **What it does:** Checks for types that implement `Copy` as well as
7+
/// `Iterator`.
8+
///
9+
/// **Why is this bad?** Implicit copies can be confusing when working with
10+
/// iterator combinators.
11+
///
12+
/// **Known problems:** None.
13+
///
14+
/// **Example:**
15+
/// ```rust
16+
/// #[derive(Copy, Clone)]
17+
/// struct Countdown(u8);
18+
///
19+
/// impl Iterator for Countdown {
20+
/// // ...
21+
/// }
22+
///
23+
/// let a: Vec<_> = my_iterator.take(1).collect();
24+
/// let b: Vec<_> = my_iterator.collect();
25+
/// ```
26+
declare_clippy_lint! {
27+
pub COPY_ITERATOR,
28+
pedantic,
29+
"implementing `Iterator` on a `Copy` type"
30+
}
31+
32+
pub struct CopyIterator;
33+
34+
impl LintPass for CopyIterator {
35+
fn get_lints(&self) -> LintArray {
36+
lint_array![COPY_ITERATOR]
37+
}
38+
}
39+
40+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyIterator {
41+
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
42+
if let ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) = item.node {
43+
let ty = cx.tcx.type_of(cx.tcx.hir.local_def_id(item.id));
44+
45+
if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
46+
span_note_and_lint(
47+
cx,
48+
COPY_ITERATOR,
49+
item.span,
50+
"you are implementing `Iterator` on a `Copy` type",
51+
item.span,
52+
"consider implementing `IntoIterator` instead",
53+
);
54+
}
55+
}
56+
}
57+
}

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub mod bytecount;
6666
pub mod collapsible_if;
6767
pub mod const_static_lifetime;
6868
pub mod copies;
69+
pub mod copy_iterator;
6970
pub mod cyclomatic_complexity;
7071
pub mod default_trait_access;
7172
pub mod derive;
@@ -338,6 +339,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
338339
reg.register_late_lint_pass(box types::InvalidUpcastComparisons);
339340
reg.register_late_lint_pass(box regex::Pass::default());
340341
reg.register_late_lint_pass(box copies::CopyAndPaste);
342+
reg.register_late_lint_pass(box copy_iterator::CopyIterator);
341343
reg.register_late_lint_pass(box format::Pass);
342344
reg.register_early_lint_pass(box formatting::Formatting);
343345
reg.register_late_lint_pass(box swap::Swap);
@@ -431,6 +433,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
431433
reg.register_lint_group("clippy_pedantic", vec![
432434
attrs::INLINE_ALWAYS,
433435
copies::MATCH_SAME_ARMS,
436+
copy_iterator::COPY_ITERATOR,
434437
default_trait_access::DEFAULT_TRAIT_ACCESS,
435438
derive::EXPL_IMPL_CLONE_ON_COPY,
436439
doc::DOC_MARKDOWN,

tests/ui/copy_iterator.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![warn(copy_iterator)]
2+
3+
#[derive(Copy, Clone)]
4+
struct Countdown(u8);
5+
6+
impl Iterator for Countdown {
7+
type Item = u8;
8+
9+
fn next(&mut self) -> Option<u8> {
10+
self.0.checked_sub(1).map(|c| {
11+
self.0 = c;
12+
c
13+
})
14+
}
15+
}
16+
17+
fn main() {
18+
let my_iterator = Countdown(5);
19+
let a: Vec<_> = my_iterator.take(1).collect();
20+
assert_eq!(a.len(), 1);
21+
let b: Vec<_> = my_iterator.collect();
22+
assert_eq!(b.len(), 5);
23+
}

tests/ui/copy_iterator.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: you are implementing `Iterator` on a `Copy` type
2+
--> $DIR/copy_iterator.rs:6:1
3+
|
4+
6 | / impl Iterator for Countdown {
5+
7 | | type Item = u8;
6+
8 | |
7+
9 | | fn next(&mut self) -> Option<u8> {
8+
... |
9+
14 | | }
10+
15 | | }
11+
| |_^
12+
|
13+
= note: `-D copy-iterator` implied by `-D warnings`
14+
= note: consider implementing `IntoIterator` instead
15+
16+
error: aborting due to previous error
17+

0 commit comments

Comments
 (0)