Repository: rust-lang/rust
Pull request: Fix pattern assignment suggestions for uninitialized bindings

Fixes rust-lang/rust#145564


Changed files:
filename: compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
patch: @@ -914,32 +914,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         if show_assign_sugg {
             struct LetVisitor {
                 decl_span: Span,
-                sugg_span: Option<Span>,
+                sugg: Option<(Span, bool)>,
             }
 
             impl<'v> Visitor<'v> for LetVisitor {
                 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
-                    if self.sugg_span.is_some() {
+                    if self.sugg.is_some() {
                         return;
                     }
 
                     // FIXME: We make sure that this is a normal top-level binding,
                     // but we could suggest `todo!()` for all uninitialized bindings in the pattern
                     if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) =
                         &ex.kind
-                        && let hir::PatKind::Binding(..) = pat.kind
+                        && let hir::PatKind::Binding(binding_mode, ..) = pat.kind
                         && span.contains(self.decl_span)
                     {
-                        self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
+                        // Insert after the whole binding pattern so suggestions stay valid for
+                        // bindings with `@` subpatterns like `ref mut x @ v`.
+                        let strip_ref = matches!(binding_mode.0, hir::ByRef::Yes(..));
+                        self.sugg =
+                            ty.map_or(Some((pat.span, strip_ref)), |ty| Some((ty.span, strip_ref)));
                     }
                     hir::intravisit::walk_stmt(self, ex);
                 }
             }
 
-            let mut visitor = LetVisitor { decl_span, sugg_span: None };
+            let mut visitor = LetVisitor { decl_span, sugg: None };
             visitor.visit_body(&body);
-            if let Some(span) = visitor.sugg_span {
-                self.suggest_assign_value(&mut err, moved_place, span);
+            if let Some((span, strip_ref)) = visitor.sugg {
+                self.suggest_assign_value(&mut err, moved_place, span, strip_ref);
             }
         }
         err
@@ -950,8 +954,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         err: &mut Diag<'_>,
         moved_place: PlaceRef<'tcx>,
         sugg_span: Span,
+        strip_ref: bool,
     ) {
-        let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
+        let mut ty = moved_place.ty(self.body, self.infcx.tcx).ty;
+        if strip_ref && let ty::Ref(_, inner, _) = ty.kind() {
+            ty = *inner;
+        }
         debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
 
         let Some(assign_value) = self.infcx.err_ctxt().ty_kind_suggestion(self.infcx.param_env, ty)

filename: tests/ui/moves/assign-value-after-at-pattern-issue-145564.fixed
patch: @@ -0,0 +1,19 @@
+//@ run-rustfix
+#![allow(unused_variables)]
+
+fn main() {
+    let ref mut x @ _v = 42;
+    *x = 1; //~ ERROR used binding `x` isn't initialized
+
+    let a @ _b: i32 = 42;
+    println!("{}", a); //~ ERROR used binding `a` isn't initialized
+
+    let ref c @ _d: i32 = 42;
+    println!("{:?}", c); //~ ERROR used binding `c` isn't initialized
+
+    let ref e: i32 = 42;
+    println!("{:?}", e); //~ ERROR used binding `e` isn't initialized
+
+    let ref mut y = 42;
+    *y = 1; //~ ERROR used binding `y` isn't initialized
+}

filename: tests/ui/moves/assign-value-after-at-pattern-issue-145564.rs
patch: @@ -0,0 +1,19 @@
+//@ run-rustfix
+#![allow(unused_variables)]
+
+fn main() {
+    let ref mut x @ _v;
+    *x = 1; //~ ERROR used binding `x` isn't initialized
+
+    let a @ _b: i32;
+    println!("{}", a); //~ ERROR used binding `a` isn't initialized
+
+    let ref c @ _d: i32;
+    println!("{:?}", c); //~ ERROR used binding `c` isn't initialized
+
+    let ref e: i32;
+    println!("{:?}", e); //~ ERROR used binding `e` isn't initialized
+
+    let ref mut y;
+    *y = 1; //~ ERROR used binding `y` isn't initialized
+}

filename: tests/ui/moves/assign-value-after-at-pattern-issue-145564.stderr
patch: @@ -0,0 +1,68 @@
+error[E0381]: used binding `x` isn't initialized
+  --> $DIR/assign-value-after-at-pattern-issue-145564.rs:6:5
+   |
+LL |     let ref mut x @ _v;
+   |         --------- binding declared here but left uninitialized
+LL |     *x = 1;
+   |     ^^^^^^ `x` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let ref mut x @ _v = 42;
+   |                        ++++
+
+error[E0381]: used binding `a` isn't initialized
+  --> $DIR/assign-value-after-at-pattern-issue-145564.rs:9:20
+   |
+LL |     let a @ _b: i32;
+   |         - binding declared here but left uninitialized
+LL |     println!("{}", a);
+   |                    ^ `a` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let a @ _b: i32 = 42;
+   |                     ++++
+
+error[E0381]: used binding `c` isn't initialized
+  --> $DIR/assign-value-after-at-pattern-issue-145564.rs:12:22
+   |
+LL |     let ref c @ _d: i32;
+   |         ----- binding declared here but left uninitialized
+LL |     println!("{:?}", c);
+   |                      ^ `c` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let ref c @ _d: i32 = 42;
+   |                         ++++
+
+error[E0381]: used binding `e` isn't initialized
+  --> $DIR/assign-value-after-at-pattern-issue-145564.rs:15:22
+   |
+LL |     let ref e: i32;
+   |         ----- binding declared here but left uninitialized
+LL |     println!("{:?}", e);
+   |                      ^ `e` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let ref e: i32 = 42;
+   |                    ++++
+
+error[E0381]: used binding `y` isn't initialized
+  --> $DIR/assign-value-after-at-pattern-issue-145564.rs:18:5
+   |
+LL |     let ref mut y;
+   |         --------- binding declared here but left uninitialized
+LL |     *y = 1;
+   |     ^^^^^^ `y` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let ref mut y = 42;
+   |                   ++++
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0381`.