]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/map_clone.rs
1546964e4261296801b4af0772691f874d364a5b
[rust.git] / clippy_lints / src / map_clone.rs
1 use crate::utils::paths;
2 use crate::utils::{
3     in_macro, match_trait_method, match_type, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
4 };
5 use if_chain::if_chain;
6 use rustc::hir;
7 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
8 use rustc::{declare_tool_lint, lint_array};
9 use rustc_errors::Applicability;
10 use syntax::ast::Ident;
11 use syntax::source_map::Span;
12
13 #[derive(Clone)]
14 pub struct Pass;
15
16 /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
17 /// `iterator.cloned()` instead
18 ///
19 /// **Why is this bad?** Readability, this can be written more concisely
20 ///
21 /// **Known problems:** Sometimes `.cloned()` requires stricter trait
22 /// bound than `.map(|e| e.clone())` (which works because of the coercion).
23 /// See [#498](https://github.com/rust-lang-nursery/rust-clippy/issues/498).
24 ///
25 /// **Example:**
26 ///
27 /// ```rust
28 /// let x = vec![42, 43];
29 /// let y = x.iter();
30 /// let z = y.map(|i| *i);
31 /// ```
32 ///
33 /// The correct use would be:
34 ///
35 /// ```rust
36 /// let x = vec![42, 43];
37 /// let y = x.iter();
38 /// let z = y.cloned();
39 /// ```
40 declare_clippy_lint! {
41     pub MAP_CLONE,
42     style,
43     "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
44 }
45
46 impl LintPass for Pass {
47     fn get_lints(&self) -> LintArray {
48         lint_array!(MAP_CLONE)
49     }
50 }
51
52 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
53     fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
54         if in_macro(e.span) {
55             return;
56         }
57
58         if_chain! {
59             if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node;
60             if args.len() == 2;
61             if method.ident.as_str() == "map";
62             let ty = cx.tables.expr_ty(&args[0]);
63             if match_type(cx, ty, &paths::OPTION) || match_trait_method(cx, e, &paths::ITERATOR);
64             if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].node;
65             let closure_body = cx.tcx.hir().body(body_id);
66             let closure_expr = remove_blocks(&closure_body.value);
67             then {
68                 match closure_body.arguments[0].pat.node {
69                     hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(
70                         hir::BindingAnnotation::Unannotated, _, name, None
71                     ) = inner.node {
72                         lint(cx, e.span, args[0].span, name, closure_expr);
73                     },
74                     hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
75                         match closure_expr.node {
76                             hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
77                                 if !cx.tables.expr_ty(inner).is_box() {
78                                     lint(cx, e.span, args[0].span, name, inner);
79                                 }
80                             },
81                             hir::ExprKind::MethodCall(ref method, _, ref obj) => {
82                                 if method.ident.as_str() == "clone"
83                                     && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
84                                     lint(cx, e.span, args[0].span, name, &obj[0]);
85                                 }
86                             },
87                             _ => {},
88                         }
89                     },
90                     _ => {},
91                 }
92             }
93         }
94     }
95 }
96
97 fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, name: Ident, path: &hir::Expr) {
98     if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.node {
99         if path.segments.len() == 1 && path.segments[0].ident == name {
100             let mut applicability = Applicability::MachineApplicable;
101             span_lint_and_sugg(
102                 cx,
103                 MAP_CLONE,
104                 replace,
105                 "You are using an explicit closure for cloning elements",
106                 "Consider calling the dedicated `cloned` method",
107                 format!(
108                     "{}.cloned()",
109                     snippet_with_applicability(cx, root, "..", &mut applicability)
110                 ),
111                 applicability,
112             )
113         }
114     }
115 }