From a59cc5774b2b599e697db5175c331f5f98e42001 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 17 Apr 2022 01:20:11 +0900 Subject: [PATCH 1/3] fix an invalid error for a suggestion to add a slice in pattern-matching --- compiler/rustc_typeck/src/check/pat.rs | 93 ++++++++++--------- .../pattern-struct-with-slice-vec-field.rs | 13 +++ ...pattern-struct-with-slice-vec-field.stderr | 9 ++ 3 files changed, 69 insertions(+), 46 deletions(-) create mode 100644 src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs create mode 100644 src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 0baca9048b4cd..deaa0c7c7419e 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -2044,63 +2044,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.sess, span, E0529, - "expected an array or slice, found `{}`", - expected_ty + "expected an array or slice, found `{expected_ty}`" ); - if let ty::Ref(_, ty, _) = expected_ty.kind() { - if let ty::Array(..) | ty::Slice(..) = ty.kind() { - err.help("the semantics of slice patterns changed recently; see issue #62254"); - } + if let ty::Ref(_, ty, _) = expected_ty.kind() + && let ty::Array(..) | ty::Slice(..) = ty.kind() + { + err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) + && let (Some(span), true) = (ti.span, ti.origin_expr) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - if let (Some(span), true) = (ti.span, ti.origin_expr) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let applicability = Autoderef::new( - &self.infcx, - self.param_env, - self.body_id, - span, - self.resolve_vars_if_possible(ti.expected), - span, - ) - .find_map(|(ty, _)| { - match ty.kind() { - ty::Adt(adt_def, _) - if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) - || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => - { - // Slicing won't work here, but `.as_deref()` might (issue #91328). - err.span_suggestion( - span, - "consider using `as_deref` here", - format!("{}.as_deref()", snippet), - Applicability::MaybeIncorrect, - ); - Some(None) - } - - ty::Slice(..) | ty::Array(..) => { - Some(Some(Applicability::MachineApplicable)) - } - - _ => None, - } - }) - .unwrap_or(Some(Applicability::MaybeIncorrect)); - - if let Some(applicability) = applicability { + let any_target_ty = Autoderef::new( + &self.infcx, + self.param_env, + self.body_id, + span, + self.resolve_vars_if_possible(ti.expected), + span, + ) + .any(|(ty, _)| { + debug!("kind={:?}", ty.kind()); + match ty.kind() { + ty::Adt(adt_def, _) + if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) + || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => + { + // Slicing won't work here, but `.as_deref()` might (issue #91328). err.span_suggestion( span, - "consider slicing here", - format!("{}[..]", snippet), - applicability, + "consider using `as_deref` here", + format!("{snippet}.as_deref()"), + Applicability::MaybeIncorrect, ); + false } + _ => self.is_slice_or_array_or_vector(ty), } + }); + + if any_target_ty { + err.span_suggestion( + span, + "consider slicing here", + format!("{snippet}[..]"), + Applicability::MachineApplicable, + ); } } - err.span_label(span, format!("pattern cannot match with input type `{}`", expected_ty)); + err.span_label(span, format!("pattern cannot match with input type `{expected_ty}`")); err.emit(); } + + fn is_slice_or_array_or_vector(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => true, + ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(*ty), + ty::Slice(..) | ty::Array(..) => true, + _ => false, + } + } } diff --git a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs new file mode 100644 index 0000000000000..1a9fc2f0050a7 --- /dev/null +++ b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs @@ -0,0 +1,13 @@ +struct Foo { + v: Vec, +} + +fn f(foo: &Foo) { + match foo { + Foo { v: [1, 2] } => {} + //~^ ERROR expected an array or slice, found `Vec + _ => {} + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr new file mode 100644 index 0000000000000..cb408d38fd276 --- /dev/null +++ b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr @@ -0,0 +1,9 @@ +error[E0529]: expected an array or slice, found `Vec` + --> $DIR/pattern-struct-with-slice-vec-field.rs:7:18 + | +LL | Foo { v: [1, 2] } => {} + | ^^^^^^ pattern cannot match with input type `Vec` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0529`. From 5924ef874ebee33894cd34be07f88ed762f32385 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com> Date: Mon, 18 Apr 2022 12:51:12 +0900 Subject: [PATCH 2/3] stop using `Autoderef` --- compiler/rustc_typeck/src/check/pat.rs | 62 ++++++++++++-------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index deaa0c7c7419e..151adb69aa932 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -2055,35 +2055,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let (Some(span), true) = (ti.span, ti.origin_expr) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let any_target_ty = Autoderef::new( - &self.infcx, - self.param_env, - self.body_id, - span, - self.resolve_vars_if_possible(ti.expected), - span, - ) - .any(|(ty, _)| { - debug!("kind={:?}", ty.kind()); - match ty.kind() { - ty::Adt(adt_def, _) - if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) - || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => - { - // Slicing won't work here, but `.as_deref()` might (issue #91328). - err.span_suggestion( - span, - "consider using `as_deref` here", - format!("{snippet}.as_deref()"), - Applicability::MaybeIncorrect, - ); - false - } - _ => self.is_slice_or_array_or_vector(ty), + let ty = self.resolve_vars_if_possible(ti.expected); + let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(&mut err, snippet.clone(), ty); + match is_slice_or_array_or_vector.1.kind() { + ty::Adt(adt_def, _) + if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) + || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => + { + // Slicing won't work here, but `.as_deref()` might (issue #91328). + err.span_suggestion( + span, + "consider using `as_deref` here", + format!("{snippet}.as_deref()"), + Applicability::MaybeIncorrect, + ); } - }); - - if any_target_ty { + _ => () + } + if is_slice_or_array_or_vector.0 { err.span_suggestion( span, "consider slicing here", @@ -2096,12 +2085,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn is_slice_or_array_or_vector(&self, ty: Ty<'tcx>) -> bool { + fn is_slice_or_array_or_vector( + &self, + err: &mut Diagnostic, + snippet: String, + ty: Ty<'tcx>, + ) -> (bool, Ty<'tcx>) { match ty.kind() { - ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => true, - ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(*ty), - ty::Slice(..) | ty::Array(..) => true, - _ => false, + ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => { + (true, ty) + } + ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(err, snippet, *ty), + ty::Slice(..) | ty::Array(..) => (true, ty), + _ => (false, ty), } } } From efe438b47441203c8e881e87f2f2e29db76d3ff7 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com> Date: Mon, 18 Apr 2022 13:08:23 +0900 Subject: [PATCH 3/3] implement `Deref` for `Bar` --- .../pattern-struct-with-slice-vec-field.rs | 22 +++++++++++++++++++ ...pattern-struct-with-slice-vec-field.stderr | 10 +++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs index 1a9fc2f0050a7..5b223a91f508f 100644 --- a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs +++ b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.rs @@ -1,7 +1,21 @@ +use std::ops::Deref; + struct Foo { v: Vec, } +struct Bar { + v: Vec, +} + +impl Deref for Bar { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.v + } +} + fn f(foo: &Foo) { match foo { Foo { v: [1, 2] } => {} @@ -10,4 +24,12 @@ fn f(foo: &Foo) { } } +fn bar(bar: &Bar) { + match bar { + Bar { v: [1, 2] } => {} + //~^ ERROR expected an array or slice, found `Vec + _ => {} + } +} + fn main() {} diff --git a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr index cb408d38fd276..5b48a8b18a514 100644 --- a/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr +++ b/src/test/ui/suggestions/pattern-struct-with-slice-vec-field.stderr @@ -1,9 +1,15 @@ error[E0529]: expected an array or slice, found `Vec` - --> $DIR/pattern-struct-with-slice-vec-field.rs:7:18 + --> $DIR/pattern-struct-with-slice-vec-field.rs:21:18 | LL | Foo { v: [1, 2] } => {} | ^^^^^^ pattern cannot match with input type `Vec` -error: aborting due to previous error +error[E0529]: expected an array or slice, found `Vec` + --> $DIR/pattern-struct-with-slice-vec-field.rs:29:18 + | +LL | Bar { v: [1, 2] } => {} + | ^^^^^^ pattern cannot match with input type `Vec` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0529`.