Suggest .clone()
or ref binding
on E0382
This commit is contained in:
parent
d121aa3b55
commit
9e72e35ceb
86 changed files with 1092 additions and 49 deletions
|
@ -191,6 +191,146 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
is_loop_move = true;
|
||||
}
|
||||
|
||||
struct ExpressionFinder<'hir> {
|
||||
expr_span: Span,
|
||||
expr: Option<&'hir hir::Expr<'hir>>,
|
||||
pat: Option<&'hir hir::Pat<'hir>>,
|
||||
}
|
||||
impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
|
||||
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
|
||||
if e.span == self.expr_span {
|
||||
self.expr = Some(e);
|
||||
}
|
||||
hir::intravisit::walk_expr(self, e);
|
||||
}
|
||||
fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
|
||||
if p.span == self.expr_span {
|
||||
self.pat = Some(p);
|
||||
}
|
||||
if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, _) = p.kind
|
||||
&& i.span == self.expr_span
|
||||
{
|
||||
self.pat = Some(p);
|
||||
}
|
||||
hir::intravisit::walk_pat(self, p);
|
||||
}
|
||||
}
|
||||
|
||||
let hir = self.infcx.tcx.hir();
|
||||
if let Some(hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Fn(_, _, body_id),
|
||||
..
|
||||
})) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
|
||||
&& let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
|
||||
{
|
||||
let place = &self.move_data.move_paths[mpi].place;
|
||||
let span = place.as_local()
|
||||
.map(|local| self.body.local_decls[local].source_info.span);
|
||||
let mut finder = ExpressionFinder {
|
||||
expr_span: move_span,
|
||||
expr: None,
|
||||
pat: None,
|
||||
};
|
||||
finder.visit_expr(expr);
|
||||
if let Some(span) = span && let Some(expr) = finder.expr {
|
||||
for (_, expr) in hir.parent_iter(expr.hir_id) {
|
||||
if let hir::Node::Expr(expr) = expr {
|
||||
if expr.span.contains(span) {
|
||||
// If the let binding occurs within the same loop, then that
|
||||
// loop isn't relevant, like in the following, the outermost `loop`
|
||||
// doesn't play into `x` being moved.
|
||||
// ```
|
||||
// loop {
|
||||
// let x = String::new();
|
||||
// loop {
|
||||
// foo(x);
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
break;
|
||||
}
|
||||
if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
|
||||
err.span_label(loop_span, "inside of this loop");
|
||||
}
|
||||
}
|
||||
}
|
||||
let typeck = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let hir_id = hir.get_parent_node(expr.hir_id);
|
||||
if let Some(parent) = hir.find(hir_id) {
|
||||
if let hir::Node::Expr(parent_expr) = parent
|
||||
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
|
||||
&& let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
|
||||
&& let Some(def_id) = def_id.as_local()
|
||||
&& let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(fn_sig) = node.fn_sig()
|
||||
&& let Some(ident) = node.ident()
|
||||
&& let Some(pos) = args.iter()
|
||||
.position(|arg| arg.hir_id == expr.hir_id)
|
||||
&& let Some(arg) = fn_sig.decl.inputs.get(pos + 1)
|
||||
{
|
||||
let mut span: MultiSpan = arg.span.into();
|
||||
span.push_span_label(
|
||||
arg.span,
|
||||
"this type parameter takes ownership of the value".to_string(),
|
||||
);
|
||||
span.push_span_label(
|
||||
ident.span,
|
||||
"in this method".to_string(),
|
||||
);
|
||||
err.span_note(
|
||||
span,
|
||||
format!(
|
||||
"consider changing this parameter type in `{}` to borrow \
|
||||
instead if ownering the value isn't necessary",
|
||||
ident,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let hir::Node::Expr(parent_expr) = parent
|
||||
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
|
||||
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
|
||||
&& let Some(def_id) = def_id.as_local()
|
||||
&& let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(fn_sig) = node.fn_sig()
|
||||
&& let Some(ident) = node.ident()
|
||||
&& let Some(pos) = args.iter()
|
||||
.position(|arg| arg.hir_id == expr.hir_id)
|
||||
&& let Some(arg) = fn_sig.decl.inputs.get(pos)
|
||||
{
|
||||
let mut span: MultiSpan = arg.span.into();
|
||||
span.push_span_label(
|
||||
arg.span,
|
||||
"this type parameter takes ownership of the value".to_string(),
|
||||
);
|
||||
span.push_span_label(
|
||||
ident.span,
|
||||
"in this function".to_string(),
|
||||
);
|
||||
err.span_note(
|
||||
span,
|
||||
format!(
|
||||
"consider changing this parameter type in `{}` to borrow \
|
||||
instead if ownering the value isn't necessary",
|
||||
ident,
|
||||
),
|
||||
);
|
||||
}
|
||||
let place = &self.move_data.move_paths[mpi].place;
|
||||
let ty = place.ty(self.body, self.infcx.tcx).ty;
|
||||
self.suggest_cloning(&mut err, ty, move_span);
|
||||
}
|
||||
}
|
||||
if let Some(pat) = finder.pat {
|
||||
in_pattern = true;
|
||||
err.span_suggestion_verbose(
|
||||
pat.span.shrink_to_lo(),
|
||||
"borrow this binding in the pattern to avoid moving the value",
|
||||
"ref ".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.explain_captures(
|
||||
&mut err,
|
||||
span,
|
||||
|
@ -203,25 +343,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
is_loop_move,
|
||||
maybe_reinitialized_locations.is_empty(),
|
||||
);
|
||||
|
||||
if let (UseSpans::PatUse(span), []) =
|
||||
(move_spans, &maybe_reinitialized_locations[..])
|
||||
{
|
||||
if maybe_reinitialized_locations.is_empty() {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
&format!(
|
||||
"borrow this field in the pattern to avoid moving {}",
|
||||
self.describe_place(moved_place.as_ref())
|
||||
.map(|n| format!("`{}`", n))
|
||||
.unwrap_or_else(|| "the value".to_string())
|
||||
),
|
||||
"ref ",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
in_pattern = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use_spans.var_path_only_subdiag(&mut err, desired_action);
|
||||
|
@ -590,6 +711,39 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
// Try to find predicates on *generic params* that would allow copying `ty`
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
|
||||
|
||||
let clone_did = infcx.tcx.lang_items().clone_trait().unwrap();
|
||||
let cause = ObligationCause::new(
|
||||
span,
|
||||
self.mir_hir_id(),
|
||||
rustc_infer::traits::ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
fulfill_cx.register_bound(
|
||||
&infcx,
|
||||
self.param_env,
|
||||
// Erase any region vids from the type, which may not be resolved
|
||||
infcx.tcx.erase_regions(ty),
|
||||
clone_did,
|
||||
cause,
|
||||
);
|
||||
// Select all, including ambiguous predicates
|
||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
||||
if errors.is_empty() {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"consider cloning the value if the performance cost is acceptable",
|
||||
".clone()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let generics = tcx.generics_of(self.mir_def_id());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue