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};
10 use rustc_span::symbol::{sym, Symbol};
12 use super::CLONE_DOUBLE_REF;
13 use super::CLONE_ON_COPY;
15 /// Checks for the `CLONE_ON_COPY` lint.
16 #[allow(clippy::too_many_lines)]
24 let arg = if method_name == sym::clone && args.is_empty() {
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)
38 let arg_adjustments = cx.typeck_results().expr_adjustments(arg);
39 let arg_ty = arg_adjustments
41 .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
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() {
51 "using `clone` on a double-reference; \
52 this will copy the reference of type `{ty}` instead of cloning the inner type"
55 if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
56 let mut ty = innermost;
58 while let ty::Ref(_, inner, _) = ty.kind() {
62 let refs = "&".repeat(n + 1);
63 let derefs = "*".repeat(n);
64 let explicit = format!("<{refs}{ty}>::clone({snip})");
67 "try dereferencing it",
68 format!("{refs}({derefs}{}).clone()", snip.deref()),
69 Applicability::MaybeIncorrect,
73 "or try being explicit if you are sure, that you want to clone a reference",
75 Applicability::MaybeIncorrect,
80 return; // don't report clone_on_copy
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) =>
95 // ? is a Call, makes sure not to rec *x?, but rather (*x)?
96 ExprKind::Call(hir_callee, _) => matches!(
98 ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
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,
106 // local binding capturing a reference
107 Some(Node::Local(l)) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
113 let mut app = Applicability::MachineApplicable;
114 let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0;
116 let deref_count = arg_adjustments
118 .take_while(|adj| matches!(adj.kind, Adjust::Deref(_)))
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)))
125 ("try dereferencing it", format!("{}{snip}", "*".repeat(deref_count)))
132 &format!("using `clone` on type `{ty}` which implements the `Copy` trait"),