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;
}
/// 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<'_>,
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<Box<expected>>, 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,
}
}
= 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<F: Future<Output=i32> + 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<dyn Future<Output = i32> + 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<Output = i32> + 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<Output = i32> + Send`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: `dyn Future<Output = i32> + 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<Output = i32> + Send`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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<Box<(dyn Future<Output = i32> + Send + 'static)>>` because of return type