5 use utils::{is_adjusted, match_path, match_trait_method, match_type, remove_blocks, paths, snippet,
6 span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
8 /// **What it does:** Checks for mapping `clone()` over an iterator.
10 /// **Why is this bad?** It makes the code less readable than using the
11 /// `.cloned()` adapter.
13 /// **Known problems:** None.
17 /// x.map(|e| e.clone());
22 "using `.map(|x| x.clone())` to clone an iterator or option's contents"
25 #[derive(Copy, Clone)]
28 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
29 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
31 if let ExprMethodCall(ref method, _, ref args) = expr.node {
32 if method.name == "map" && args.len() == 2 {
34 ExprClosure(_, ref decl, closure_eid, _) => {
35 let body = cx.tcx.hir.body(closure_eid);
36 let closure_expr = remove_blocks(&body.value);
37 let ty = cx.tables.pat_ty(&body.arguments[0].pat);
39 // nothing special in the argument, besides reference bindings
40 // (e.g. .map(|&x| x) )
41 let Some(first_arg) = iter_input_pats(decl, body).next(),
42 let Some(arg_ident) = get_arg_name(&first_arg.pat),
43 // the method is being called on a known type (option or iterator)
44 let Some(type_name) = get_type_name(cx, expr, &args[0])
46 // look for derefs, for .map(|x| *x)
47 if only_derefs(cx, &*closure_expr, arg_ident) &&
48 // .cloned() only removes one level of indirection, don't lint on more
49 walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
51 // the argument is not an &mut T
52 if let ty::TyRef(_, tam) = ty.sty {
53 if tam.mutbl == MutImmutable {
54 span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
55 "you seem to be using .map() to clone the contents of an {}, consider \
56 using `.cloned()`", type_name),
57 &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
61 // explicit clone() calls ( .map(|x| x.clone()) )
62 else if let ExprMethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
63 if clone_call.name == "clone" &&
64 clone_args.len() == 1 &&
65 match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
66 expr_eq_name(&clone_args[0], arg_ident)
68 span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
69 "you seem to be using .map() to clone the contents of an {}, consider \
70 using `.cloned()`", type_name),
71 &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
76 ExprPath(ref path) => {
77 if match_path(path, &paths::CLONE) {
78 let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
84 "you seem to be using .map() to clone the contents of an \
85 {}, consider using `.cloned()`",
88 &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
99 fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
101 ExprPath(QPath::Resolved(None, ref path)) => {
105 parameters: PathParameters::none(),
108 !path.is_global() && path.segments[..] == arg_segment
114 fn get_type_name(cx: &LateContext, expr: &Expr, arg: &Expr) -> Option<&'static str> {
115 if match_trait_method(cx, expr, &paths::ITERATOR) {
117 } else if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(arg)), &paths::OPTION) {
124 fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
126 PatKind::Binding(_, _, name, None) => Some(name.node),
127 PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
132 fn only_derefs(cx: &LateContext, expr: &Expr, id: ast::Name) -> bool {
134 ExprUnary(UnDeref, ref subexpr) if !is_adjusted(cx, subexpr) => only_derefs(cx, subexpr, id),
135 _ => expr_eq_name(expr, id),
139 impl LintPass for Pass {
140 fn get_lints(&self) -> LintArray {
141 lint_array!(MAP_CLONE)