Auto merge of #118076 - estebank:issue-109429, r=davidtwco

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() {}
   |                  ++++++++++++++++++++++++++++++            +
```

When encountering a move error, look for implementations of `Clone` for the moved type. If there is one, check if all its obligations are met. If they are, we suggest cloning without caveats. If they aren't, we suggest cloning while mentioning the unmet obligations, potentially suggesting `#[derive(Clone)]` when appropriate.

```
error[E0507]: cannot move out of a shared reference
  --> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:20:28
   |
LL |     let mut copy: Vec<U> = map.clone().into_values().collect();
   |                            ^^^^^^^^^^^ ------------- value moved due to this method call
   |                            |
   |                            move occurs because value has type `HashMap<T, U, Hash128_1>`, which does not implement the `Copy` trait
   |
note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
  --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
   |
LL |     let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect();
   |                            ++++++++++++++++++++++++++++++++++++++++++++           +
help: consider annotating `Hash128_1` with `#[derive(Clone)]`
   |
LL + #[derive(Clone)]
LL | pub struct Hash128_1;
   |
```

Fix #109429.

When encountering multiple mutable borrows, suggest cloning and adding
derive annotations as needed.

```
error[E0596]: cannot borrow `sm.x` as mutable, as it is behind a `&` reference
  --> $DIR/accidentally-cloning-ref-borrow-error.rs:32:9
   |
LL |     foo(&mut sm.x);
   |         ^^^^^^^^^ `sm` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |
help: `Str` doesn't implement `Clone`, so this call clones the reference `&Str`
  --> $DIR/accidentally-cloning-ref-borrow-error.rs:31:21
   |
LL |     let mut sm = sr.clone();
   |                     ^^^^^^^
help: consider annotating `Str` with `#[derive(Clone)]`
   |
LL + #[derive(Clone)]
LL | struct Str {
   |
help: consider specifying this binding's type
   |
LL |     let mut sm: &mut Str = sr.clone();
   |               ++++++++++
```

Fix #34629. Fix #76643. Fix #91532.
This commit is contained in:
bors 2023-12-05 06:34:44 +00:00
commit f67523d0b3
39 changed files with 800 additions and 97 deletions

View file

@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::CoroutineKind;
use rustc_index::IndexSlice;
use rustc_infer::infer::BoundRegionConversionTime;
use rustc_infer::traits::{FulfillmentErrorCode, SelectionError};
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
use rustc_span::def_id::LocalDefId;
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
use super::borrow_set::BorrowData;
use super::MirBorrowckCtxt;
@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
let self_arg = self_arg.unwrap();
let mut has_sugg = false;
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) {
self.explain_iterator_advancement_in_for_loop_if_applicable(
err,
span,
&move_spans,
);
let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
.then_some(parent_did)
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
});
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
});
if is_option_or_result && maybe_reinitialized_locations_is_empty {
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
}
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
let ty = moved_place.ty(self.body, tcx).ty;
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// Erase and shadow everything that could be passed to the new infcx.
let ty = moved_place.ty(self.body, tcx).ty;
if let ty::Adt(def, args) = ty.kind()
if let ty::Adt(def, args) = ty.peel_refs().kind()
&& Some(def.did()) == tcx.lang_items().pin_type()
&& let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
&& let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
span: move_span.shrink_to_hi(),
},
);
has_sugg = true;
}
if let Some(clone_trait) = tcx.lang_items().clone_trait()
&& let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
&& let o = Obligation::new(
tcx,
ObligationCause::dummy(),
self.param_env,
ty::Binder::dummy(trait_ref),
)
&& self.infcx.predicate_must_hold_modulo_regions(&o)
{
err.span_suggestion_verbose(
move_span.shrink_to_hi(),
"you can `clone` the value and consume it, but this might not be \
your desired behavior",
".clone()".to_string(),
Applicability::MaybeIncorrect,
);
if let Some(clone_trait) = tcx.lang_items().clone_trait() {
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())]
};
if let Some(errors) =
self.infcx.could_impl_trait(clone_trait, ty, self.param_env)
&& !has_sugg
{
let msg = match &errors[..] {
[] => "you can `clone` the value and consume it, but this \
might not be your desired behavior"
.to_string(),
[error] => {
format!(
"you could `clone` the value and consume it, if \
the `{}` trait bound could be satisfied",
error.obligation.predicate,
)
}
[errors @ .., last] => {
format!(
"you could `clone` the value and consume it, if \
the following trait bounds could be satisfied: {} \
and `{}`",
errors
.iter()
.map(|e| format!("`{}`", e.obligation.predicate))
.collect::<Vec<_>>()
.join(", "),
last.obligation.predicate,
)
}
};
err.multipart_suggestion_verbose(
msg,
sugg.clone(),
Applicability::MaybeIncorrect,
);
for error in errors {
if let FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
) = error.code
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
pred,
)) = error.obligation.predicate.kind().skip_binder()
{
self.infcx.err_ctxt().suggest_derive(
&error.obligation,
err,
error.obligation.predicate.kind().rebind(pred),
);
}
}
}
}
}
// Avoid pointing to the same function in multiple different
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
self.explain_iterator_advancement_in_for_loop_if_applicable(
err,
span,
&move_spans,
);
let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name,
span: self_arg.span,
});
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
.then_some(parent_did)
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
});
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
});
if is_option_or_result && maybe_reinitialized_locations_is_empty {
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
}
}
// Other desugarings takes &self, which cannot cause a move
_ => {}

View file

@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
use rustc_infer::traits;
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
use rustc_middle::{
hir::place::PlaceBase,
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
@ -12,6 +13,8 @@ use rustc_middle::{
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, BytePos, DesugaringKind, Span};
use rustc_target::abi::FieldIdx;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use crate::diagnostics::BorrowedContentSource;
use crate::util::FindAssignments;
@ -1212,6 +1215,103 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if let Some(hir_id) = hir_id
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
{
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
&& let Some(expr) = local.init
&& let ty = tables.node_type_opt(expr.hir_id)
&& let Some(ty) = ty
&& let ty::Ref(..) = ty.kind()
{
match self
.infcx
.could_impl_trait(clone_trait, ty.peel_refs(), self.param_env)
.as_deref()
{
Some([]) => {
// The type implements Clone.
err.span_help(
expr.span,
format!(
"you can `clone` the `{}` value and consume it, but this \
might not be your desired behavior",
ty.peel_refs(),
),
);
}
None => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
expr.kind
&& segment.ident.name == sym::clone
{
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone`, so this call clones \
the reference `{ty}`",
ty.peel_refs(),
),
);
}
// The type doesn't implement Clone.
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
self.infcx.tcx,
clone_trait,
[ty.peel_refs()],
));
let obligation = traits::Obligation::new(
self.infcx.tcx,
traits::ObligationCause::dummy(),
self.param_env,
trait_ref,
);
self.infcx.err_ctxt().suggest_derive(
&obligation,
err,
trait_ref.to_predicate(self.infcx.tcx),
);
}
Some(errors) => {
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
expr.kind
&& segment.ident.name == sym::clone
{
err.span_help(
span,
format!(
"`{}` doesn't implement `Clone` because its \
implementations trait bounds could not be met, so \
this call clones the reference `{ty}`",
ty.peel_refs(),
),
);
err.note(format!(
"the following trait bounds weren't met: {}",
errors
.iter()
.map(|e| e.obligation.predicate.to_string())
.collect::<Vec<_>>()
.join("\n"),
));
}
// The type doesn't implement Clone because of unmet obligations.
for error in errors {
if let traits::FulfillmentErrorCode::CodeSelectionError(
traits::SelectionError::Unimplemented,
) = error.code
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
pred,
)) = error.obligation.predicate.kind().skip_binder()
{
self.infcx.err_ctxt().suggest_derive(
&error.obligation,
err,
error.obligation.predicate.kind().rebind(pred),
);
}
}
}
}
}
let (changing, span, sugg) = match local.ty {
Some(ty) => ("changing", ty.span, message),
None => {

View file

@ -1619,6 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None,
);
} else {
if let Some(errors) =
self.could_impl_trait(clone_trait_did, expected_ty, self.param_env)
{
match &errors[..] {
[] => {}
[error] => {
diag.help(format!(
"`Clone` is not implemented because the trait bound `{}` is \
not satisfied",
error.obligation.predicate,
));
}
[errors @ .., last] => {
diag.help(format!(
"`Clone` is not implemented because the following trait bounds \
could not be satisfied: {} and `{}`",
errors
.iter()
.map(|e| format!("`{}`", e.obligation.predicate))
.collect::<Vec<_>>()
.join(", "),
last.obligation.predicate,
));
}
}
for error in errors {
if let traits::FulfillmentErrorCode::CodeSelectionError(
traits::SelectionError::Unimplemented,
) = error.code
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
error.obligation.predicate.kind().skip_binder()
{
self.infcx.err_ctxt().suggest_derive(
&error.obligation,
diag,
error.obligation.predicate.kind().rebind(pred),
);
}
}
}
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
}
}

View file

@ -1,8 +1,10 @@
use crate::solve::FulfillmentCtxt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{self, DefiningAnchor, ObligationCtxt};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::{TraitEngine, TraitEngineExt};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
use rustc_middle::traits::query::NoSolution;
@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> {
params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
param_env: ty::ParamEnv<'tcx>,
) -> traits::EvaluationResult;
fn could_impl_trait(
&self,
trait_def_id: DefId,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<Vec<traits::FulfillmentError<'tcx>>>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
@ -76,6 +85,69 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
};
self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
}
fn could_impl_trait(
&self,
trait_def_id: DefId,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<Vec<traits::FulfillmentError<'tcx>>> {
self.probe(|_snapshot| {
if let ty::Adt(def, args) = ty.kind()
&& let Some((impl_def_id, _)) = self
.tcx
.all_impls(trait_def_id)
.filter_map(|impl_def_id| {
self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r))
})
.map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder()))
.filter(|(_, imp)| match imp.self_ty().peel_refs().kind() {
ty::Adt(i_def, _) if i_def.did() == def.did() => true,
_ => false,
})
.next()
{
let mut fulfill_cx = FulfillmentCtxt::new(self);
// We get all obligations from the impl to talk about specific
// trait bounds.
let obligations = self
.tcx
.predicates_of(impl_def_id)
.instantiate(self.tcx, args)
.into_iter()
.map(|(clause, span)| {
traits::Obligation::new(
self.tcx,
traits::ObligationCause::dummy_with_span(span),
param_env,
clause,
)
})
.collect::<Vec<_>>();
fulfill_cx.register_predicate_obligations(self, obligations);
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]);
let obligation = traits::Obligation::new(
self.tcx,
traits::ObligationCause::dummy(),
param_env,
trait_ref,
);
fulfill_cx.register_predicate_obligation(self, obligation);
let mut errors = fulfill_cx.select_all_or_error(self);
// We remove the last predicate failure, which corresponds to
// the top-level obligation, because most of the type we only
// care about the other ones, *except* when it is the only one.
// This seems to only be relevant for arbitrary self-types.
// Look at `tests/ui/moves/move-fn-self-receiver.rs`.
if errors.len() > 1 {
errors.truncate(errors.len() - 1);
}
Some(errors)
} else {
None
}
})
}
}
pub trait InferCtxtBuilderExt<'tcx> {

View file

@ -0,0 +1,38 @@
#[derive(Debug)]
struct X<T>(T);
impl<T: Clone> Clone for X<T> {
fn clone(&self) -> X<T> {
X(self.0.clone())
}
}
#[derive(Debug)]
struct Y;
#[derive(Debug)]
struct Str {
x: Option<i32>,
}
fn foo(s: &mut Option<i32>) {
if s.is_none() {
*s = Some(0);
}
println!("{:?}", s);
}
fn bar<T: std::fmt::Debug>(s: &mut X<T>) {
println!("{:?}", s);
}
fn main() {
let s = Str { x: None };
let sr = &s;
let mut sm = sr.clone();
foo(&mut sm.x); //~ ERROR cannot borrow `sm.x` as mutable, as it is behind a `&` reference
let x = X(Y);
let xr = &x;
let mut xm = xr.clone();
bar(&mut xm); //~ ERROR cannot borrow data in a `&` reference as mutable
}

View file

@ -0,0 +1,30 @@
error[E0596]: cannot borrow `sm.x` as mutable, as it is behind a `&` reference
--> $DIR/accidentally-cloning-ref-borrow-error.rs:32:9
|
LL | foo(&mut sm.x);
| ^^^^^^^^^ `sm` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: `Str` doesn't implement `Clone`, so this call clones the reference `&Str`
--> $DIR/accidentally-cloning-ref-borrow-error.rs:31:21
|
LL | let mut sm = sr.clone();
| ^^^^^^^
help: consider annotating `Str` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct Str {
|
help: consider specifying this binding's type
|
LL | let mut sm: &mut Str = sr.clone();
| ++++++++++
error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/accidentally-cloning-ref-borrow-error.rs:37:9
|
LL | bar(&mut xm);
| ^^^^^^^ cannot borrow as mutable
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0596`.

View file

@ -0,0 +1,7 @@
// run-rustfix
use std::rc::Rc;
pub fn main() {
let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
//~^ ERROR [E0507]
}

View file

@ -1,3 +1,4 @@
// run-rustfix
use std::rc::Rc;
pub fn main() {

View file

@ -1,5 +1,5 @@
error[E0507]: cannot move out of an `Rc`
--> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:4:14
--> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:5:14
|
LL | let _x = Rc::new(vec![1, 2]).into_iter();
| ^^^^^^^^^^^^^^^^^^^ ----------- value moved due to this method call
@ -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

View file

@ -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
}

View file

@ -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

View file

@ -0,0 +1,47 @@
// run-rustfix
#![allow(unused_variables, dead_code)]
#[derive(Clone)]
struct Struct;
#[derive(Clone)]
struct Struct2;
// We use a second one here because otherwise when applying suggestions we'd end up with two
// `#[derive(Clone)]` annotations.
fn test1() {
let mut val = Some(Struct);
while let Some(ref foo) = val { //~ ERROR use of moved value
if true {
val = None;
} else {
}
}
}
fn test2() {
let mut foo = Some(Struct);
let _x = foo.clone().unwrap();
if true {
foo = Some(Struct);
} else {
}
let _y = foo; //~ ERROR use of moved value: `foo`
}
fn test3() {
let mut foo = Some(Struct2);
let _x = foo.clone().unwrap();
if true {
foo = Some(Struct2);
} else if true {
foo = Some(Struct2);
} else if true {
foo = Some(Struct2);
} else if true {
foo = Some(Struct2);
} else {
}
let _y = foo; //~ ERROR use of moved value: `foo`
}
fn main() {}

View file

@ -1,4 +1,9 @@
// run-rustfix
#![allow(unused_variables, dead_code)]
struct Struct;
struct Struct2;
// We use a second one here because otherwise when applying suggestions we'd end up with two
// `#[derive(Clone)]` annotations.
fn test1() {
let mut val = Some(Struct);
@ -22,16 +27,16 @@ fn test2() {
}
fn test3() {
let mut foo = Some(Struct);
let mut foo = Some(Struct2);
let _x = foo.unwrap();
if true {
foo = Some(Struct);
foo = Some(Struct2);
} else if true {
foo = Some(Struct);
foo = Some(Struct2);
} else if true {
foo = Some(Struct);
foo = Some(Struct2);
} else if true {
foo = Some(Struct);
foo = Some(Struct2);
} else {
}
let _y = foo; //~ ERROR use of moved value: `foo`

View file

@ -1,5 +1,5 @@
error[E0382]: use of moved value
--> $DIR/issue-83760.rs:5:20
--> $DIR/issue-83760.rs:10:20
|
LL | while let Some(foo) = val {
| ^^^ value moved here, in previous iteration of loop
@ -14,7 +14,7 @@ LL | while let Some(ref foo) = val {
| +++
error[E0382]: use of moved value: `foo`
--> $DIR/issue-83760.rs:21:14
--> $DIR/issue-83760.rs:26:14
|
LL | let mut foo = Some(Struct);
| ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
@ -29,12 +29,21 @@ LL | let _y = foo;
|
note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied
|
LL | let _x = foo.clone().unwrap();
| ++++++++
help: consider annotating `Struct` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct Struct;
|
error[E0382]: use of moved value: `foo`
--> $DIR/issue-83760.rs:37:14
--> $DIR/issue-83760.rs:42:14
|
LL | let mut foo = Some(Struct);
| ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
LL | let mut foo = Some(Struct2);
| ------- move occurs because `foo` has type `Option<Struct2>`, which does not implement the `Copy` trait
LL | let _x = foo.unwrap();
| -------- `foo` moved due to this method call
...
@ -42,18 +51,27 @@ LL | let _y = foo;
| ^^^ value used here after move
|
note: these 3 reinitializations and 1 other might get skipped
--> $DIR/issue-83760.rs:30:9
--> $DIR/issue-83760.rs:35:9
|
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
LL | foo = Some(Struct2);
| ^^^^^^^^^^^^^^^^^^^
LL | } else if true {
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
LL | foo = Some(Struct2);
| ^^^^^^^^^^^^^^^^^^^
LL | } else if true {
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
LL | foo = Some(Struct2);
| ^^^^^^^^^^^^^^^^^^^
note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `Struct2: Clone` trait bound could be satisfied
|
LL | let _x = foo.clone().unwrap();
| ++++++++
help: consider annotating `Struct2` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct Struct2;
|
error: aborting due to 3 previous errors

View file

@ -3,6 +3,7 @@ fn main() {
let mut test = Vec::new();
let rofl: &Vec<Vec<i32>> = &mut test;
//~^ HELP consider changing this binding's type
//~| HELP you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
rofl.push(Vec::new());
//~^ ERROR cannot borrow `*rofl` as mutable, as it is behind a `&` reference
//~| NOTE `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable

View file

@ -1,16 +1,21 @@
error[E0596]: cannot borrow `*rofl` as mutable, as it is behind a `&` reference
--> $DIR/issue-85765-closure.rs:6:9
--> $DIR/issue-85765-closure.rs:7:9
|
LL | rofl.push(Vec::new());
| ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
--> $DIR/issue-85765-closure.rs:4:36
|
LL | let rofl: &Vec<Vec<i32>> = &mut test;
| ^^^^^^^^^
help: consider changing this binding's type
|
LL | let rofl: &mut Vec<Vec<i32>> = &mut test;
| ~~~~~~~~~~~~~~~~~~
error[E0594]: cannot assign to `*r`, which is behind a `&` reference
--> $DIR/issue-85765-closure.rs:13:9
--> $DIR/issue-85765-closure.rs:14:9
|
LL | *r = 0;
| ^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written
@ -21,7 +26,7 @@ LL | let r = &mut mutvar;
| +++
error[E0594]: cannot assign to `*x`, which is behind a `&` reference
--> $DIR/issue-85765-closure.rs:20:9
--> $DIR/issue-85765-closure.rs:21:9
|
LL | *x = 1;
| ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
@ -32,7 +37,7 @@ LL | let x: &mut usize = &mut{0};
| ~~~~~~~~~~
error[E0594]: cannot assign to `*y`, which is behind a `&` reference
--> $DIR/issue-85765-closure.rs:27:9
--> $DIR/issue-85765-closure.rs:28:9
|
LL | *y = 1;
| ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written

View file

@ -2,6 +2,7 @@ fn main() {
let mut test = Vec::new();
let rofl: &Vec<Vec<i32>> = &mut test;
//~^ HELP consider changing this binding's type
//~| HELP you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
rofl.push(Vec::new());
//~^ ERROR cannot borrow `*rofl` as mutable, as it is behind a `&` reference
//~| NOTE `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable

View file

@ -1,16 +1,21 @@
error[E0596]: cannot borrow `*rofl` as mutable, as it is behind a `&` reference
--> $DIR/issue-85765.rs:5:5
--> $DIR/issue-85765.rs:6:5
|
LL | rofl.push(Vec::new());
| ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
--> $DIR/issue-85765.rs:3:32
|
LL | let rofl: &Vec<Vec<i32>> = &mut test;
| ^^^^^^^^^
help: consider changing this binding's type
|
LL | let rofl: &mut Vec<Vec<i32>> = &mut test;
| ~~~~~~~~~~~~~~~~~~
error[E0594]: cannot assign to `*r`, which is behind a `&` reference
--> $DIR/issue-85765.rs:12:5
--> $DIR/issue-85765.rs:13:5
|
LL | *r = 0;
| ^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written
@ -21,7 +26,7 @@ LL | let r = &mut mutvar;
| +++
error[E0594]: cannot assign to `*x`, which is behind a `&` reference
--> $DIR/issue-85765.rs:19:5
--> $DIR/issue-85765.rs:20:5
|
LL | *x = 1;
| ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
@ -32,7 +37,7 @@ LL | let x: &mut usize = &mut{0};
| ~~~~~~~~~~
error[E0594]: cannot assign to `*y`, which is behind a `&` reference
--> $DIR/issue-85765.rs:26:5
--> $DIR/issue-85765.rs:27:5
|
LL | *y = 1;
| ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written

View file

@ -10,6 +10,7 @@ fn main() {
let client = TestClient;
let inner = client.get_inner_ref();
//~^ HELP consider specifying this binding's type
//~| HELP you can `clone` the `Vec<usize>` value and consume it, but this might not be your desired behavior
inner.clear();
//~^ ERROR cannot borrow `*inner` as mutable, as it is behind a `&` reference [E0596]
//~| NOTE `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable

View file

@ -1,9 +1,14 @@
error[E0596]: cannot borrow `*inner` as mutable, as it is behind a `&` reference
--> $DIR/issue-91206.rs:13:5
--> $DIR/issue-91206.rs:14:5
|
LL | inner.clear();
| ^^^^^ `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: you can `clone` the `Vec<usize>` value and consume it, but this might not be your desired behavior
--> $DIR/issue-91206.rs:11:17
|
LL | let inner = client.get_inner_ref();
| ^^^^^^^^^^^^^^^^^^^^^^
help: consider specifying this binding's type
|
LL | let inner: &mut Vec<usize> = client.get_inner_ref();

View file

@ -9,6 +9,10 @@ LL | cb.map(|cb| cb());
|
note: `Option::<T>::map` takes ownership of the receiver `self`, which moves `*cb`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `&mut dyn FnMut(): Clone` trait bound could be satisfied
|
LL | <Option<&mut dyn FnMut()> as Clone>::clone(&cb).map(|cb| cb());
| ++++++++++++++++++++++++++++++++++++++++++++ +
error[E0596]: cannot borrow `*cb` as mutable, as it is behind a `&` reference
--> $DIR/suggest-as-ref-on-mut-closure.rs:12:26

View file

@ -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
});
}

View file

@ -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

View file

@ -0,0 +1,30 @@
// run-rustfix
#![allow(unused_variables, dead_code)]
use std::collections::BTreeMap;
use std::collections::HashSet;
#[derive(Debug,Eq,PartialEq,Hash)]
#[derive(Clone)]
enum Day {
Mon,
}
struct Class {
days: BTreeMap<u32, HashSet<Day>>
}
impl Class {
fn do_stuff(&self) {
for (_, v) in &self.days {
let mut x: HashSet<Day> = v.clone(); //~ ERROR
let y: Vec<Day> = x.drain().collect();
println!("{:?}", x);
}
}
}
fn fail() {
let c = Class { days: BTreeMap::new() };
c.do_stuff();
}
fn main() {}

View file

@ -0,0 +1,29 @@
// run-rustfix
#![allow(unused_variables, dead_code)]
use std::collections::BTreeMap;
use std::collections::HashSet;
#[derive(Debug,Eq,PartialEq,Hash)]
enum Day {
Mon,
}
struct Class {
days: BTreeMap<u32, HashSet<Day>>
}
impl Class {
fn do_stuff(&self) {
for (_, v) in &self.days {
let mut x: HashSet<Day> = v.clone(); //~ ERROR
let y: Vec<Day> = x.drain().collect();
println!("{:?}", x);
}
}
}
fn fail() {
let c = Class { days: BTreeMap::new() };
c.do_stuff();
}
fn main() {}

View file

@ -0,0 +1,25 @@
error[E0308]: mismatched types
--> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
|
LL | let mut x: HashSet<Day> = v.clone();
| ------------ ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>`
| |
| expected due to this
|
= note: expected struct `HashSet<Day>`
found reference `&HashSet<Day>`
note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead
--> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
|
LL | let mut x: HashSet<Day> = v.clone();
| ^
= help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
help: consider annotating `Day` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | enum Day {
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.

View file

@ -55,6 +55,10 @@ note: `Foo::use_box_self` takes ownership of the receiver `self`, which moves `b
|
LL | fn use_box_self(self: Box<Self>) {}
| ^^^^
help: you could `clone` the value and consume it, if the `Box<Foo>: Clone` trait bound could be satisfied
|
LL | boxed_foo.clone().use_box_self();
| ++++++++
error[E0382]: use of moved value: `pin_box_foo`
--> $DIR/move-fn-self-receiver.rs:46:5
@ -71,6 +75,10 @@ note: `Foo::use_pin_box_self` takes ownership of the receiver `self`, which move
|
LL | fn use_pin_box_self(self: Pin<Box<Self>>) {}
| ^^^^
help: you could `clone` the value and consume it, if the `Box<Foo>: Clone` trait bound could be satisfied
|
LL | pin_box_foo.clone().use_pin_box_self();
| ++++++++
error[E0505]: cannot move out of `mut_foo` because it is borrowed
--> $DIR/move-fn-self-receiver.rs:50:5

View 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() {}

View 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() {}

View 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 1 previous error
For more information about this error, try `rustc --explain E0507`.

View file

@ -0,0 +1,28 @@
// run-rustfix
// Issue #109429
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::Hash;
#[derive(Clone)]
pub struct Hash128_1;
impl BuildHasher for Hash128_1 {
type Hasher = DefaultHasher;
fn build_hasher(&self) -> DefaultHasher { DefaultHasher::default() }
}
#[allow(unused)]
pub fn hashmap_copy<T, U>(
map: &HashMap<T, U, Hash128_1>,
) where T: Hash + Clone, U: Clone
{
let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect(); //~ ERROR
}
pub fn make_map() -> HashMap<String, i64, Hash128_1>
{
HashMap::with_hasher(Hash128_1)
}
fn main() {}

View file

@ -0,0 +1,27 @@
// run-rustfix
// Issue #109429
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::Hash;
pub struct Hash128_1;
impl BuildHasher for Hash128_1 {
type Hasher = DefaultHasher;
fn build_hasher(&self) -> DefaultHasher { DefaultHasher::default() }
}
#[allow(unused)]
pub fn hashmap_copy<T, U>(
map: &HashMap<T, U, Hash128_1>,
) where T: Hash + Clone, U: Clone
{
let mut copy: Vec<U> = map.clone().into_values().collect(); //~ ERROR
}
pub fn make_map() -> HashMap<String, i64, Hash128_1>
{
HashMap::with_hasher(Hash128_1)
}
fn main() {}

View file

@ -0,0 +1,23 @@
error[E0507]: cannot move out of a shared reference
--> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:20:28
|
LL | let mut copy: Vec<U> = map.clone().into_values().collect();
| ^^^^^^^^^^^ ------------- value moved due to this method call
| |
| move occurs because value has type `HashMap<T, U, Hash128_1>`, which does not implement the `Copy` trait
|
note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
|
LL | let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect();
| ++++++++++++++++++++++++++++++++++++++++++++ +
help: consider annotating `Hash128_1` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | pub struct Hash128_1;
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0507`.

View file

@ -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
}

View file

@ -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

View file

@ -12,6 +12,15 @@ LL | let _y = foo;
|
note: `Option::<T>::map` takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied
|
LL | let _x: Option<Struct> = foo.clone().map(|s| bar(&s));
| ++++++++
help: consider annotating `Struct` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct Struct;
|
error: aborting due to 1 previous error

View file

@ -0,0 +1,38 @@
// run-rustfix
pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}
impl LipogramCorpora {
pub fn validate_all(&mut self) -> Result<(), char> {
for selection in &self.selections {
if selection.1.is_some() {
if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
//~^ ERROR cannot move out of `selection.1`
return Err(selection.0);
}
}
}
Ok(())
}
}
pub struct LipogramCorpora2 {
selections: Vec<(char, Result<String, String>)>,
}
impl LipogramCorpora2 {
pub fn validate_all(&mut self) -> Result<(), char> {
for selection in &self.selections {
if selection.1.is_ok() {
if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
//~^ ERROR cannot move out of `selection.1`
return Err(selection.0);
}
}
}
Ok(())
}
}
fn main() {}

View file

@ -1,3 +1,4 @@
// run-rustfix
pub struct LipogramCorpora {
selections: Vec<(char, Option<String>)>,
}

View file

@ -1,5 +1,5 @@
error[E0507]: cannot move out of `selection.1` which is behind a shared reference
--> $DIR/option-content-move.rs:9:20
--> $DIR/option-content-move.rs:10:20
|
LL | if selection.1.unwrap().contains(selection.0) {
| ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@ -11,11 +11,11 @@ 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
--> $DIR/option-content-move.rs:28:20
|
LL | if selection.1.unwrap().contains(selection.0) {
| ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@ -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