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:
commit
f67523d0b3
39 changed files with 800 additions and 97 deletions
|
@ -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
|
||||
_ => {}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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)]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
38
tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs
Normal file
38
tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs
Normal 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
|
||||
}
|
|
@ -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`.
|
|
@ -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]
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// run-rustfix
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn main() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
47
tests/ui/borrowck/issue-83760.fixed
Normal file
47
tests/ui/borrowck/issue-83760.fixed
Normal 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() {}
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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
|
||||
|
|
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 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0507`.
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
38
tests/ui/suggestions/option-content-move.fixed
Normal file
38
tests/ui/suggestions/option-content-move.fixed
Normal 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() {}
|
|
@ -1,3 +1,4 @@
|
|||
// run-rustfix
|
||||
pub struct LipogramCorpora {
|
||||
selections: Vec<(char, Option<String>)>,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue