Skip to content

Commit 281ff1d

Browse files
committed
Added lints into_iter_on_ref and into_iter_on_array. Fix #1565.
1 parent b1d0343 commit 281ff1d

File tree

7 files changed

+346
-3
lines changed

7 files changed

+346
-3
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,8 @@ All notable changes to this project will be documented in this file.
713713
[`inline_fn_without_body`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#inline_fn_without_body
714714
[`int_plus_one`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#int_plus_one
715715
[`integer_arithmetic`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#integer_arithmetic
716+
[`into_iter_on_array`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#into_iter_on_array
717+
[`into_iter_on_ref`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#into_iter_on_ref
716718
[`invalid_ref`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#invalid_ref
717719
[`invalid_regex`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#invalid_regex
718720
[`invalid_upcast_comparisons`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in
99

1010
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
1111

12-
[There are 280 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
12+
[There are 282 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
1313

1414
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1515

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
618618
methods::EXPECT_FUN_CALL,
619619
methods::FILTER_NEXT,
620620
methods::GET_UNWRAP,
621+
methods::INTO_ITER_ON_ARRAY,
622+
methods::INTO_ITER_ON_REF,
621623
methods::ITER_CLONED_COLLECT,
622624
methods::ITER_NTH,
623625
methods::ITER_SKIP_NEXT,
@@ -773,6 +775,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
773775
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
774776
methods::CHARS_LAST_CMP,
775777
methods::GET_UNWRAP,
778+
methods::INTO_ITER_ON_REF,
776779
methods::ITER_CLONED_COLLECT,
777780
methods::ITER_SKIP_NEXT,
778781
methods::NEW_RET_NO_SELF,
@@ -923,6 +926,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
923926
loops::REVERSE_RANGE_LOOP,
924927
loops::WHILE_IMMUTABLE_CONDITION,
925928
methods::CLONE_DOUBLE_REF,
929+
methods::INTO_ITER_ON_ARRAY,
926930
methods::TEMPORARY_CSTRING_AS_PTR,
927931
minmax::MIN_MAX,
928932
misc::CMP_NAN,

clippy_lints/src/methods/mod.rs

+116-1
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,51 @@ declare_clippy_lint! {
738738
"using `filter_map` when a more succinct alternative exists"
739739
}
740740

741+
/// **What it does:** Checks for `into_iter` calls on types which should be replaced by `iter` or
742+
/// `iter_mut`.
743+
///
744+
/// **Why is this bad?** Arrays and `PathBuf` do not yet have an `into_iter` method which move out
745+
/// their content into an iterator. Calling `into_iter` instead just forwards to `iter` or
746+
/// `iter_mut` due to auto-referencing, of which only yield references. Furthermore, when the
747+
/// standard library actually [implements the `into_iter` method][25725] which moves the content out
748+
/// of the array, the original use of `into_iter` got inferred with the wrong type and the code will
749+
/// be broken.
750+
///
751+
/// **Known problems:** None
752+
///
753+
/// **Example:**
754+
///
755+
/// ```rust
756+
/// let _ = [1, 2, 3].into_iter().map(|x| *x).collect::<Vec<u32>>();
757+
/// ```
758+
///
759+
/// [25725]: https://github.com/rust-lang/rust/issues/25725
760+
declare_clippy_lint! {
761+
pub INTO_ITER_ON_ARRAY,
762+
correctness,
763+
"using `.into_iter()` on an array"
764+
}
765+
766+
/// **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter`
767+
/// or `iter_mut`.
768+
///
769+
/// **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its
770+
/// content into the resulting iterator, which is confusing. It is better just call `iter` or
771+
/// `iter_mut` directly.
772+
///
773+
/// **Known problems:** None
774+
///
775+
/// **Example:**
776+
///
777+
/// ```rust
778+
/// let _ = (&vec![3, 4, 5]).into_iter();
779+
/// ```
780+
declare_clippy_lint! {
781+
pub INTO_ITER_ON_REF,
782+
style,
783+
"using `.into_iter()` on a reference"
784+
}
785+
741786
impl LintPass for Pass {
742787
fn get_lints(&self) -> LintArray {
743788
lint_array!(
@@ -772,7 +817,9 @@ impl LintPass for Pass {
772817
ITER_CLONED_COLLECT,
773818
USELESS_ASREF,
774819
UNNECESSARY_FOLD,
775-
UNNECESSARY_FILTER_MAP
820+
UNNECESSARY_FILTER_MAP,
821+
INTO_ITER_ON_ARRAY,
822+
INTO_ITER_ON_REF,
776823
)
777824
}
778825
}
@@ -857,6 +904,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
857904
lint_single_char_pattern(cx, expr, &args[pos]);
858905
}
859906
},
907+
ty::Ref(..) if method_call.ident.name == "into_iter" => {
908+
lint_into_iter(cx, expr, self_ty, *method_span);
909+
},
860910
_ => (),
861911
}
862912
},
@@ -2093,6 +2143,71 @@ fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr, call_name: &str, as_re
20932143
}
20942144
}
20952145

2146+
fn ty_has_iter_method(cx: &LateContext<'_, '_>, self_ref_ty: ty::Ty<'_>) -> Option<(&'static Lint, &'static str, &'static str)> {
2147+
let (self_ty, mutbl) = match self_ref_ty.sty {
2148+
ty::TyKind::Ref(_, self_ty, mutbl) => (self_ty, mutbl),
2149+
_ => unreachable!(),
2150+
};
2151+
let method_name = match mutbl {
2152+
hir::MutImmutable => "iter",
2153+
hir::MutMutable => "iter_mut",
2154+
};
2155+
2156+
let def_id = match self_ty.sty {
2157+
ty::TyKind::Array(..) => return Some((INTO_ITER_ON_ARRAY, "array", method_name)),
2158+
ty::TyKind::Slice(..) => return Some((INTO_ITER_ON_REF, "slice", method_name)),
2159+
ty::Adt(adt, _) => adt.did,
2160+
_ => return None,
2161+
};
2162+
2163+
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
2164+
// exists and has the desired signature. Unfortunately FnCtxt is not exported
2165+
// so we can't use its `lookup_method` method.
2166+
static INTO_ITER_COLLECTIONS: [(&Lint, &[&str]); 13] = [
2167+
(INTO_ITER_ON_REF, &paths::VEC),
2168+
(INTO_ITER_ON_REF, &paths::OPTION),
2169+
(INTO_ITER_ON_REF, &paths::RESULT),
2170+
(INTO_ITER_ON_REF, &paths::BTREESET),
2171+
(INTO_ITER_ON_REF, &paths::BTREEMAP),
2172+
(INTO_ITER_ON_REF, &paths::VEC_DEQUE),
2173+
(INTO_ITER_ON_REF, &paths::LINKED_LIST),
2174+
(INTO_ITER_ON_REF, &paths::BINARY_HEAP),
2175+
(INTO_ITER_ON_REF, &paths::HASHSET),
2176+
(INTO_ITER_ON_REF, &paths::HASHMAP),
2177+
(INTO_ITER_ON_ARRAY, &["std", "path", "PathBuf"]),
2178+
(INTO_ITER_ON_REF, &["std", "path", "Path"]),
2179+
(INTO_ITER_ON_REF, &["std", "sync", "mpsc", "Receiver"]),
2180+
];
2181+
2182+
for (lint, path) in &INTO_ITER_COLLECTIONS {
2183+
if match_def_path(cx.tcx, def_id, path) {
2184+
return Some((lint, path.last().unwrap(), method_name))
2185+
}
2186+
}
2187+
None
2188+
}
2189+
2190+
fn lint_into_iter(cx: &LateContext<'_, '_>, expr: &hir::Expr, self_ref_ty: ty::Ty<'_>, method_span: Span) {
2191+
if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
2192+
return;
2193+
}
2194+
if let Some((lint, kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
2195+
span_lint_and_sugg(
2196+
cx,
2197+
lint,
2198+
method_span,
2199+
&format!(
2200+
"this .into_iter() call is equivalent to .{}() and will not move the {}",
2201+
method_name,
2202+
kind,
2203+
),
2204+
"call directly",
2205+
method_name.to_owned(),
2206+
);
2207+
}
2208+
}
2209+
2210+
20962211
/// Given a `Result<T, E>` type, return its error type (`E`).
20972212
fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
20982213
if let ty::Adt(_, substs) = ty.sty {

tests/ui/for_loop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Unrelated {
8787
clippy::explicit_counter_loop, clippy::for_kv_map)]
8888
#[warn(clippy::unused_collect)]
8989
#[allow(clippy::linkedlist, clippy::shadow_unrelated, clippy::unnecessary_mut_passed, clippy::cyclomatic_complexity, clippy::similar_names)]
90-
#[allow(clippy::many_single_char_names, unused_variables)]
90+
#[allow(clippy::many_single_char_names, unused_variables, clippy::into_iter_on_array)]
9191
fn main() {
9292
const MAX_LEN: usize = 42;
9393

tests/ui/into_iter_on_ref.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![warn(clippy::into_iter_on_ref)]
2+
#![deny(clippy::into_iter_on_array)]
3+
4+
struct X;
5+
use std::collections::*;
6+
7+
fn main() {
8+
for _ in &[1,2,3] {}
9+
for _ in vec![X, X] {}
10+
for _ in &vec![X, X] {}
11+
for _ in [1,2,3].into_iter() {} //~ ERROR equivalent to .iter()
12+
13+
let _ = [1,2,3].into_iter(); //~ ERROR equivalent to .iter()
14+
let _ = vec![1,2,3].into_iter();
15+
let _ = (&vec![1,2,3]).into_iter(); //~ WARN equivalent to .iter()
16+
let _ = vec![1,2,3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
17+
let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
18+
let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
19+
20+
let _ = (&&&&&&&[1,2,3]).into_iter(); //~ ERROR equivalent to .iter()
21+
let _ = (&&&&mut &&&[1,2,3]).into_iter(); //~ ERROR equivalent to .iter()
22+
let _ = (&mut &mut &mut [1,2,3]).into_iter(); //~ ERROR equivalent to .iter_mut()
23+
24+
let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
25+
let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
26+
let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
27+
let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
28+
let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
29+
let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
30+
let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
31+
let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
32+
let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
33+
let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
34+
let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
35+
let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
36+
let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
37+
let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
38+
39+
let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
40+
let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
41+
let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
42+
let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
43+
let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
44+
}

0 commit comments

Comments
 (0)