Suggest borrowing on fn argument that is `impl AsRef` · rust-lang/rust@2df4f7d

@@ -449,31 +449,81 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

449449

} else {

450450

(None, &[][..], 0)

451451

};

452+

let mut can_suggest_clone = true;

452453

if let Some(def_id) = def_id

453454

&& let node = self.infcx.tcx.hir_node_by_def_id(def_id)

454455

&& let Some(fn_sig) = node.fn_sig()

455456

&& let Some(ident) = node.ident()

456457

&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)

457458

&& let Some(arg) = fn_sig.decl.inputs.get(pos + offset)

458459

{

459-

let mut span: MultiSpan = arg.span.into();

460-

span.push_span_label(

461-

arg.span,

462-

"this parameter takes ownership of the value".to_string(),

463-

);

464-

let descr = match node.fn_kind() {

465-

Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",

466-

Some(hir::intravisit::FnKind::Method(..)) => "method",

467-

Some(hir::intravisit::FnKind::Closure) => "closure",

468-

};

469-

span.push_span_label(ident.span, format!("in this {descr}"));

470-

err.span_note(

471-

span,

472-

format!(

473-

"consider changing this parameter type in {descr} `{ident}` to borrow \

474-

instead if owning the value isn't necessary",

475-

),

476-

);

460+

let mut is_mut = false;

461+

if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind

462+

&& let Res::Def(DefKind::TyParam, param_def_id) = path.res

463+

&& self

464+

.infcx

465+

.tcx

466+

.predicates_of(def_id)

467+

.instantiate_identity(self.infcx.tcx)

468+

.predicates

469+

.into_iter()

470+

.any(|pred| {

471+

if let ty::ClauseKind::Trait(predicate) = pred.kind().skip_binder()

472+

&& [

473+

self.infcx.tcx.get_diagnostic_item(sym::AsRef),

474+

self.infcx.tcx.get_diagnostic_item(sym::AsMut),

475+

self.infcx.tcx.get_diagnostic_item(sym::Borrow),

476+

self.infcx.tcx.get_diagnostic_item(sym::BorrowMut),

477+

]

478+

.contains(&Some(predicate.def_id()))

479+

&& let ty::Param(param) = predicate.self_ty().kind()

480+

&& let generics = self.infcx.tcx.generics_of(def_id)

481+

&& let param = generics.type_param(*param, self.infcx.tcx)

482+

&& param.def_id == param_def_id

483+

{

484+

if [

485+

self.infcx.tcx.get_diagnostic_item(sym::AsMut),

486+

self.infcx.tcx.get_diagnostic_item(sym::BorrowMut),

487+

]

488+

.contains(&Some(predicate.def_id()))

489+

{

490+

is_mut = true;

491+

}

492+

true

493+

} else {

494+

false

495+

}

496+

})

497+

{

498+

// The type of the argument corresponding to the expression that got moved

499+

// is a type parameter `T`, which is has a `T: AsRef` obligation.

500+

err.span_suggestion_verbose(

501+

expr.span.shrink_to_lo(),

502+

"borrow the value to avoid moving it",

503+

format!("&{}", if is_mut { "mut " } else { "" }),

504+

Applicability::MachineApplicable,

505+

);

506+

can_suggest_clone = is_mut;

507+

} else {

508+

let mut span: MultiSpan = arg.span.into();

509+

span.push_span_label(

510+

arg.span,

511+

"this parameter takes ownership of the value".to_string(),

512+

);

513+

let descr = match node.fn_kind() {

514+

Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",

515+

Some(hir::intravisit::FnKind::Method(..)) => "method",

516+

Some(hir::intravisit::FnKind::Closure) => "closure",

517+

};

518+

span.push_span_label(ident.span, format!("in this {descr}"));

519+

err.span_note(

520+

span,

521+

format!(

522+

"consider changing this parameter type in {descr} `{ident}` to \

523+

borrow instead if owning the value isn't necessary",

524+

),

525+

);

526+

}

477527

}

478528

let place = &self.move_data.move_paths[mpi].place;

479529

let ty = place.ty(self.body, self.infcx.tcx).ty;

@@ -491,9 +541,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

491541

ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),

492542

..

493543

} = move_spans

544+

&& can_suggest_clone

494545

{

495546

self.suggest_cloning(err, ty, expr, None, Some(move_spans));

496-

} else if self.suggest_hoisting_call_outside_loop(err, expr) {

547+

} else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {

497548

// The place where the type moves would be misleading to suggest clone.

498549

// #121466

499550

self.suggest_cloning(err, ty, expr, None, Some(move_spans));