Rollup merge of #124599 - estebank:issue-41708, r=wesleywiser · rust-lang/rust@8de487f
@@ -445,31 +445,81 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
445445} else {
446446(None, &[][..], 0)
447447};
448+let mut can_suggest_clone = true;
448449if let Some(def_id) = def_id
449450&& let node = self.infcx.tcx.hir_node_by_def_id(def_id)
450451&& let Some(fn_sig) = node.fn_sig()
451452&& let Some(ident) = node.ident()
452453&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
453454&& let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
454455{
455-let mut span: MultiSpan = arg.span.into();
456- span.push_span_label(
457- arg.span,
458-"this parameter takes ownership of the value".to_string(),
459-);
460-let descr = match node.fn_kind() {
461-Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
462-Some(hir::intravisit::FnKind::Method(..)) => "method",
463-Some(hir::intravisit::FnKind::Closure) => "closure",
464-};
465- span.push_span_label(ident.span, format!("in this {descr}"));
466- err.span_note(
467- span,
468-format!(
469-"consider changing this parameter type in {descr} `{ident}` to borrow \
470- instead if owning the value isn't necessary",
471-),
472-);
456+let mut is_mut = false;
457+if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind
458+&& let Res::Def(DefKind::TyParam, param_def_id) = path.res
459+&& self
460+.infcx
461+.tcx
462+.predicates_of(def_id)
463+.instantiate_identity(self.infcx.tcx)
464+.predicates
465+.into_iter()
466+.any(|pred| {
467+if let ty::ClauseKind::Trait(predicate) = pred.kind().skip_binder()
468+&& [
469+self.infcx.tcx.get_diagnostic_item(sym::AsRef),
470+self.infcx.tcx.get_diagnostic_item(sym::AsMut),
471+self.infcx.tcx.get_diagnostic_item(sym::Borrow),
472+self.infcx.tcx.get_diagnostic_item(sym::BorrowMut),
473+]
474+.contains(&Some(predicate.def_id()))
475+&& let ty::Param(param) = predicate.self_ty().kind()
476+&& let generics = self.infcx.tcx.generics_of(def_id)
477+&& let param = generics.type_param(*param, self.infcx.tcx)
478+&& param.def_id == param_def_id
479+{
480+if [
481+self.infcx.tcx.get_diagnostic_item(sym::AsMut),
482+self.infcx.tcx.get_diagnostic_item(sym::BorrowMut),
483+]
484+.contains(&Some(predicate.def_id()))
485+{
486+ is_mut = true;
487+}
488+true
489+} else {
490+false
491+}
492+})
493+{
494+// The type of the argument corresponding to the expression that got moved
495+// is a type parameter `T`, which is has a `T: AsRef` obligation.
496+ err.span_suggestion_verbose(
497+ expr.span.shrink_to_lo(),
498+"borrow the value to avoid moving it",
499+format!("&{}", if is_mut { "mut " } else { "" }),
500+Applicability::MachineApplicable,
501+);
502+ can_suggest_clone = is_mut;
503+} else {
504+let mut span: MultiSpan = arg.span.into();
505+ span.push_span_label(
506+ arg.span,
507+"this parameter takes ownership of the value".to_string(),
508+);
509+let descr = match node.fn_kind() {
510+Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
511+Some(hir::intravisit::FnKind::Method(..)) => "method",
512+Some(hir::intravisit::FnKind::Closure) => "closure",
513+};
514+ span.push_span_label(ident.span, format!("in this {descr}"));
515+ err.span_note(
516+ span,
517+format!(
518+"consider changing this parameter type in {descr} `{ident}` to \
519+ borrow instead if owning the value isn't necessary",
520+),
521+);
522+}
473523}
474524let place = &self.move_data.move_paths[mpi].place;
475525let ty = place.ty(self.body, self.infcx.tcx).ty;
@@ -487,9 +537,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
487537ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),
488538 ..
489539} = move_spans
540+&& can_suggest_clone
490541{
491542self.suggest_cloning(err, ty, expr, None, Some(move_spans));
492-} else if self.suggest_hoisting_call_outside_loop(err, expr) {
543+} else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {
493544// The place where the type moves would be misleading to suggest clone.
494545// #121466
495546self.suggest_cloning(err, ty, expr, None, Some(move_spans));