diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 5fdf8a8d1ee19..a80efa2a7670f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -197,7 +197,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { + FnSelfUseKind::Normal { + self_arg, + implicit_into_iter, + is_option_or_result, + } => { if implicit_into_iter { err.span_label( fn_call_span, @@ -215,6 +219,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ), ); } + if is_option_or_result { + err.span_suggestion_verbose( + fn_call_span.shrink_to_lo(), + "consider calling `.as_ref()` to borrow the type's contents", + "as_ref().".to_string(), + Applicability::MachineApplicable, + ); + } // Avoid pointing to the same function in multiple different // error messages. if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 577d7d53814ee..aa9f18d999628 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -573,7 +573,13 @@ pub(super) enum UseSpans<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(super) enum FnSelfUseKind<'tcx> { /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { self_arg: Ident, implicit_into_iter: bool }, + Normal { + self_arg: Ident, + implicit_into_iter: bool, + /// Whether the self type of the method call has an `.as_ref()` method. + /// Used for better diagnostics. + is_option_or_result: bool, + }, /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` FnOnceCall, /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) @@ -900,7 +906,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn_call_span.desugaring_kind(), Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) ); - FnSelfUseKind::Normal { self_arg, implicit_into_iter } + let parent_self_ty = parent + .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + tcx.is_diagnostic_item(sym::option_type, def_id) + || tcx.is_diagnostic_item(sym::result_type, def_id) + }); + FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } }); return FnSelfUse { diff --git a/src/test/ui/suggestions/as-ref-2.fixed b/src/test/ui/suggestions/as-ref-2.fixed new file mode 100644 index 0000000000000..13bbb233f3986 --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +struct Struct; + +fn bar(_: &Struct) -> Struct { + Struct +} + +fn main() { + let foo = Some(Struct); + let _x: Option = foo.as_ref().map(|s| bar(&s)); + let _y = foo; //~ERROR use of moved value: `foo` +} diff --git a/src/test/ui/suggestions/as-ref-2.rs b/src/test/ui/suggestions/as-ref-2.rs new file mode 100644 index 0000000000000..74d61cdd95f8d --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.rs @@ -0,0 +1,13 @@ +// run-rustfix + +struct Struct; + +fn bar(_: &Struct) -> Struct { + Struct +} + +fn main() { + let foo = Some(Struct); + let _x: Option = foo.map(|s| bar(&s)); + let _y = foo; //~ERROR use of moved value: `foo` +} diff --git a/src/test/ui/suggestions/as-ref-2.stderr b/src/test/ui/suggestions/as-ref-2.stderr new file mode 100644 index 0000000000000..f2eddf2fb098e --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.stderr @@ -0,0 +1,23 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/as-ref-2.rs:12:14 + | +LL | let foo = Some(Struct); + | --- move occurs because `foo` has type `Option`, which does not implement the `Copy` trait +LL | let _x: Option = foo.map(|s| bar(&s)); + | ---------------- `foo` moved due to this method call +LL | let _y = foo; + | ^^^ value used here after move + | +note: this function takes ownership of the receiver `self`, which moves `foo` + --> $SRC_DIR/core/src/option.rs:LL:COL + | +LL | pub fn map U>(self, f: F) -> Option { + | ^^^^ +help: consider calling `.as_ref()` to borrow the type's contents + | +LL | let _x: Option = foo.as_ref().map(|s| bar(&s)); + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/suggestions/as-ref.rs b/src/test/ui/suggestions/as-ref.rs index 03f04c389f1f3..46d9461538c78 100644 --- a/src/test/ui/suggestions/as-ref.rs +++ b/src/test/ui/suggestions/as-ref.rs @@ -1,25 +1,20 @@ struct Foo; + fn takes_ref(_: &Foo) {} fn main() { - let ref opt = Some(Foo); - opt.map(|arg| takes_ref(arg)); - //~^ ERROR mismatched types [E0308] - opt.and_then(|arg| Some(takes_ref(arg))); - //~^ ERROR mismatched types [E0308] - let ref opt: Result<_, ()> = Ok(Foo); - opt.map(|arg| takes_ref(arg)); - //~^ ERROR mismatched types [E0308] - opt.and_then(|arg| Ok(takes_ref(arg))); - //~^ ERROR mismatched types [E0308] - let x: &Option = &Some(3); - let y: Option<&usize> = x; - //~^ ERROR mismatched types [E0308] - let x: &Result = &Ok(3); - let y: Result<&usize, &usize> = x; - //~^ ERROR mismatched types [E0308] - // note: do not suggest because of `E: usize` - let x: &Result = &Ok(3); - let y: Result<&usize, usize> = x; - //~^ ERROR mismatched types [E0308] + let ref opt = Some(Foo); + opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] + opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308] + let ref opt: Result<_, ()> = Ok(Foo); + opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] + opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308] + let x: &Option = &Some(3); + let y: Option<&usize> = x; //~ ERROR mismatched types [E0308] + let x: &Result = &Ok(3); + let y: Result<&usize, &usize> = x; + //~^ ERROR mismatched types [E0308] + // note: do not suggest because of `E: usize` + let x: &Result = &Ok(3); + let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308] } diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 7e4d7fb3933d3..dc5d7efd752cf 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/src/test/ui/suggestions/as-ref.stderr @@ -1,70 +1,70 @@ error[E0308]: mismatched types - --> $DIR/as-ref.rs:6:27 + --> $DIR/as-ref.rs:7:29 | -LL | opt.map(|arg| takes_ref(arg)); - | --- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().map` +LL | opt.map(|arg| takes_ref(arg)); + | --- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().map` error[E0308]: mismatched types - --> $DIR/as-ref.rs:8:37 + --> $DIR/as-ref.rs:8:39 | -LL | opt.and_then(|arg| Some(takes_ref(arg))); - | -------- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().and_then` +LL | opt.and_then(|arg| Some(takes_ref(arg))); + | -------- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().and_then` error[E0308]: mismatched types - --> $DIR/as-ref.rs:11:27 + --> $DIR/as-ref.rs:10:29 | -LL | opt.map(|arg| takes_ref(arg)); - | --- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().map` +LL | opt.map(|arg| takes_ref(arg)); + | --- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().map` error[E0308]: mismatched types - --> $DIR/as-ref.rs:13:35 + --> $DIR/as-ref.rs:11:37 | -LL | opt.and_then(|arg| Ok(takes_ref(arg))); - | -------- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().and_then` +LL | opt.and_then(|arg| Ok(takes_ref(arg))); + | -------- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().and_then` error[E0308]: mismatched types - --> $DIR/as-ref.rs:16:27 + --> $DIR/as-ref.rs:13:29 | -LL | let y: Option<&usize> = x; - | -------------- ^ - | | | - | | expected enum `Option`, found `&Option` - | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `x.as_ref()` - | expected due to this +LL | let y: Option<&usize> = x; + | -------------- ^ + | | | + | | expected enum `Option`, found `&Option` + | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `x.as_ref()` + | expected due to this | = note: expected enum `Option<&usize>` found reference `&Option` error[E0308]: mismatched types - --> $DIR/as-ref.rs:19:35 + --> $DIR/as-ref.rs:15:37 | -LL | let y: Result<&usize, &usize> = x; - | ---------------------- ^ expected enum `Result`, found reference - | | - | expected due to this +LL | let y: Result<&usize, &usize> = x; + | ---------------------- ^ expected enum `Result`, found reference + | | + | expected due to this | = note: expected enum `Result<&usize, &usize>` found reference `&Result` help: you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` | -LL | let y: Result<&usize, &usize> = x.as_ref(); - | ^^^^^^^^^^ +LL | let y: Result<&usize, &usize> = x.as_ref(); + | ^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/as-ref.rs:23:34 + --> $DIR/as-ref.rs:19:36 | -LL | let y: Result<&usize, usize> = x; - | --------------------- ^ expected enum `Result`, found reference - | | - | expected due to this +LL | let y: Result<&usize, usize> = x; + | --------------------- ^ expected enum `Result`, found reference + | | + | expected due to this | = note: expected enum `Result<&usize, usize>` found reference `&Result`