From 5585f3133c05a33e59cd352a32fdf171447a9b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 28 May 2024 20:48:35 +0000 Subject: [PATCH] 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 Future for FutureWrapper | - 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. --- .../rustc_hir_typeck/src/method/suggest.rs | 68 ++++++++++++++++--- .../async-await/pin-needed-to-poll-3.stderr | 4 +- 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 @@ fn suggest_traits_to_import( .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