diff --git a/clippy_lints/src/methods.rs b/clippy_lints/src/methods.rs index 30ffcc9f6e3a..2885c27e5456 100644 --- a/clippy_lints/src/methods.rs +++ b/clippy_lints/src/methods.rs @@ -494,13 +494,33 @@ declare_lint! { /// s.push_str(abc); /// s.push_str(&def)); /// ``` - declare_lint! { pub STRING_EXTEND_CHARS, Warn, "using `x.extend(s.chars())` where s is a `&str` or `String`" } +/// **What it does:** Checks for the use of `.cloned().collect()` on slice to create a Vec. +/// +/// **Why is this bad?** `.to_owned()` is clearer +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let s = [1,2,3,4,5]; +/// let s2 : Vec = s.iter().cloned().collect(); +/// ``` +/// The correct use would be: +/// ```rust +/// let s = [1,2,3,4,5]; +/// let s2 : Vec = s.to_owned(); +/// ``` +declare_lint! { + pub ITER_CLONED_COLLECT, + Warn, + "using `.cloned().collect()` on slice to create a `Vec`" +} impl LintPass for Pass { fn get_lints(&self) -> LintArray { @@ -525,7 +545,8 @@ impl LintPass for Pass { ITER_NTH, ITER_SKIP_NEXT, GET_UNWRAP, - STRING_EXTEND_CHARS) + STRING_EXTEND_CHARS, + ITER_CLONED_COLLECT) } } @@ -580,6 +601,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { lint_iter_nth(cx, expr, arglists[0], true); } else if method_chain_args(expr, &["skip", "next"]).is_some() { lint_iter_skip_next(cx, expr); + } else if let Some(arglists) = method_chain_args(expr, &["cloned", "collect"]) { + lint_iter_cloned_collect(cx, expr, arglists[0]); } lint_or_fun_call(cx, expr, &name.node.as_str(), args); @@ -879,6 +902,16 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr }} } +fn lint_iter_cloned_collect(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr]) { + if match_type(cx, cx.tables.expr_ty(expr), &paths::VEC) && + derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() { + span_lint(cx, + ITER_CLONED_COLLECT, + expr.span, + "called `cloned().collect()` on a slice to create a `Vec`. This is more succinctly expressed by calling `to_owned(x)`"); + } +} + fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr], is_mut: bool) { let mut_str = if is_mut { "_mut" } else { "" }; let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() { diff --git a/src/main.rs b/src/main.rs index 7a744356eb4f..ac90528c6063 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ // error-pattern:yummy #![feature(box_syntax)] #![feature(rustc_private)] -#![feature(static_in_const)] #![allow(unknown_lints, missing_docs_in_private_items)] diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index da37ac8f566a..8eb4ff6870f7 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -689,3 +689,8 @@ fn temporary_cstring() { } + +fn iter_clone_collect() { + let v = [1,2,3,4,5]; + let v2 : Vec = v.iter().cloned().collect(); +} diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index f4294d8b396b..a62784e3302b 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -950,5 +950,13 @@ help: assign the `CString` to a variable to extend its lifetime 687 | CString::new("foo").unwrap().as_ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: called `cloned().collect()` on a slice to create a `Vec`. This is more succinctly expressed by calling `to_owned(x)` + --> $DIR/methods.rs:695:27 + | +695 | let v2 : Vec = v.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(iter_cloned_collect)] on by default + error: aborting due to 88 previous errors