Skip to content

Commit

Permalink
Suggest .clone() on method call move errors
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Dec 23, 2022
1 parent c79db9c commit a929316
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3447,6 +3447,7 @@ dependencies = [
"rustc_errors",
"rustc_graphviz",
"rustc_hir",
"rustc_hir_analysis",
"rustc_index",
"rustc_infer",
"rustc_lexer",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_lexer = { path = "../rustc_lexer" }
Expand Down
43 changes: 34 additions & 9 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::GeneratorKind;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
Expand Down Expand Up @@ -1066,18 +1067,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
CallKind::Normal { self_arg, desugaring, method_did } => {
let self_arg = self_arg.unwrap();
let tcx = self.infcx.tcx;
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) {
let ty = moved_place.ty(self.body, tcx).ty;
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
Some(def_id) => {
let infcx = self.infcx.tcx.infer_ctxt().build();
type_known_to_meet_bound_modulo_regions(
&infcx,
self.param_env,
infcx.tcx.mk_imm_ref(
infcx.tcx.lifetimes.re_erased,
infcx.tcx.erase_regions(ty),
),
tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.erase_regions(ty)),
def_id,
DUMMY_SP,
)
Expand Down Expand Up @@ -1133,8 +1132,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place_name, partially_str, loop_message
),
);
if let ty::Adt(def, ..)
= moved_place.ty(self.body, self.infcx.tcx).ty.kind()
let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
if let ty::Adt(def, ..) = ty.kind()
&& Some(def.did()) == self.infcx.tcx.lang_items().pin_type()
{
err.span_suggestion_verbose(
Expand All @@ -1144,8 +1143,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Applicability::MaybeIncorrect,
);
}
if let Some(clone_trait) = tcx.lang_items().clone_trait() {
// We can't use `predicate_may_hold` or `can_eq` without ICEs in
// borrowck because of the inference context, so we do a poor-man's
// version here.
for impl_def_id in tcx.all_impls(clone_trait) {
if let Some(def_id) = impl_def_id.as_local()
&& let hir_id = tcx.hir().local_def_id_to_hir_id(def_id)
&& let hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl {
self_ty,
..
}),
..
}) = tcx.hir().get(hir_id)
{
if ty == hir_ty_to_ty(tcx, self_ty) {
err.span_suggestion_verbose(
fn_call_span.shrink_to_lo(),
"you can `clone` the value and consume it, but this \
might not be your desired behavior",
"clone().".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
}
}
let tcx = self.infcx.tcx;
// Avoid pointing to the same function in multiple different
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/moves/suggest-clone.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// run-rustfix

#[derive(Clone)]
struct Foo;
impl Foo {
fn foo(self) {}
}
fn main() {
let foo = &Foo;
foo.clone().foo(); //~ ERROR cannot move out
}
11 changes: 11 additions & 0 deletions src/test/ui/moves/suggest-clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// run-rustfix

#[derive(Clone)]
struct Foo;
impl Foo {
fn foo(self) {}
}
fn main() {
let foo = &Foo;
foo.foo(); //~ ERROR cannot move out
}
22 changes: 22 additions & 0 deletions src/test/ui/moves/suggest-clone.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0507]: cannot move out of `*foo` which is behind a shared reference
--> $DIR/suggest-clone.rs:10:5
|
LL | foo.foo();
| ^^^^-----
| | |
| | `*foo` moved due to this method call
| move occurs because `*foo` has type `Foo`, which does not implement the `Copy` trait
|
note: `Foo::foo` takes ownership of the receiver `self`, which moves `*foo`
--> $DIR/suggest-clone.rs:6:12
|
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();
| ++++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0507`.

0 comments on commit a929316

Please sign in to comment.