]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
Merge commit '7f27e2e74ef957baa382dc05cf08df6368165c74' into clippyup
[rust.git] / src / tools / clippy / clippy_lints / src / methods / clone_on_copy.rs
1 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2 use clippy_utils::get_parent_node;
3 use clippy_utils::source::snippet_with_context;
4 use clippy_utils::sugg;
5 use clippy_utils::ty::is_copy;
6 use rustc_errors::Applicability;
7 use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
8 use rustc_lint::LateContext;
9 use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths};
10 use rustc_span::symbol::{sym, Symbol};
11
12 use super::CLONE_DOUBLE_REF;
13 use super::CLONE_ON_COPY;
14
15 /// Checks for the `CLONE_ON_COPY` lint.
16 #[allow(clippy::too_many_lines)]
17 pub(super) fn check(
18     cx: &LateContext<'_>,
19     expr: &Expr<'_>,
20     method_name: Symbol,
21     receiver: &Expr<'_>,
22     args: &[Expr<'_>],
23 ) {
24     let arg = if method_name == sym::clone && args.is_empty() {
25         receiver
26     } else {
27         return;
28     };
29     if cx
30         .typeck_results()
31         .type_dependent_def_id(expr.hir_id)
32         .and_then(|id| cx.tcx.trait_of_item(id))
33         .zip(cx.tcx.lang_items().clone_trait())
34         .map_or(true, |(x, y)| x != y)
35     {
36         return;
37     }
38     let arg_adjustments = cx.typeck_results().expr_adjustments(arg);
39     let arg_ty = arg_adjustments
40         .last()
41         .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
42
43     let ty = cx.typeck_results().expr_ty(expr);
44     if let ty::Ref(_, inner, _) = arg_ty.kind() {
45         if let ty::Ref(_, innermost, _) = inner.kind() {
46             span_lint_and_then(
47                 cx,
48                 CLONE_DOUBLE_REF,
49                 expr.span,
50                 &with_forced_trimmed_paths!(format!(
51                     "using `clone` on a double-reference; \
52                     this will copy the reference of type `{ty}` instead of cloning the inner type"
53                 )),
54                 |diag| {
55                     if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
56                         let mut ty = innermost;
57                         let mut n = 0;
58                         while let ty::Ref(_, inner, _) = ty.kind() {
59                             ty = inner;
60                             n += 1;
61                         }
62                         let refs = "&".repeat(n + 1);
63                         let derefs = "*".repeat(n);
64                         let explicit = with_forced_trimmed_paths!(format!("<{refs}{ty}>::clone({snip})"));
65                         diag.span_suggestion(
66                             expr.span,
67                             "try dereferencing it",
68                             with_forced_trimmed_paths!(format!("{refs}({derefs}{}).clone()", snip.deref())),
69                             Applicability::MaybeIncorrect,
70                         );
71                         diag.span_suggestion(
72                             expr.span,
73                             "or try being explicit if you are sure, that you want to clone a reference",
74                             explicit,
75                             Applicability::MaybeIncorrect,
76                         );
77                     }
78                 },
79             );
80             return; // don't report clone_on_copy
81         }
82     }
83
84     if is_copy(cx, ty) {
85         let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
86             Some(Node::Expr(parent)) => match parent.kind {
87                 // &*x is a nop, &x.clone() is not
88                 ExprKind::AddrOf(..) => return,
89                 // (*x).func() is useless, x.clone().func() can work in case func borrows self
90                 ExprKind::MethodCall(_, self_arg, ..)
91                     if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) =>
92                 {
93                     return;
94                 },
95                 // ? is a Call, makes sure not to rec *x?, but rather (*x)?
96                 ExprKind::Call(hir_callee, _) => matches!(
97                     hir_callee.kind,
98                     ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
99                 ),
100                 ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true,
101                 ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
102                 | ExprKind::Field(..)
103                 | ExprKind::Index(..) => true,
104                 _ => false,
105             },
106             // local binding capturing a reference
107             Some(Node::Local(l)) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
108                 return;
109             },
110             _ => false,
111         };
112
113         let mut app = Applicability::MachineApplicable;
114         let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0;
115
116         let deref_count = arg_adjustments
117             .iter()
118             .take_while(|adj| matches!(adj.kind, Adjust::Deref(_)))
119             .count();
120         let (help, sugg) = if deref_count == 0 {
121             ("try removing the `clone` call", snip.into())
122         } else if parent_is_suffix_expr {
123             ("try dereferencing it", format!("({}{snip})", "*".repeat(deref_count)))
124         } else {
125             ("try dereferencing it", format!("{}{snip}", "*".repeat(deref_count)))
126         };
127
128         span_lint_and_sugg(
129             cx,
130             CLONE_ON_COPY,
131             expr.span,
132             &with_forced_trimmed_paths!(format!(
133                 "using `clone` on type `{ty}` which implements the `Copy` trait"
134             )),
135             help,
136             sugg,
137             app,
138         );
139     }
140 }