mirror of
https://github.com/rust-lang/rust
synced 2024-11-05 20:45:15 +00:00
Tweak .clone()
suggestion to work in more cases
When going through auto-deref, the `<T as Clone>` impl sometimes needs to be specified for rustc to actually clone the value and not the reference. ``` error[E0507]: cannot move out of dereference of `S` --> $DIR/needs-clone-through-deref.rs:15:18 | LL | for _ in self.clone().into_iter() {} | ^^^^^^^^^^^^ ----------- value moved due to this method call | | | move occurs because value has type `Vec<usize>`, which does not implement the `Copy` trait | note: `into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | LL | for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} | ++++++++++++++++++++++++++++++ + ``` CC #109429.
This commit is contained in:
parent
0e2dac8375
commit
03c88aaf21
12 changed files with 86 additions and 18 deletions
|
@ -1135,11 +1135,25 @@ fn explain_captures(
|
|||
)
|
||||
&& self.infcx.predicate_must_hold_modulo_regions(&o)
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
move_span.shrink_to_hi(),
|
||||
let sugg = if moved_place
|
||||
.iter_projections()
|
||||
.any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
|
||||
{
|
||||
vec![
|
||||
// We use the fully-qualified path because `.clone()` can
|
||||
// sometimes choose `<&T as Clone>` instead of `<T as Clone>`
|
||||
// when going through auto-deref, so this ensures that doesn't
|
||||
// happen, causing suggestions for `.clone().clone()`.
|
||||
(move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
|
||||
(move_span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
} else {
|
||||
vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
|
||||
};
|
||||
err.multipart_suggestion_verbose(
|
||||
"you can `clone` the value and consume it, but this might not be \
|
||||
your desired behavior",
|
||||
".clone()".to_string(),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value
|
|||
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | let _x = Rc::new(vec![1, 2]).clone().into_iter();
|
||||
| ++++++++
|
||||
LL | let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
|
||||
| ++++++++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@ impl Foo {
|
|||
}
|
||||
fn main() {
|
||||
let foo = &Foo;
|
||||
(*foo).clone().foo(); //~ ERROR cannot move out
|
||||
<Foo as Clone>::clone(&(*foo)).foo(); //~ ERROR cannot move out
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ LL | fn foo(self) {}
|
|||
| ^^^^
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | (*foo).clone().foo();
|
||||
| ++++++++
|
||||
LL | <Foo as Clone>::clone(&(*foo)).foo();
|
||||
| +++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ fn call<F>(f: F) where F : Fn() {
|
|||
fn main() {
|
||||
let y = vec![format!("World")];
|
||||
call(|| {
|
||||
y.clone().into_iter();
|
||||
<Vec<String> as Clone>::clone(&y).into_iter();
|
||||
//~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves `y`
|
|||
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | y.clone().into_iter();
|
||||
| ++++++++
|
||||
LL | <Vec<String> as Clone>::clone(&y).into_iter();
|
||||
| +++++++++++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
18
tests/ui/moves/needs-clone-through-deref.fixed
Normal file
18
tests/ui/moves/needs-clone-through-deref.fixed
Normal file
|
@ -0,0 +1,18 @@
|
|||
// run-rustfix
|
||||
#![allow(dead_code, noop_method_call)]
|
||||
use std::ops::Deref;
|
||||
struct S(Vec<usize>);
|
||||
impl Deref for S {
|
||||
type Target = Vec<usize>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl S {
|
||||
fn foo(&self) {
|
||||
// `self.clone()` returns `&S`, not `Vec`
|
||||
for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} //~ ERROR cannot move out of dereference of `S`
|
||||
}
|
||||
}
|
||||
fn main() {}
|
18
tests/ui/moves/needs-clone-through-deref.rs
Normal file
18
tests/ui/moves/needs-clone-through-deref.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
// run-rustfix
|
||||
#![allow(dead_code, noop_method_call)]
|
||||
use std::ops::Deref;
|
||||
struct S(Vec<usize>);
|
||||
impl Deref for S {
|
||||
type Target = Vec<usize>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl S {
|
||||
fn foo(&self) {
|
||||
// `self.clone()` returns `&S`, not `Vec`
|
||||
for _ in self.clone().into_iter() {} //~ ERROR cannot move out of dereference of `S`
|
||||
}
|
||||
}
|
||||
fn main() {}
|
18
tests/ui/moves/needs-clone-through-deref.stderr
Normal file
18
tests/ui/moves/needs-clone-through-deref.stderr
Normal file
|
@ -0,0 +1,18 @@
|
|||
error[E0507]: cannot move out of dereference of `S`
|
||||
--> $DIR/needs-clone-through-deref.rs:15:18
|
||||
|
|
||||
LL | for _ in self.clone().into_iter() {}
|
||||
| ^^^^^^^^^^^^ ----------- value moved due to this method call
|
||||
| |
|
||||
| move occurs because value has type `Vec<usize>`, which does not implement the `Copy` trait
|
||||
|
|
||||
note: `into_iter` takes ownership of the receiver `self`, which moves value
|
||||
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {}
|
||||
| ++++++++++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0507`.
|
|
@ -7,5 +7,5 @@ impl Foo {
|
|||
}
|
||||
fn main() {
|
||||
let foo = &Foo;
|
||||
foo.clone().foo(); //~ ERROR cannot move out
|
||||
<Foo as Clone>::clone(&foo).foo(); //~ ERROR cannot move out
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ LL | fn foo(self) {}
|
|||
| ^^^^
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | foo.clone().foo();
|
||||
| ++++++++
|
||||
LL | <Foo as Clone>::clone(&foo).foo();
|
||||
| +++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves
|
|||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | if selection.1.clone().unwrap().contains(selection.0) {
|
||||
| ++++++++
|
||||
LL | if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
|
||||
| ++++++++++++++++++++++++++++++++++ +
|
||||
|
||||
error[E0507]: cannot move out of `selection.1` which is behind a shared reference
|
||||
--> $DIR/option-content-move.rs:27:20
|
||||
|
@ -27,8 +27,8 @@ note: `Result::<T, E>::unwrap` takes ownership of the receiver `self`, which mov
|
|||
--> $SRC_DIR/core/src/result.rs:LL:COL
|
||||
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
||||
|
|
||||
LL | if selection.1.clone().unwrap().contains(selection.0) {
|
||||
| ++++++++
|
||||
LL | if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
|
||||
| ++++++++++++++++++++++++++++++++++++++++++ +
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue