Skip to content

Commit 0552852

Browse files
committed
Auto merge of rust-lang#7000 - Jarcho:clone_on_copy_fp, r=llogiq
Improve `clone_on_copy` This also removes the `clone_on_copy_mut` test as the same thing is covered in the `clone_on_copy` test. changelog: `copy_on_clone` lint on chained method calls taking self by value changelog: `copy_on_clone` only lint when using the `Clone` trait changelog: `copy_on_clone` correct suggestion when the cloned value is a macro call.
2 parents 1ddeaa6 + d265776 commit 0552852

File tree

5 files changed

+157
-77
lines changed

5 files changed

+157
-77
lines changed
Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
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;
24
use clippy_utils::sugg;
35
use clippy_utils::ty::is_copy;
46
use rustc_errors::Applicability;
5-
use rustc_hir as hir;
7+
use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind};
68
use rustc_lint::LateContext;
7-
use rustc_middle::ty;
9+
use rustc_middle::ty::{self, adjustment::Adjust};
810
use rustc_span::symbol::{sym, Symbol};
911
use std::iter;
1012

1113
use super::CLONE_DOUBLE_REF;
1214
use super::CLONE_ON_COPY;
1315

1416
/// 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+
{
1730
return;
1831
}
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+
2137
let ty = cx.typeck_results().expr_ty(expr);
2238
if let ty::Ref(_, inner, _) = arg_ty.kind() {
2339
if let ty::Ref(_, innermost, _) = inner.kind() {
@@ -61,57 +77,57 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Sym
6177
}
6278

6379
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;
87104
}
105+
_ => false,
106+
};
88107

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))
102119
} 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(
106124
cx,
107125
CLONE_ON_COPY,
108126
expr.span,
109127
&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,
115131
);
116132
}
117133
}

tests/ui/clone_on_copy.fixed

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
clippy::deref_addrof,
77
clippy::no_effect,
88
clippy::unnecessary_operation,
9-
clippy::vec_init_then_push
9+
clippy::vec_init_then_push,
10+
clippy::toplevel_ref_arg
1011
)]
1112

1213
use std::cell::RefCell;
@@ -29,6 +30,37 @@ fn clone_on_copy() {
2930
let rc = RefCell::new(0);
3031
*rc.borrow();
3132

33+
let x = 0u32;
34+
x.rotate_left(1);
35+
36+
#[derive(Clone, Copy)]
37+
struct Foo;
38+
impl Foo {
39+
fn clone(&self) -> u32 {
40+
0
41+
}
42+
}
43+
Foo.clone(); // ok, this is not the clone trait
44+
45+
macro_rules! m {
46+
($e:expr) => {{ $e }};
47+
}
48+
m!(42);
49+
50+
struct Wrap([u32; 2]);
51+
impl core::ops::Deref for Wrap {
52+
type Target = [u32; 2];
53+
fn deref(&self) -> &[u32; 2] {
54+
&self.0
55+
}
56+
}
57+
let x = Wrap([0, 0]);
58+
(*x)[0];
59+
60+
let x = 42;
61+
let ref y = x.clone(); // ok, binds by reference
62+
let ref mut y = x.clone(); // ok, binds by reference
63+
3264
// Issue #4348
3365
let mut x = 43;
3466
let _ = &x.clone(); // ok, getting a ref

tests/ui/clone_on_copy.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
clippy::deref_addrof,
77
clippy::no_effect,
88
clippy::unnecessary_operation,
9-
clippy::vec_init_then_push
9+
clippy::vec_init_then_push,
10+
clippy::toplevel_ref_arg
1011
)]
1112

1213
use std::cell::RefCell;
@@ -29,6 +30,37 @@ fn clone_on_copy() {
2930
let rc = RefCell::new(0);
3031
rc.borrow().clone();
3132

33+
let x = 0u32;
34+
x.clone().rotate_left(1);
35+
36+
#[derive(Clone, Copy)]
37+
struct Foo;
38+
impl Foo {
39+
fn clone(&self) -> u32 {
40+
0
41+
}
42+
}
43+
Foo.clone(); // ok, this is not the clone trait
44+
45+
macro_rules! m {
46+
($e:expr) => {{ $e }};
47+
}
48+
m!(42).clone();
49+
50+
struct Wrap([u32; 2]);
51+
impl core::ops::Deref for Wrap {
52+
type Target = [u32; 2];
53+
fn deref(&self) -> &[u32; 2] {
54+
&self.0
55+
}
56+
}
57+
let x = Wrap([0, 0]);
58+
x.clone()[0];
59+
60+
let x = 42;
61+
let ref y = x.clone(); // ok, binds by reference
62+
let ref mut y = x.clone(); // ok, binds by reference
63+
3264
// Issue #4348
3365
let mut x = 43;
3466
let _ = &x.clone(); // ok, getting a ref

tests/ui/clone_on_copy.stderr

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,52 @@
11
error: using `clone` on type `i32` which implements the `Copy` trait
2-
--> $DIR/clone_on_copy.rs:23:5
2+
--> $DIR/clone_on_copy.rs:24:5
33
|
44
LL | 42.clone();
55
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
66
|
77
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
88

99
error: using `clone` on type `i32` which implements the `Copy` trait
10-
--> $DIR/clone_on_copy.rs:27:5
10+
--> $DIR/clone_on_copy.rs:28:5
1111
|
1212
LL | (&42).clone();
1313
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
1414

1515
error: using `clone` on type `i32` which implements the `Copy` trait
16-
--> $DIR/clone_on_copy.rs:30:5
16+
--> $DIR/clone_on_copy.rs:31:5
1717
|
1818
LL | rc.borrow().clone();
1919
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
2020

21+
error: using `clone` on type `u32` which implements the `Copy` trait
22+
--> $DIR/clone_on_copy.rs:34:5
23+
|
24+
LL | x.clone().rotate_left(1);
25+
| ^^^^^^^^^ help: try removing the `clone` call: `x`
26+
27+
error: using `clone` on type `i32` which implements the `Copy` trait
28+
--> $DIR/clone_on_copy.rs:48:5
29+
|
30+
LL | m!(42).clone();
31+
| ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)`
32+
33+
error: using `clone` on type `[u32; 2]` which implements the `Copy` trait
34+
--> $DIR/clone_on_copy.rs:58:5
35+
|
36+
LL | x.clone()[0];
37+
| ^^^^^^^^^ help: try dereferencing it: `(*x)`
38+
2139
error: using `clone` on type `char` which implements the `Copy` trait
22-
--> $DIR/clone_on_copy.rs:36:14
40+
--> $DIR/clone_on_copy.rs:68:14
2341
|
2442
LL | is_ascii('z'.clone());
2543
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
2644

2745
error: using `clone` on type `i32` which implements the `Copy` trait
28-
--> $DIR/clone_on_copy.rs:40:14
46+
--> $DIR/clone_on_copy.rs:72:14
2947
|
3048
LL | vec.push(42.clone());
3149
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
3250

33-
error: aborting due to 5 previous errors
51+
error: aborting due to 8 previous errors
3452

tests/ui/clone_on_copy_mut.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)