Add a lint for slice.iter().cloned().collect()

If one uses `slice.iter().cloned().collect()` to create a new `Vec`,
it should be `slice.to_owned()`.

Fix #1292
This commit is contained in:
f001 2017-02-12 17:03:09 +08:00
parent 4b29f9a364
commit 98aa0db0ac
4 changed files with 48 additions and 3 deletions

View file

@ -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<isize> = s.iter().cloned().collect();
/// ```
/// The correct use would be:
/// ```rust
/// let s = [1,2,3,4,5];
/// let s2 : Vec<isize> = 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() {

View file

@ -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)]

View file

@ -689,3 +689,8 @@ fn temporary_cstring() {
}
fn iter_clone_collect() {
let v = [1,2,3,4,5];
let v2 : Vec<isize> = v.iter().cloned().collect();
}

View file

@ -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<isize> = v.iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(iter_cloned_collect)] on by default
error: aborting due to 88 previous errors