use crate::utils::{is_copy, span_lint_and_then, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use std::iter; use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() { if let ty::Ref(_, innermost, _) = inner.kind() { span_lint_and_then( cx, CLONE_DOUBLE_REF, expr.span, &format!( "using `clone` on a double-reference; \ this will copy the reference of type `{}` instead of cloning the inner type", ty ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; let mut n = 0; while let ty::Ref(_, inner, _) = ty.kind() { ty = inner; n += 1; } let refs: String = iter::repeat('&').take(n + 1).collect(); let derefs: String = iter::repeat('*').take(n).collect(); let explicit = format!("<{}{}>::clone({})", refs, ty, snip); diag.span_suggestion( expr.span, "try dereferencing it", format!("{}({}{}).clone()", refs, derefs, snip.deref()), Applicability::MaybeIncorrect, ); diag.span_suggestion( expr.span, "or try being explicit if you are sure, that you want to clone a reference", explicit, Applicability::MaybeIncorrect, ); } }, ); return; // don't report clone_on_copy } } if is_copy(cx, ty) { let snip; if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { let parent = cx.tcx.hir().get_parent_node(expr.hir_id); match &cx.tcx.hir().get(parent) { hir::Node::Expr(parent) => match parent.kind { // &*x is a nop, &x.clone() is not hir::ExprKind::AddrOf(..) => return, // (*x).func() is useless, x.clone().func() can work in case func borrows mutably hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { return; }, _ => {}, }, hir::Node::Stmt(stmt) => { if let hir::StmtKind::Local(ref loc) = stmt.kind { if let hir::PatKind::Ref(..) = loc.pat.kind { // let ref y = *x borrows x, let ref y = x.clone() does not return; } } }, _ => {}, } // x.clone() might have dereferenced x, possibly through Deref impls if cx.typeck_results().expr_ty(arg) == ty { snip = Some(("try removing the `clone` call", format!("{}", snippet))); } else { let deref_count = cx .typeck_results() .expr_adjustments(arg) .iter() .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) .count(); let derefs: String = iter::repeat('*').take(deref_count).collect(); snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); } } else { snip = None; } span_lint_and_then( cx, CLONE_ON_COPY, expr.span, &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), |diag| { if let Some((text, snip)) = snip { diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); } }, ); } }