Code change:
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) { <del> let mut span: MultiSpan = arg.span.into(); span.push_span_label( arg.span, "this parameter takes ownership of the value".to_string(), ); let descr = match node.fn_kind() { Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function", Some(hir::intravisit::FnKind::Method(..)) => "method", Some(hir::intravisit::FnKind::Closure) => "closure", }; span.push_span_label(ident.span, format!("in this {descr}")); err.span_note( span, format!( "consider changing this parameter type in {descr} `{ident}` to borrow  instead if owning the value isn't necessary", ), ); </del> <ins> let mut is_mut = false; if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind && let Res::Def(DefKind::TyParam, param_def_id) = path.res && self .infcx .tcx .predicates_of(def_id) .instantiate_identity(self.infcx.tcx) .predicates .into_iter() .any(|pred| { if let ty::ClauseKind::Trait(predicate) = pred.kind().skip_binder() && [ self.infcx.tcx.get_diagnostic_item(sym::AsRef), self.infcx.tcx.get_diagnostic_item(sym::AsMut), self.infcx.tcx.get_diagnostic_item(sym::Borrow), self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), ] .contains(&Some(predicate.def_id())) && let ty::Param(param) = predicate.self_ty().kind() && let generics = self.infcx.tcx.generics_of(def_id) && let param = generics.type_param(*param, self.infcx.tcx) && param.def_id == param_def_id { if [ self.infcx.tcx.get_diagnostic_item(sym::AsMut), self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), ] .contains(&Some(predicate.def_id())) { is_mut = true; } true } else { false } }) { // The type of the argument corresponding to the expression that got moved // is a type parameter `T`, which is has a `T: AsRef` obligation. err.span_suggestion_verbose( expr.span.shrink_to_lo(), "borrow the value to avoid moving it", format!("&{}", if is_mut { "mut " } else { "" }), Applicability::MachineApplicable, ); can_suggest_clone = is_mut; } else { let mut span: MultiSpan = arg.span.into(); span.push_span_label( arg.span, "this parameter takes ownership of the value".to_string(), ); let descr = match node.fn_kind() { Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function", Some(hir::intravisit::FnKind::Method(..)) => "method", Some(hir::intravisit::FnKind::Closure) => "closure", }; span.push_span_label(ident.span, format!("in this {descr}")); err.span_note( span, format!( "consider changing this parameter type in {descr} `{ident}` to  borrow instead if owning the value isn't necessary", ), ); } </ins> } let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty;

Related discussion:
text: ... and trying to use afterwards, which triggers Example: currently yields I suggest that it should also contain
AsRef is a library trait, so IMO we should not do that. Also to me "value moved here" is pretty much the "you need to use this as reference".
While this might be obvious to us, I've had students struggling strongly with these errors. When I tell them "just add an ampersand", they're like "Oh, that makes total sense". I don't see that as reason not to do it. Could you elaborate?
This should also handle , , .
and aren't as clear, since the object will be modified, so the user might also want to , but that can later be signalled through and/or multiple suggestions
Obvious way of doing this would involve making a language item or otherwise special in some other way. To me it is pretty clear that language and the standard library should be as independent as possible and IME a diagnostic is a pretty minor gain for the special casing involved. One could imagine a similar diagnostic independent of library traits (e.g. always suggest a reference for "value moved here" if there’s a trait implementation for also), but then the problem of suggestion not being always correct kicks in. So, I will not oppose a change that implements this without making any more of the libstd/libcore special and is always correct, but I have no idea how anybody even begin approaching this problem.
I fully agree that that is not desirable In clippy we have similar diagnostics which in this case would simply check for (see for all the paths that we resolve manually).
Triage: this still produces the same error message. Given the concerns voiced above, should this issue stay open? I suppose we could leave this open should someone come up with a clever way to handle this without special casing , but that seems unlikely.
We have diagnostic items nowadays, so we can make a diagnostic item and detect in in these situations. Though I'm not sure how easy it is to detect. The error may get reported far too late
Current :