]> git.lizzy.rs Git - rust.git/commitdiff
Suggest Box::pin when Pin::new is used instead
authorTyler Mandry <tmandry@gmail.com>
Wed, 13 Oct 2021 23:24:48 +0000 (23:24 +0000)
committerTyler Mandry <tmandry@gmail.com>
Thu, 14 Oct 2021 01:06:25 +0000 (01:06 +0000)
compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr

index 339c46616a590b944c01528381c3b174a1b299f5..babc06822ac529baa6b4a3b8b59d2afe1e94b2cc 100644 (file)
@@ -8,11 +8,11 @@
 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 @@ pub(in super::super) fn suggest_no_capture_closure(
     }
 
     /// 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 @@ pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
         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,
         }
     }
 
index 89a36e89b0acf513c92f6588b5e4323aae4b4d79..7e9c5492d1a6bc083e119eedace934dc90768b4e 100644 (file)
@@ -15,9 +15,6 @@ fn bar<F: Future<Output=i32> + 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<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
     Pin::new(x) //~ ERROR mismatched types
     //~^ ERROR E0277
index f0af37e0cbe8af7e47f7b276e0ceb804c04a9dcf..aa3175dae2e66cca43d57f73472b30826c67ccce 100644 (file)
@@ -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<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`
@@ -56,7 +53,7 @@ LL |     pub const fn new(pointer: P) -> Pin<P> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 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`
@@ -69,7 +66,7 @@ LL |     pub const fn new(pointer: P) -> Pin<P> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 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