diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 339c46616a590..babc06822ac52 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -8,11 +8,11 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Expr, ExprKind, ItemKind, Node, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind}; use rustc_infer::infer; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, Ty}; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, sym}; use std::iter; @@ -350,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. + #[instrument(skip(self, err))] pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( &self, err: &mut DiagnosticBuilder<'_>, @@ -368,41 +369,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { return false; } - match expected.kind() { - ty::Adt(def, _) if Some(def.did) == pin_did => (), - _ => return false, - } let box_found = self.tcx.mk_box(found); let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap(); let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap(); - if self.can_coerce(pin_box_found, expected) { - debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.multipart_suggestion( - "you need to pin and box this expression", - vec![ - (expr.span.shrink_to_lo(), "Box::pin(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); + match expected.kind() { + ty::Adt(def, _) if Some(def.did) == pin_did => { + if self.can_coerce(pin_box_found, expected) { + debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + } + _ => { + err.multipart_suggestion( + "you need to pin and box this expression", + vec![ + (expr.span.shrink_to_lo(), "Box::pin(".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + } + true + } else if self.can_coerce(pin_found, expected) { + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + true + } + _ => false, + } + } else { + false } } - true - } else if self.can_coerce(pin_found, expected) { - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - true + ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { + // Check if the parent expression is a call to Pin::new. If it + // is and we were expecting a Box, ergo Pin>, we + // can suggest Box::pin. + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let fn_name = match self.tcx.hir().find(parent) { + Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) => fn_name, + _ => return false, + }; + match fn_name.kind { + ExprKind::Path(QPath::TypeRelative( + hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), + .. + }, + method, + )) if Some(recv_ty.def_id()) == pin_did && method.ident.name == sym::new => { + err.span_suggestion( + fn_name.span, + "use `Box::pin` to pin and box this expression", + "Box::pin".to_string(), + Applicability::MachineApplicable, + ); + true + } + _ => false, } - _ => false, } - } else { - false + _ => false, } } diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs index 89a36e89b0acf..7e9c5492d1a6b 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs @@ -15,9 +15,6 @@ fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> Box::new(x) //~ ERROR mismatched types } -// This case is still subpar: -// `Pin::new(x)`: store this in the heap by calling `Box::new`: `Box::new(x)` -// Should suggest changing the code from `Pin::new` to `Box::pin`. fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { Pin::new(x) //~ ERROR mismatched types //~^ ERROR E0277 diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index f0af37e0cbe8a..aa3175dae2e66 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -27,23 +27,20 @@ LL | Box::new(x) = help: use `Box::pin` error[E0308]: mismatched types - --> $DIR/expected-boxed-future-isnt-pinned.rs:22:14 + --> $DIR/expected-boxed-future-isnt-pinned.rs:19:14 | LL | fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { | - this type parameter LL | Pin::new(x) - | ^ expected struct `Box`, found type parameter `F` + | -------- ^ expected struct `Box`, found type parameter `F` + | | + | help: use `Box::pin` to pin and box this expression: `Box::pin` | = note: expected struct `Box + Send>` found type parameter `F` - = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html -help: store this in the heap by calling `Box::new` - | -LL | Pin::new(Box::new(x)) - | +++++++++ + error[E0277]: `dyn Future + Send` cannot be unpinned - --> $DIR/expected-boxed-future-isnt-pinned.rs:22:5 + --> $DIR/expected-boxed-future-isnt-pinned.rs:19:5 | LL | Pin::new(x) | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future + Send` @@ -56,7 +53,7 @@ LL | pub const fn new(pointer: P) -> Pin

{ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: `dyn Future + Send` cannot be unpinned - --> $DIR/expected-boxed-future-isnt-pinned.rs:27:5 + --> $DIR/expected-boxed-future-isnt-pinned.rs:24:5 | LL | Pin::new(Box::new(x)) | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future + Send` @@ -69,7 +66,7 @@ LL | pub const fn new(pointer: P) -> Pin

{ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/expected-boxed-future-isnt-pinned.rs:31:5 + --> $DIR/expected-boxed-future-isnt-pinned.rs:28:5 | LL | fn zap() -> BoxFuture<'static, i32> { | ----------------------- expected `Pin + Send + 'static)>>` because of return type