]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/map_clone.rs
Auto merge of #81993 - flip1995:clippyup, r=Manishearth
[rust.git] / clippy_lints / src / map_clone.rs
1 use crate::utils::paths;
2 use crate::utils::{
3     is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
4 };
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir as hir;
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_middle::mir::Mutability;
10 use rustc_middle::ty;
11 use rustc_middle::ty::adjustment::Adjust;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_span::symbol::Ident;
14 use rustc_span::{sym, Span};
15
16 declare_clippy_lint! {
17     /// **What it does:** Checks for usage of `map(|x| x.clone())` or
18     /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
19     /// and suggests `cloned()` or `copied()` instead
20     ///
21     /// **Why is this bad?** Readability, this can be written more concisely
22     ///
23     /// **Known problems:** None
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     pub MAP_CLONE,
41     style,
42     "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
43 }
44
45 declare_lint_pass!(MapClone => [MAP_CLONE]);
46
47 impl<'tcx> LateLintPass<'tcx> for MapClone {
48     fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
49         if e.span.from_expansion() {
50             return;
51         }
52
53         if_chain! {
54             if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind;
55             if args.len() == 2;
56             if method.ident.name == sym::map;
57             let ty = cx.typeck_results().expr_ty(&args[0]);
58             if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
59             if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
60             let closure_body = cx.tcx.hir().body(body_id);
61             let closure_expr = remove_blocks(&closure_body.value);
62             then {
63                 match closure_body.params[0].pat.kind {
64                     hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
65                         hir::BindingAnnotation::Unannotated, .., name, None
66                     ) = inner.kind {
67                         if ident_eq(name, closure_expr) {
68                             lint(cx, e.span, args[0].span, true);
69                         }
70                     },
71                     hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
72                         match closure_expr.kind {
73                             hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) => {
74                                 if ident_eq(name, inner) {
75                                     if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
76                                         lint(cx, e.span, args[0].span, true);
77                                     }
78                                 }
79                             },
80                             hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! {
81                                 if ident_eq(name, obj) && method.ident.name == sym::clone;
82                                 if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT);
83                                 // no autoderefs
84                                 if !cx.typeck_results().expr_adjustments(obj).iter()
85                                     .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
86                                 then {
87                                     let obj_ty = cx.typeck_results().expr_ty(obj);
88                                     if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
89                                         if matches!(mutability, Mutability::Not) {
90                                             let copy = is_copy(cx, ty);
91                                             lint(cx, e.span, args[0].span, copy);
92                                         }
93                                     } else {
94                                         lint_needless_cloning(cx, e.span, args[0].span);
95                                     }
96                                 }
97                             },
98                             _ => {},
99                         }
100                     },
101                     _ => {},
102                 }
103             }
104         }
105     }
106 }
107
108 fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
109     if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind {
110         path.segments.len() == 1 && path.segments[0].ident == name
111     } else {
112         false
113     }
114 }
115
116 fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
117     span_lint_and_sugg(
118         cx,
119         MAP_CLONE,
120         root.trim_start(receiver).unwrap(),
121         "you are needlessly cloning iterator elements",
122         "remove the `map` call",
123         String::new(),
124         Applicability::MachineApplicable,
125     )
126 }
127
128 fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
129     let mut applicability = Applicability::MachineApplicable;
130     if copied {
131         span_lint_and_sugg(
132             cx,
133             MAP_CLONE,
134             replace,
135             "you are using an explicit closure for copying elements",
136             "consider calling the dedicated `copied` method",
137             format!(
138                 "{}.copied()",
139                 snippet_with_applicability(cx, root, "..", &mut applicability)
140             ),
141             applicability,
142         )
143     } else {
144         span_lint_and_sugg(
145             cx,
146             MAP_CLONE,
147             replace,
148             "you are using an explicit closure for cloning elements",
149             "consider calling the dedicated `cloned` method",
150             format!(
151                 "{}.cloned()",
152                 snippet_with_applicability(cx, root, "..", &mut applicability)
153             ),
154             applicability,
155         )
156     }
157 }