Skip to content

Commit a929316

Browse files
committed
Suggest .clone() on method call move errors
1 parent c79db9c commit a929316

File tree

6 files changed

+80
-9
lines changed

6 files changed

+80
-9
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3447,6 +3447,7 @@ dependencies = [
34473447
"rustc_errors",
34483448
"rustc_graphviz",
34493449
"rustc_hir",
3450+
"rustc_hir_analysis",
34503451
"rustc_index",
34513452
"rustc_infer",
34523453
"rustc_lexer",

compiler/rustc_borrowck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
1515
rustc_errors = { path = "../rustc_errors" }
1616
rustc_graphviz = { path = "../rustc_graphviz" }
1717
rustc_hir = { path = "../rustc_hir" }
18+
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
1819
rustc_index = { path = "../rustc_index" }
1920
rustc_infer = { path = "../rustc_infer" }
2021
rustc_lexer = { path = "../rustc_lexer" }

compiler/rustc_borrowck/src/diagnostics/mod.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_errors::{Applicability, Diagnostic};
66
use rustc_hir as hir;
77
use rustc_hir::def::{CtorKind, Namespace};
88
use rustc_hir::GeneratorKind;
9+
use rustc_hir_analysis::hir_ty_to_ty;
910
use rustc_infer::infer::TyCtxtInferExt;
1011
use rustc_middle::mir::tcx::PlaceTy;
1112
use rustc_middle::mir::{
@@ -1066,18 +1067,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10661067
}
10671068
CallKind::Normal { self_arg, desugaring, method_did } => {
10681069
let self_arg = self_arg.unwrap();
1070+
let tcx = self.infcx.tcx;
10691071
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
1070-
let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
1071-
let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) {
1072+
let ty = moved_place.ty(self.body, tcx).ty;
1073+
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
10721074
Some(def_id) => {
10731075
let infcx = self.infcx.tcx.infer_ctxt().build();
10741076
type_known_to_meet_bound_modulo_regions(
10751077
&infcx,
10761078
self.param_env,
1077-
infcx.tcx.mk_imm_ref(
1078-
infcx.tcx.lifetimes.re_erased,
1079-
infcx.tcx.erase_regions(ty),
1080-
),
1079+
tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)),
10811080
def_id,
10821081
DUMMY_SP,
10831082
)
@@ -1133,8 +1132,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11331132
place_name, partially_str, loop_message
11341133
),
11351134
);
1136-
if let ty::Adt(def, ..)
1137-
= moved_place.ty(self.body, self.infcx.tcx).ty.kind()
1135+
let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
1136+
if let ty::Adt(def, ..) = ty.kind()
11381137
&& Some(def.did()) == self.infcx.tcx.lang_items().pin_type()
11391138
{
11401139
err.span_suggestion_verbose(
@@ -1144,8 +1143,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11441143
Applicability::MaybeIncorrect,
11451144
);
11461145
}
1146+
if let Some(clone_trait) = tcx.lang_items().clone_trait() {
1147+
// We can't use `predicate_may_hold` or `can_eq` without ICEs in
1148+
// borrowck because of the inference context, so we do a poor-man's
1149+
// version here.
1150+
for impl_def_id in tcx.all_impls(clone_trait) {
1151+
if let Some(def_id) = impl_def_id.as_local()
1152+
&& let hir_id = tcx.hir().local_def_id_to_hir_id(def_id)
1153+
&& let hir::Node::Item(hir::Item {
1154+
kind: hir::ItemKind::Impl(hir::Impl {
1155+
self_ty,
1156+
..
1157+
}),
1158+
..
1159+
}) = tcx.hir().get(hir_id)
1160+
{
1161+
if ty == hir_ty_to_ty(tcx, self_ty) {
1162+
err.span_suggestion_verbose(
1163+
fn_call_span.shrink_to_lo(),
1164+
"you can `clone` the value and consume it, but this \
1165+
might not be your desired behavior",
1166+
"clone().".to_string(),
1167+
Applicability::MaybeIncorrect,
1168+
);
1169+
}
1170+
}
1171+
}
1172+
}
11471173
}
1148-
let tcx = self.infcx.tcx;
11491174
// Avoid pointing to the same function in multiple different
11501175
// error messages.
11511176
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {

src/test/ui/moves/suggest-clone.fixed

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// run-rustfix
2+
3+
#[derive(Clone)]
4+
struct Foo;
5+
impl Foo {
6+
fn foo(self) {}
7+
}
8+
fn main() {
9+
let foo = &Foo;
10+
foo.clone().foo(); //~ ERROR cannot move out
11+
}

src/test/ui/moves/suggest-clone.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// run-rustfix
2+
3+
#[derive(Clone)]
4+
struct Foo;
5+
impl Foo {
6+
fn foo(self) {}
7+
}
8+
fn main() {
9+
let foo = &Foo;
10+
foo.foo(); //~ ERROR cannot move out
11+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0507]: cannot move out of `*foo` which is behind a shared reference
2+
--> $DIR/suggest-clone.rs:10:5
3+
|
4+
LL | foo.foo();
5+
| ^^^^-----
6+
| | |
7+
| | `*foo` moved due to this method call
8+
| move occurs because `*foo` has type `Foo`, which does not implement the `Copy` trait
9+
|
10+
note: `Foo::foo` takes ownership of the receiver `self`, which moves `*foo`
11+
--> $DIR/suggest-clone.rs:6:12
12+
|
13+
LL | fn foo(self) {}
14+
| ^^^^
15+
help: you can `clone` the value and consume it, but this might not be your desired behavior
16+
|
17+
LL | foo.clone().foo();
18+
| ++++++++
19+
20+
error: aborting due to previous error
21+
22+
For more information about this error, try `rustc --explain E0507`.

0 commit comments

Comments
 (0)