|
1 |
| -use clippy_utils::diagnostics::span_lint_and_then; |
| 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; |
2 | 4 | use clippy_utils::sugg;
|
3 | 5 | use clippy_utils::ty::is_copy;
|
4 | 6 | use rustc_errors::Applicability;
|
5 |
| -use rustc_hir as hir; |
| 7 | +use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; |
6 | 8 | use rustc_lint::LateContext;
|
7 |
| -use rustc_middle::ty; |
| 9 | +use rustc_middle::ty::{self, adjustment::Adjust}; |
8 | 10 | use rustc_span::symbol::{sym, Symbol};
|
9 | 11 | use std::iter;
|
10 | 12 |
|
11 | 13 | use super::CLONE_DOUBLE_REF;
|
12 | 14 | use super::CLONE_ON_COPY;
|
13 | 15 |
|
14 | 16 | /// Checks for the `CLONE_ON_COPY` lint.
|
15 |
| -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { |
16 |
| - if !(args.len() == 1 && method_name == sym::clone) { |
| 17 | +#[allow(clippy::too_many_lines)] |
| 18 | +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) { |
| 19 | + let arg = match args { |
| 20 | + [arg] if method_name == sym::clone => arg, |
| 21 | + _ => return, |
| 22 | + }; |
| 23 | + if cx |
| 24 | + .typeck_results() |
| 25 | + .type_dependent_def_id(expr.hir_id) |
| 26 | + .and_then(|id| cx.tcx.trait_of_item(id)) |
| 27 | + .zip(cx.tcx.lang_items().clone_trait()) |
| 28 | + .map_or(true, |(x, y)| x != y) |
| 29 | + { |
17 | 30 | return;
|
18 | 31 | }
|
19 |
| - let arg = &args[0]; |
20 |
| - let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); |
| 32 | + let arg_adjustments = cx.typeck_results().expr_adjustments(arg); |
| 33 | + let arg_ty = arg_adjustments |
| 34 | + .last() |
| 35 | + .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); |
| 36 | + |
21 | 37 | let ty = cx.typeck_results().expr_ty(expr);
|
22 | 38 | if let ty::Ref(_, inner, _) = arg_ty.kind() {
|
23 | 39 | if let ty::Ref(_, innermost, _) = inner.kind() {
|
@@ -61,57 +77,57 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Sym
|
61 | 77 | }
|
62 | 78 |
|
63 | 79 | if is_copy(cx, ty) {
|
64 |
| - let snip; |
65 |
| - if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { |
66 |
| - let parent = cx.tcx.hir().get_parent_node(expr.hir_id); |
67 |
| - match &cx.tcx.hir().get(parent) { |
68 |
| - hir::Node::Expr(parent) => match parent.kind { |
69 |
| - // &*x is a nop, &x.clone() is not |
70 |
| - hir::ExprKind::AddrOf(..) => return, |
71 |
| - // (*x).func() is useless, x.clone().func() can work in case func borrows mutably |
72 |
| - hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { |
73 |
| - return; |
74 |
| - }, |
75 |
| - |
76 |
| - _ => {}, |
77 |
| - }, |
78 |
| - hir::Node::Stmt(stmt) => { |
79 |
| - if let hir::StmtKind::Local(ref loc) = stmt.kind { |
80 |
| - if let hir::PatKind::Ref(..) = loc.pat.kind { |
81 |
| - // let ref y = *x borrows x, let ref y = x.clone() does not |
82 |
| - return; |
83 |
| - } |
84 |
| - } |
85 |
| - }, |
86 |
| - _ => {}, |
| 80 | + let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) { |
| 81 | + Some(Node::Expr(parent)) => match parent.kind { |
| 82 | + // &*x is a nop, &x.clone() is not |
| 83 | + ExprKind::AddrOf(..) => return, |
| 84 | + // (*x).func() is useless, x.clone().func() can work in case func borrows self |
| 85 | + ExprKind::MethodCall(_, _, [self_arg, ..], _) |
| 86 | + if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) => |
| 87 | + { |
| 88 | + return; |
| 89 | + } |
| 90 | + ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true, |
| 91 | + ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) |
| 92 | + | ExprKind::Field(..) |
| 93 | + | ExprKind::Index(..) => true, |
| 94 | + _ => false, |
| 95 | + }, |
| 96 | + // local binding capturing a reference |
| 97 | + Some(Node::Local(l)) |
| 98 | + if matches!( |
| 99 | + l.pat.kind, |
| 100 | + PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) |
| 101 | + ) => |
| 102 | + { |
| 103 | + return; |
87 | 104 | }
|
| 105 | + _ => false, |
| 106 | + }; |
88 | 107 |
|
89 |
| - // x.clone() might have dereferenced x, possibly through Deref impls |
90 |
| - if cx.typeck_results().expr_ty(arg) == ty { |
91 |
| - snip = Some(("try removing the `clone` call", format!("{}", snippet))); |
92 |
| - } else { |
93 |
| - let deref_count = cx |
94 |
| - .typeck_results() |
95 |
| - .expr_adjustments(arg) |
96 |
| - .iter() |
97 |
| - .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) |
98 |
| - .count(); |
99 |
| - let derefs: String = iter::repeat('*').take(deref_count).collect(); |
100 |
| - snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); |
101 |
| - } |
| 108 | + let mut app = Applicability::MachineApplicable; |
| 109 | + let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; |
| 110 | + |
| 111 | + let deref_count = arg_adjustments |
| 112 | + .iter() |
| 113 | + .take_while(|adj| matches!(adj.kind, Adjust::Deref(_))) |
| 114 | + .count(); |
| 115 | + let (help, sugg) = if deref_count == 0 { |
| 116 | + ("try removing the `clone` call", snip.into()) |
| 117 | + } else if parent_is_suffix_expr { |
| 118 | + ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) |
102 | 119 | } else {
|
103 |
| - snip = None; |
104 |
| - } |
105 |
| - span_lint_and_then( |
| 120 | + ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) |
| 121 | + }; |
| 122 | + |
| 123 | + span_lint_and_sugg( |
106 | 124 | cx,
|
107 | 125 | CLONE_ON_COPY,
|
108 | 126 | expr.span,
|
109 | 127 | &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
|
110 |
| - |diag| { |
111 |
| - if let Some((text, snip)) = snip { |
112 |
| - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); |
113 |
| - } |
114 |
| - }, |
| 128 | + help, |
| 129 | + sugg, |
| 130 | + app, |
115 | 131 | );
|
116 | 132 | }
|
117 | 133 | }
|
0 commit comments