about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_passes/src/liveness.rs23
-rw-r--r--src/test/ui/suggestions/ignore-nested-field-binding.fixed20
-rw-r--r--src/test/ui/suggestions/ignore-nested-field-binding.rs20
-rw-r--r--src/test/ui/suggestions/ignore-nested-field-binding.stderr20
4 files changed, 75 insertions, 8 deletions
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index ab9bfea9694..55de2e9d2f8 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -265,12 +265,13 @@ impl IrMaps<'tcx> {
         self.capture_info_map.insert(hir_id, Rc::new(cs));
     }
 
-    fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) {
+    fn collect_shorthand_field_ids(&self, pat: &hir::Pat<'tcx>) -> HirIdSet {
         // For struct patterns, take note of which fields used shorthand
         // (`x` rather than `x: x`).
         let mut shorthand_field_ids = HirIdSet::default();
         let mut pats = VecDeque::new();
         pats.push_back(pat);
+
         while let Some(pat) = pats.pop_front() {
             use rustc_hir::PatKind::*;
             match &pat.kind {
@@ -278,8 +279,10 @@ impl IrMaps<'tcx> {
                     pats.extend(inner_pat.iter());
                 }
                 Struct(_, fields, _) => {
-                    let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id);
-                    shorthand_field_ids.extend(ids);
+                    let (short, not_short): (Vec<&_>, Vec<&_>) =
+                        fields.iter().partition(|f| f.is_shorthand);
+                    shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id));
+                    pats.extend(not_short.iter().map(|f| f.pat));
                 }
                 Ref(inner_pat, _) | Box(inner_pat) => {
                     pats.push_back(inner_pat);
@@ -296,6 +299,12 @@ impl IrMaps<'tcx> {
             }
         }
 
+        return shorthand_field_ids;
+    }
+
+    fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) {
+        let shorthand_field_ids = self.collect_shorthand_field_ids(pat);
+
         pat.each_binding(|_, hir_id, _, ident| {
             self.add_live_node_for_node(hir_id, VarDefNode(ident.span, hir_id));
             self.add_variable(Local(LocalInfo {
@@ -373,15 +382,13 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+        let shorthand_field_ids = self.collect_shorthand_field_ids(param.pat);
         param.pat.each_binding(|_bm, hir_id, _x, ident| {
             let var = match param.pat.kind {
-                rustc_hir::PatKind::Struct(_, fields, _) => Local(LocalInfo {
+                rustc_hir::PatKind::Struct(..) => Local(LocalInfo {
                     id: hir_id,
                     name: ident.name,
-                    is_shorthand: fields
-                        .iter()
-                        .find(|f| f.ident == ident)
-                        .map_or(false, |f| f.is_shorthand),
+                    is_shorthand: shorthand_field_ids.contains(&hir_id),
                 }),
                 _ => Param(hir_id, ident.name),
             };
diff --git a/src/test/ui/suggestions/ignore-nested-field-binding.fixed b/src/test/ui/suggestions/ignore-nested-field-binding.fixed
new file mode 100644
index 00000000000..1dc44838e8b
--- /dev/null
+++ b/src/test/ui/suggestions/ignore-nested-field-binding.fixed
@@ -0,0 +1,20 @@
+// Regression test for #88403, where prefixing with an underscore was
+// erroneously suggested for a nested shorthand struct field binding.
+
+// run-rustfix
+#![allow(unused)]
+#![forbid(unused_variables)]
+
+struct Inner { i: i32 }
+struct Outer { o: Inner }
+
+fn foo(Outer { o: Inner { i: _ } }: Outer) {}
+//~^ ERROR: unused variable: `i`
+//~| HELP: try ignoring the field
+
+fn main() {
+    let s = Outer { o: Inner { i: 42 } };
+    let Outer { o: Inner { i: _ } } = s;
+    //~^ ERROR: unused variable: `i`
+    //~| HELP: try ignoring the field
+}
diff --git a/src/test/ui/suggestions/ignore-nested-field-binding.rs b/src/test/ui/suggestions/ignore-nested-field-binding.rs
new file mode 100644
index 00000000000..6dc0263ec9f
--- /dev/null
+++ b/src/test/ui/suggestions/ignore-nested-field-binding.rs
@@ -0,0 +1,20 @@
+// Regression test for #88403, where prefixing with an underscore was
+// erroneously suggested for a nested shorthand struct field binding.
+
+// run-rustfix
+#![allow(unused)]
+#![forbid(unused_variables)]
+
+struct Inner { i: i32 }
+struct Outer { o: Inner }
+
+fn foo(Outer { o: Inner { i } }: Outer) {}
+//~^ ERROR: unused variable: `i`
+//~| HELP: try ignoring the field
+
+fn main() {
+    let s = Outer { o: Inner { i: 42 } };
+    let Outer { o: Inner { i } } = s;
+    //~^ ERROR: unused variable: `i`
+    //~| HELP: try ignoring the field
+}
diff --git a/src/test/ui/suggestions/ignore-nested-field-binding.stderr b/src/test/ui/suggestions/ignore-nested-field-binding.stderr
new file mode 100644
index 00000000000..b2936a22a22
--- /dev/null
+++ b/src/test/ui/suggestions/ignore-nested-field-binding.stderr
@@ -0,0 +1,20 @@
+error: unused variable: `i`
+  --> $DIR/ignore-nested-field-binding.rs:11:27
+   |
+LL | fn foo(Outer { o: Inner { i } }: Outer) {}
+   |                           ^ help: try ignoring the field: `i: _`
+   |
+note: the lint level is defined here
+  --> $DIR/ignore-nested-field-binding.rs:6:11
+   |
+LL | #![forbid(unused_variables)]
+   |           ^^^^^^^^^^^^^^^^
+
+error: unused variable: `i`
+  --> $DIR/ignore-nested-field-binding.rs:17:28
+   |
+LL |     let Outer { o: Inner { i } } = s;
+   |                            ^ help: try ignoring the field: `i: _`
+
+error: aborting due to 2 previous errors
+