]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/clone_on_copy.rs
4a130ed47db15bc00e6f3afc075c963f55a5a75d
[rust.git] / clippy_lints / src / methods / clone_on_copy.rs
1 use crate::utils::{is_copy, span_lint_and_then, sugg};
2 use rustc_errors::Applicability;
3 use rustc_hir as hir;
4 use rustc_lint::LateContext;
5 use rustc_middle::ty::{self, Ty};
6 use std::iter;
7
8 use super::CLONE_DOUBLE_REF;
9 use super::CLONE_ON_COPY;
10
11 /// Checks for the `CLONE_ON_COPY` lint.
12 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
13     let ty = cx.typeck_results().expr_ty(expr);
14     if let ty::Ref(_, inner, _) = arg_ty.kind() {
15         if let ty::Ref(_, innermost, _) = inner.kind() {
16             span_lint_and_then(
17                 cx,
18                 CLONE_DOUBLE_REF,
19                 expr.span,
20                 &format!(
21                     "using `clone` on a double-reference; \
22                     this will copy the reference of type `{}` instead of cloning the inner type",
23                     ty
24                 ),
25                 |diag| {
26                     if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
27                         let mut ty = innermost;
28                         let mut n = 0;
29                         while let ty::Ref(_, inner, _) = ty.kind() {
30                             ty = inner;
31                             n += 1;
32                         }
33                         let refs: String = iter::repeat('&').take(n + 1).collect();
34                         let derefs: String = iter::repeat('*').take(n).collect();
35                         let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
36                         diag.span_suggestion(
37                             expr.span,
38                             "try dereferencing it",
39                             format!("{}({}{}).clone()", refs, derefs, snip.deref()),
40                             Applicability::MaybeIncorrect,
41                         );
42                         diag.span_suggestion(
43                             expr.span,
44                             "or try being explicit if you are sure, that you want to clone a reference",
45                             explicit,
46                             Applicability::MaybeIncorrect,
47                         );
48                     }
49                 },
50             );
51             return; // don't report clone_on_copy
52         }
53     }
54
55     if is_copy(cx, ty) {
56         let snip;
57         if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
58             let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
59             match &cx.tcx.hir().get(parent) {
60                 hir::Node::Expr(parent) => match parent.kind {
61                     // &*x is a nop, &x.clone() is not
62                     hir::ExprKind::AddrOf(..) => return,
63                     // (*x).func() is useless, x.clone().func() can work in case func borrows mutably
64                     hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => {
65                         return;
66                     },
67
68                     _ => {},
69                 },
70                 hir::Node::Stmt(stmt) => {
71                     if let hir::StmtKind::Local(ref loc) = stmt.kind {
72                         if let hir::PatKind::Ref(..) = loc.pat.kind {
73                             // let ref y = *x borrows x, let ref y = x.clone() does not
74                             return;
75                         }
76                     }
77                 },
78                 _ => {},
79             }
80
81             // x.clone() might have dereferenced x, possibly through Deref impls
82             if cx.typeck_results().expr_ty(arg) == ty {
83                 snip = Some(("try removing the `clone` call", format!("{}", snippet)));
84             } else {
85                 let deref_count = cx
86                     .typeck_results()
87                     .expr_adjustments(arg)
88                     .iter()
89                     .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
90                     .count();
91                 let derefs: String = iter::repeat('*').take(deref_count).collect();
92                 snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
93             }
94         } else {
95             snip = None;
96         }
97         span_lint_and_then(
98             cx,
99             CLONE_ON_COPY,
100             expr.span,
101             &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
102             |diag| {
103                 if let Some((text, snip)) = snip {
104                     diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
105                 }
106             },
107         );
108     }
109 }