Skip to content

Commit 2280d8e

Browse files
committed
Provide borrow-instead-of-move suggestions for calls of fn-like items from other crates
This also downgrades its applicability to MaybeIncorrect. Its suggestion can result in ill-typed code when the type parameter it suggests providing a different generic argument for appears elsewhere in the callee's signature or predicates.
1 parent 8adb4b3 commit 2280d8e

File tree

5 files changed

+170
-39
lines changed

5 files changed

+170
-39
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+40-39
Original file line numberDiff line numberDiff line change
@@ -440,47 +440,46 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
440440
} else {
441441
(None, &[][..], 0)
442442
};
443+
let ty = place.ty(self.body, self.infcx.tcx).ty;
443444

444-
// If the moved value is a mut reference, it is used in a
445-
// generic function and it's type is a generic param, it can be
446-
// reborrowed to avoid moving.
447-
// for example:
448-
// struct Y(u32);
449-
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
445+
let mut can_suggest_clone = true;
450446
if let Some(def_id) = def_id
451-
&& self.infcx.tcx.def_kind(def_id).is_fn_like()
452447
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
453-
&& let Some(arg) = self
454-
.infcx
455-
.tcx
456-
.fn_sig(def_id)
457-
.skip_binder()
458-
.skip_binder()
459-
.inputs()
460-
.get(pos + offset)
461-
&& let ty::Param(_) = arg.kind()
462448
{
463-
let place = &self.move_data.move_paths[mpi].place;
464-
let ty = place.ty(self.body, self.infcx.tcx).ty;
465-
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
449+
// The move occurred as one of the arguments to a function call. Is that
450+
// argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine
451+
let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like()
452+
&& let Some(arg_ty) = self
453+
.infcx
454+
.tcx
455+
.fn_sig(def_id)
456+
.instantiate_identity()
457+
.skip_binder()
458+
.inputs()
459+
.get(pos + offset)
460+
&& let ty::Param(arg_param) = arg_ty.kind()
461+
{
462+
Some(arg_param)
463+
} else {
464+
None
465+
};
466+
467+
// If the moved value is a mut reference, it is used in a
468+
// generic function and it's type is a generic param, it can be
469+
// reborrowed to avoid moving.
470+
// for example:
471+
// struct Y(u32);
472+
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
473+
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind()
474+
&& arg_param.is_some()
475+
{
466476
*has_suggest_reborrow = true;
467477
self.suggest_reborrow(err, expr.span, moved_place);
468478
return;
469479
}
470-
}
471480

472-
let mut can_suggest_clone = true;
473-
if let Some(def_id) = def_id
474-
&& let Some(local_def_id) = def_id.as_local()
475-
&& let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)
476-
&& let Some(fn_sig) = node.fn_sig()
477-
&& let Some(ident) = node.ident()
478-
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
479-
&& let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
480-
{
481481
let mut is_mut = false;
482-
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind
483-
&& let Res::Def(DefKind::TyParam, param_def_id) = path.res
482+
if let Some(param) = arg_param
484483
&& self
485484
.infcx
486485
.tcx
@@ -497,10 +496,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
497496
self.infcx.tcx.get_diagnostic_item(sym::BorrowMut),
498497
]
499498
.contains(&Some(predicate.def_id()))
500-
&& let ty::Param(param) = predicate.self_ty().kind()
501-
&& let generics = self.infcx.tcx.generics_of(def_id)
502-
&& let param = generics.type_param(*param, self.infcx.tcx)
503-
&& param.def_id == param_def_id
499+
&& *predicate.self_ty().kind() == ty::Param(*param)
504500
{
505501
if [
506502
self.infcx.tcx.get_diagnostic_item(sym::AsMut),
@@ -522,10 +518,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
522518
expr.span.shrink_to_lo(),
523519
"borrow the value to avoid moving it",
524520
format!("&{}", if is_mut { "mut " } else { "" }),
525-
Applicability::MachineApplicable,
521+
Applicability::MaybeIncorrect,
526522
);
527523
can_suggest_clone = is_mut;
528-
} else {
524+
} else if let Some(local_def_id) = def_id.as_local()
525+
&& let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)
526+
&& let Some(fn_decl) = node.fn_decl()
527+
&& let Some(ident) = node.ident()
528+
&& let Some(arg) = fn_decl.inputs.get(pos + offset)
529+
{
530+
// If we can't suggest borrowing in the call, but the function definition
531+
// is local, instead offer changing the function to borrow that argument.
529532
let mut span: MultiSpan = arg.span.into();
530533
span.push_span_label(
531534
arg.span,
@@ -546,8 +549,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
546549
);
547550
}
548551
}
549-
let place = &self.move_data.move_paths[mpi].place;
550-
let ty = place.ty(self.body, self.infcx.tcx).ty;
551552
if let hir::Node::Expr(parent_expr) = parent
552553
&& let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
553554
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! auxiliary definitons for suggest-borrow-for-generic-arg.rs, to ensure the suggestion works on
2+
//! functions defined in other crates.
3+
4+
use std::borrow::{Borrow, BorrowMut};
5+
use std::convert::{AsMut, AsRef};
6+
pub struct Bar;
7+
8+
impl AsRef<Bar> for Bar {
9+
fn as_ref(&self) -> &Bar {
10+
self
11+
}
12+
}
13+
14+
impl AsMut<Bar> for Bar {
15+
fn as_mut(&mut self) -> &mut Bar {
16+
self
17+
}
18+
}
19+
20+
pub fn foo<T: AsRef<Bar>>(_: T) {}
21+
pub fn qux<T: AsMut<Bar>>(_: T) {}
22+
pub fn bat<T: Borrow<T>>(_: T) {}
23+
pub fn baz<T: BorrowMut<T>>(_: T) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after
2+
//! substituting in the reference type.
3+
//@ run-rustfix
4+
//@ aux-build:suggest-borrow-for-generic-arg-aux.rs
5+
6+
#![allow(unused_mut)]
7+
extern crate suggest_borrow_for_generic_arg_aux as aux;
8+
9+
pub fn main() {
10+
let bar = aux::Bar;
11+
aux::foo(&bar); //~ HELP borrow the value
12+
let _baa = bar; //~ ERROR use of moved value
13+
let mut bar = aux::Bar;
14+
aux::qux(&mut bar); //~ HELP borrow the value
15+
let _baa = bar; //~ ERROR use of moved value
16+
let bar = aux::Bar;
17+
aux::bat(&bar); //~ HELP borrow the value
18+
let _baa = bar; //~ ERROR use of moved value
19+
let mut bar = aux::Bar;
20+
aux::baz(&mut bar); //~ HELP borrow the value
21+
let _baa = bar; //~ ERROR use of moved value
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after
2+
//! substituting in the reference type.
3+
//@ run-rustfix
4+
//@ aux-build:suggest-borrow-for-generic-arg-aux.rs
5+
6+
#![allow(unused_mut)]
7+
extern crate suggest_borrow_for_generic_arg_aux as aux;
8+
9+
pub fn main() {
10+
let bar = aux::Bar;
11+
aux::foo(bar); //~ HELP borrow the value
12+
let _baa = bar; //~ ERROR use of moved value
13+
let mut bar = aux::Bar;
14+
aux::qux(bar); //~ HELP borrow the value
15+
let _baa = bar; //~ ERROR use of moved value
16+
let bar = aux::Bar;
17+
aux::bat(bar); //~ HELP borrow the value
18+
let _baa = bar; //~ ERROR use of moved value
19+
let mut bar = aux::Bar;
20+
aux::baz(bar); //~ HELP borrow the value
21+
let _baa = bar; //~ ERROR use of moved value
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
error[E0382]: use of moved value: `bar`
2+
--> $DIR/suggest-borrow-for-generic-arg.rs:12:16
3+
|
4+
LL | let bar = aux::Bar;
5+
| --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait
6+
LL | aux::foo(bar);
7+
| --- value moved here
8+
LL | let _baa = bar;
9+
| ^^^ value used here after move
10+
|
11+
help: borrow the value to avoid moving it
12+
|
13+
LL | aux::foo(&bar);
14+
| +
15+
16+
error[E0382]: use of moved value: `bar`
17+
--> $DIR/suggest-borrow-for-generic-arg.rs:15:16
18+
|
19+
LL | let mut bar = aux::Bar;
20+
| ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait
21+
LL | aux::qux(bar);
22+
| --- value moved here
23+
LL | let _baa = bar;
24+
| ^^^ value used here after move
25+
|
26+
help: borrow the value to avoid moving it
27+
|
28+
LL | aux::qux(&mut bar);
29+
| ++++
30+
31+
error[E0382]: use of moved value: `bar`
32+
--> $DIR/suggest-borrow-for-generic-arg.rs:18:16
33+
|
34+
LL | let bar = aux::Bar;
35+
| --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait
36+
LL | aux::bat(bar);
37+
| --- value moved here
38+
LL | let _baa = bar;
39+
| ^^^ value used here after move
40+
|
41+
help: borrow the value to avoid moving it
42+
|
43+
LL | aux::bat(&bar);
44+
| +
45+
46+
error[E0382]: use of moved value: `bar`
47+
--> $DIR/suggest-borrow-for-generic-arg.rs:21:16
48+
|
49+
LL | let mut bar = aux::Bar;
50+
| ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait
51+
LL | aux::baz(bar);
52+
| --- value moved here
53+
LL | let _baa = bar;
54+
| ^^^ value used here after move
55+
|
56+
help: borrow the value to avoid moving it
57+
|
58+
LL | aux::baz(&mut bar);
59+
| ++++
60+
61+
error: aborting due to 4 previous errors
62+
63+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)