From 2bff77d255b9960489459157900a95a6e5a8279f Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 12 Sep 2021 02:05:07 +0200 Subject: [PATCH] Fix suggestion for nested struct patterns --- compiler/rustc_passes/src/liveness.rs | 23 ++++++++++++------- .../ignore-nested-field-binding.fixed | 20 ++++++++++++++++ .../ignore-nested-field-binding.rs | 20 ++++++++++++++++ .../ignore-nested-field-binding.stderr | 20 ++++++++++++++++ 4 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/suggestions/ignore-nested-field-binding.fixed create mode 100644 src/test/ui/suggestions/ignore-nested-field-binding.rs create mode 100644 src/test/ui/suggestions/ignore-nested-field-binding.stderr diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index ab9bfea96943f..55de2e9d2f89b 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 0000000000000..1dc44838e8bb0 --- /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 0000000000000..6dc0263ec9f2b --- /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 0000000000000..b2936a22a22f1 --- /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 +