about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-05-28 20:48:35 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-05-28 20:48:35 +0000
commit5585f3133c05a33e59cd352a32fdf171447a9b2b (patch)
treeed3a88cf132425ab21722dca316f55ecfb859d05
parent1a840e7732536bbf5c4d2f434d4af138983f4b0a (diff)
downloadrust-5585f3133c05a33e59cd352a32fdf171447a9b2b.tar.gz
rust-5585f3133c05a33e59cd352a32fdf171447a9b2b.zip
Account for existing bindings when suggesting pinning
When we encounter a situation where we'd suggest `pin!()`, we now account for that expression exising as part of an assignment and provide an appropriate suggestion:

```
error[E0599]: no method named `poll` found for type parameter `F` in the current scope
  --> $DIR/pin-needed-to-poll-3.rs:19:28
   |
LL | impl<F> Future for FutureWrapper<F>
   |      - method `poll` not found for this type parameter
...
LL |         let res = self.fut.poll(cx);
   |                            ^^^^ method not found in `F`
   |
help: consider pinning the expression
   |
LL ~         let mut pinned = std::pin::pin!(self.fut);
LL ~         let res = pinned.as_mut().poll(cx);
   |
```

Fix #125661.
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs68
-rw-r--r--tests/ui/async-await/pin-needed-to-poll-3.stderr4
2 files changed, 62 insertions, 10 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 54af8354c4c..75bb755183e 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -3326,14 +3326,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .source_map()
                         .indentation_before(rcvr.span)
                         .unwrap_or_else(|| " ".to_string());
-                    err.multipart_suggestion(
-                        "consider pinning the expression",
-                        vec![
-                            (rcvr.span.shrink_to_lo(), format!("let mut pinned = std::pin::pin!(")),
-                            (rcvr.span.shrink_to_hi(), format!(");\n{indent}pinned.{pin_call}()")),
-                        ],
-                        Applicability::MaybeIncorrect,
-                    );
+                    let mut expr = rcvr;
+                    while let Node::Expr(call_expr) = self.tcx.parent_hir_node(expr.hir_id)
+                        && let hir::ExprKind::MethodCall(hir::PathSegment { .. }, ..) =
+                            call_expr.kind
+                    {
+                        expr = call_expr;
+                    }
+                    match self.tcx.parent_hir_node(expr.hir_id) {
+                        Node::LetStmt(stmt)
+                            if let Some(init) = stmt.init
+                                && let Ok(code) =
+                                    self.tcx.sess.source_map().span_to_snippet(rcvr.span) =>
+                        {
+                            // We need to take care to account for the existing binding when we
+                            // suggest the code.
+                            err.multipart_suggestion(
+                                "consider pinning the expression",
+                                vec![
+                                    (
+                                        stmt.span.shrink_to_lo(),
+                                        format!(
+                                            "let mut pinned = std::pin::pin!({code});\n{indent}"
+                                        ),
+                                    ),
+                                    (
+                                        init.span.until(rcvr.span.shrink_to_hi()),
+                                        format!("pinned.{pin_call}()"),
+                                    ),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        Node::Block(_) | Node::Stmt(_) => {
+                            // There's no binding, so we can provide a slightly nicer looking
+                            // suggestion.
+                            err.multipart_suggestion(
+                                "consider pinning the expression",
+                                vec![
+                                    (
+                                        rcvr.span.shrink_to_lo(),
+                                        format!("let mut pinned = std::pin::pin!("),
+                                    ),
+                                    (
+                                        rcvr.span.shrink_to_hi(),
+                                        format!(");\n{indent}pinned.{pin_call}()"),
+                                    ),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        _ => {
+                            // We don't quite know what the users' code looks like, so we don't
+                            // provide a pinning suggestion.
+                            err.span_help(
+                                rcvr.span,
+                                "consider pinning the expression with `std::pin::pin!()` and \
+                                 assigning that to a new binding",
+                            );
+                        }
+                    }
                     // We don't care about the other suggestions.
                     alt_rcvr_sugg = true;
                 }
diff --git a/tests/ui/async-await/pin-needed-to-poll-3.stderr b/tests/ui/async-await/pin-needed-to-poll-3.stderr
index 83a0808c4ec..e4fbef69bac 100644
--- a/tests/ui/async-await/pin-needed-to-poll-3.stderr
+++ b/tests/ui/async-await/pin-needed-to-poll-3.stderr
@@ -9,8 +9,8 @@ LL |         let res = self.fut.poll(cx);
    |
 help: consider pinning the expression
    |
-LL ~         let res = let mut pinned = std::pin::pin!(self.fut);
-LL ~         pinned.as_mut().poll(cx);
+LL ~         let mut pinned = std::pin::pin!(self.fut);
+LL ~         let res = pinned.as_mut().poll(cx);
    |
 
 error: aborting due to 1 previous error