about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-03-17 14:04:14 +0100
committerGitHub <noreply@github.com>2024-03-17 14:04:14 +0100
commit3ec2b7bd1d08687e83ea8a1e0aa01d3c85bdc720 (patch)
tree734a9ebb89a638247c182ca54c260dca321a79bb
parentecdea9e9438cdb8cf707e7f60e066eda02344d05 (diff)
parent78e94cba7733d52f0601d853be15622361d94e44 (diff)
downloadrust-3ec2b7bd1d08687e83ea8a1e0aa01d3c85bdc720.tar.gz
rust-3ec2b7bd1d08687e83ea8a1e0aa01d3c85bdc720.zip
Rollup merge of #121236 - long-long-float:rust-fix-consider-slicing, r=Nadrieril
Don't show suggestion if slice pattern is not top-level

Close #120605

Don't show suggestion to add slicing (`[..]`) if the slice pattern is enclosed by struct like `Struct { a: [] }`.

For example, current rustc makes a suggestion as a comment. However, the pattern `a: []` is wrong, not scrutinee `&self.a`.
In this case, the structure type `a: Vec<Struct>` and the pattern `a: []` are different so I think the pattern should be fixed, not the scrutinee.
If the parent of the pattern that was the target of the error is a structure, I made the compiler not show a suggestion.

```rs
pub struct Struct {
    a: Vec<Struct>,
}

impl Struct {
    pub fn test(&self) {
        if let [Struct { a: [] }] = &self.a {
//             ^^^^^^^^^^^^^^^^^^   ------- help: consider slicing here: `&self.a[..]`
            println!("matches!")
        }
    }
}
```

Note:

* ~~I created `PatInfo.history` to store parent-child relationships for patterns, but this may be inefficient.~~
  * I use two fields `parent_kind` and `current_kind` instead of vec. It may not performance issue.
* Currently only looking at direct parents, but may need to look at deeper ancestry.
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs40
-rw-r--r--tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs20
-rw-r--r--tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr23
3 files changed, 70 insertions, 13 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index bb963ad7a39..55372950368 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -83,6 +83,9 @@ struct PatInfo<'tcx, 'a> {
     binding_mode: BindingMode,
     top_info: TopInfo<'tcx>,
     decl_origin: Option<DeclOrigin<'a>>,
+
+    /// The depth of current pattern
+    current_depth: u32,
 }
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
@@ -152,7 +155,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         decl_origin: Option<DeclOrigin<'tcx>>,
     ) {
         let info = TopInfo { expected, origin_expr, span };
-        let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin };
+        let pat_info =
+            PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 };
         self.check_pat(pat, expected, pat_info);
     }
 
@@ -163,7 +167,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Conversely, inside this module, `check_pat_top` should never be used.
     #[instrument(level = "debug", skip(self, pat_info))]
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
-        let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info;
+        let PatInfo { binding_mode: def_bm, top_info: ti, current_depth, .. } = pat_info;
+
         let path_res = match &pat.kind {
             PatKind::Path(qpath) => Some(
                 self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None),
@@ -172,8 +177,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
         let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
-        let pat_info =
-            PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin };
+        let pat_info = PatInfo {
+            binding_mode: def_bm,
+            top_info: ti,
+            decl_origin: pat_info.decl_origin,
+            current_depth: current_depth + 1,
+        };
 
         let ty = match pat.kind {
             PatKind::Wild | PatKind::Err(_) => expected,
@@ -1046,14 +1055,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
-        let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info;
+        let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info;
         let tcx = self.tcx;
         let on_error = |e| {
             for pat in subpats {
                 self.check_pat(
                     pat,
                     Ty::new_error(tcx, e),
-                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin },
+                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
                 );
             }
         };
@@ -1120,7 +1129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_pat(
                     subpat,
                     field_ty,
-                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin },
+                    PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
                 );
 
                 self.tcx.check_stability(
@@ -2134,7 +2143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // The expected type must be an array or slice, but was neither, so error.
             _ => {
                 let guar = expected.error_reported().err().unwrap_or_else(|| {
-                    self.error_expected_array_or_slice(span, expected, pat_info.top_info)
+                    self.error_expected_array_or_slice(span, expected, pat_info)
                 });
                 let err = Ty::new_error(self.tcx, guar);
                 (err, Some(err), err)
@@ -2273,8 +2282,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         span: Span,
         expected_ty: Ty<'tcx>,
-        ti: TopInfo<'tcx>,
+        pat_info: PatInfo<'tcx, '_>,
     ) -> ErrorGuaranteed {
+        let PatInfo { top_info: ti, current_depth, .. } = pat_info;
+
         let mut err = struct_span_code_err!(
             self.dcx(),
             span,
@@ -2292,9 +2303,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && let Some(_) = ti.origin_expr
             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
         {
-            let ty = self.resolve_vars_if_possible(ti.expected);
-            let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(ty);
-            match is_slice_or_array_or_vector.1.kind() {
+            let resolved_ty = self.resolve_vars_if_possible(ti.expected);
+            let (is_slice_or_array_or_vector, resolved_ty) =
+                self.is_slice_or_array_or_vector(resolved_ty);
+            match resolved_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()) =>
@@ -2309,7 +2321,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 _ => (),
             }
-            if is_slice_or_array_or_vector.0 {
+
+            let is_top_level = current_depth <= 1;
+            if is_slice_or_array_or_vector && is_top_level {
                 err.span_suggestion(
                     span,
                     "consider slicing here",
diff --git a/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs
new file mode 100644
index 00000000000..135535cd00a
--- /dev/null
+++ b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs
@@ -0,0 +1,20 @@
+pub struct Struct {
+    a: Vec<Struct>,
+}
+
+impl Struct {
+    pub fn test(&self) {
+        if let [Struct { a: [] }] = &self.a {
+            //~^ ERROR expected an array or slice
+            //~| ERROR expected an array or slice
+            println!("matches!")
+        }
+
+        if let [Struct { a: [] }] = &self.a[..] {
+            //~^ ERROR expected an array or slice
+            println!("matches!")
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr
new file mode 100644
index 00000000000..c28d67604da
--- /dev/null
+++ b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr
@@ -0,0 +1,23 @@
+error[E0529]: expected an array or slice, found `Vec<Struct>`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:7:16
+   |
+LL |         if let [Struct { a: [] }] = &self.a {
+   |                ^^^^^^^^^^^^^^^^^^   ------- help: consider slicing here: `&self.a[..]`
+   |                |
+   |                pattern cannot match with input type `Vec<Struct>`
+
+error[E0529]: expected an array or slice, found `Vec<Struct>`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:7:29
+   |
+LL |         if let [Struct { a: [] }] = &self.a {
+   |                             ^^ pattern cannot match with input type `Vec<Struct>`
+
+error[E0529]: expected an array or slice, found `Vec<Struct>`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:13:29
+   |
+LL |         if let [Struct { a: [] }] = &self.a[..] {
+   |                             ^^ pattern cannot match with input type `Vec<Struct>`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0529`.