Skip to content

Commit c32ba5e

Browse files
committed
Add copy_iterator lint (#1534)
1 parent f27aaac commit c32ba5e

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

clippy_lints/src/copy_iterator.rs

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

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub mod bytecount;
8282
pub mod collapsible_if;
8383
pub mod const_static_lifetime;
8484
pub mod copies;
85+
pub mod copy_iterator;
8586
pub mod cyclomatic_complexity;
8687
pub mod default_trait_access;
8788
pub mod derive;
@@ -345,6 +346,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
345346
reg.register_late_lint_pass(box types::InvalidUpcastComparisons);
346347
reg.register_late_lint_pass(box regex::Pass::default());
347348
reg.register_late_lint_pass(box copies::CopyAndPaste);
349+
reg.register_late_lint_pass(box copy_iterator::CopyIterator);
348350
reg.register_late_lint_pass(box format::Pass);
349351
reg.register_early_lint_pass(box formatting::Formatting);
350352
reg.register_late_lint_pass(box swap::Swap);
@@ -437,6 +439,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
437439
reg.register_lint_group("clippy_pedantic", vec![
438440
attrs::INLINE_ALWAYS,
439441
copies::MATCH_SAME_ARMS,
442+
copy_iterator::COPY_ITERATOR,
440443
default_trait_access::DEFAULT_TRAIT_ACCESS,
441444
derive::EXPL_IMPL_CLONE_ON_COPY,
442445
doc::DOC_MARKDOWN,

tests/ui/copy_iterator.rs

+23
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

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
--> $DIR/copy_iterator.rs:6:1
16+
|
17+
6 | / impl Iterator for Countdown {
18+
7 | | type Item = u8;
19+
8 | |
20+
9 | | fn next(&mut self) -> Option<u8> {
21+
... |
22+
14 | | }
23+
15 | | }
24+
| |_^
25+
26+
error: aborting due to previous error
27+

0 commit comments

Comments
 (0)