]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_typeck/src/check/pat.rs
Account for existing `_` field pattern when suggesting `..`
[rust.git] / compiler / rustc_typeck / src / check / pat.rs
index db2f4eca0c9d063921736bcb1e187b8b2209f43d..12256058b87152ace4c6bcc19e56e483964e1c10 100644 (file)
@@ -20,7 +20,6 @@
 
 use std::cmp;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
-use std::iter;
 
 use super::report_unexpected_variant_res;
 
@@ -1003,7 +1002,7 @@ fn e0023(
         // More generally, the expected type wants a tuple variant with one field of an
         // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
         // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
-        let missing_parenthesis = match (&expected.kind(), fields, had_err) {
+        let missing_parentheses = match (&expected.kind(), fields, had_err) {
             // #67037: only do this if we could successfully type-check the expected type against
             // the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
             // `let P() = U;` where `P != U` with `struct P<T>(T);`.
@@ -1016,13 +1015,13 @@ fn e0023(
             }
             _ => false,
         };
-        if missing_parenthesis {
+        if missing_parentheses {
             let (left, right) = match subpats {
                 // This is the zero case; we aim to get the "hi" part of the `QPath`'s
                 // span as the "lo" and then the "hi" part of the pattern's span as the "hi".
                 // This looks like:
                 //
-                // help: missing parenthesis
+                // help: missing parentheses
                 //   |
                 // L |     let A(()) = A(());
                 //   |          ^  ^
@@ -1031,43 +1030,71 @@ fn e0023(
                 // last sub-pattern. In the case of `A(x)` the first and last may coincide.
                 // This looks like:
                 //
-                // help: missing parenthesis
+                // help: missing parentheses
                 //   |
                 // L |     let A((x, y)) = A((1, 2));
                 //   |           ^    ^
                 [first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span),
             };
             err.multipart_suggestion(
-                "missing parenthesis",
+                "missing parentheses",
                 vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())],
                 Applicability::MachineApplicable,
             );
-        } else if fields.len() > subpats.len() {
-            let after_fields_span = if pat_span == DUMMY_SP {
-                pat_span
-            } else {
-                pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi()
+        } else if fields.len() > subpats.len() && pat_span != DUMMY_SP {
+            let after_fields_span = pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi();
+            let all_fields_span = match subpats {
+                [] => after_fields_span,
+                [field] => field.span,
+                [first, .., last] => first.span.to(last.span),
+            };
+
+            // Check if all the fields in the pattern are wildcards.
+            let all_wildcards = subpats.iter().all(|pat| matches!(pat.kind, PatKind::Wild));
+            let first_tail_wildcard =
+                subpats.iter().enumerate().fold(None, |acc, (pos, pat)| match (acc, &pat.kind) {
+                    (None, PatKind::Wild) => Some(pos),
+                    (Some(_), PatKind::Wild) => acc,
+                    _ => None,
+                });
+            let tail_span = match first_tail_wildcard {
+                None => after_fields_span,
+                Some(0) => subpats[0].span.to(after_fields_span),
+                Some(pos) => subpats[pos - 1].span.shrink_to_hi().to(after_fields_span),
             };
 
+            // FIXME: heuristic-based suggestion to check current types for where to add `_`.
             let mut wildcard_sugg = vec!["_"; fields.len() - subpats.len()].join(", ");
             if !subpats.is_empty() {
                 wildcard_sugg = String::from(", ") + &wildcard_sugg;
             }
 
-            let rest_sugg = if subpats.is_empty() { "..".to_owned() } else { ", ..".to_owned() };
-
-            err.span_suggestion(
+            err.span_suggestion_verbose(
                 after_fields_span,
                 "use `_` to explicitly ignore each field",
                 wildcard_sugg,
                 Applicability::MaybeIncorrect,
             );
-            err.span_suggestion(
-                after_fields_span,
-                "use `..` to ignore all unmentioned fields",
-                rest_sugg,
-                Applicability::MaybeIncorrect,
-            );
+
+            // Only suggest `..` if more than one field is missing
+            // or the pattern consists of all wildcards.
+            if fields.len() - subpats.len() > 1 || all_wildcards {
+                if subpats.is_empty() || all_wildcards {
+                    err.span_suggestion_verbose(
+                        all_fields_span,
+                        "use `..` to ignore all fields",
+                        String::from(".."),
+                        Applicability::MaybeIncorrect,
+                    );
+                } else {
+                    err.span_suggestion_verbose(
+                        tail_span,
+                        "use `..` to ignore the rest of the fields",
+                        String::from(", .."),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
         }
 
         err.emit();
@@ -1467,11 +1494,11 @@ fn error_no_accessible_fields(
     /// Returns a diagnostic reporting a struct pattern which does not mention some fields.
     ///
     /// ```text
-    /// error[E0027]: pattern does not mention field `you_cant_use_this_field`
+    /// error[E0027]: pattern does not mention field `bar`
     ///   --> src/main.rs:15:9
     ///    |
     /// LL |     let foo::Foo {} = foo::Foo::new();
-    ///    |         ^^^^^^^^^^^ missing field `you_cant_use_this_field`
+    ///    |         ^^^^^^^^^^^ missing field `bar`
     /// ```
     fn error_unmentioned_fields(
         &self,
@@ -1505,14 +1532,26 @@ fn error_unmentioned_fields(
                 }
                 _ => return err,
             },
-            [.., field] => (
-                match pat.kind {
-                    PatKind::Struct(_, [_, ..], _) => ", ",
-                    _ => "",
-                },
-                "",
-                field.span.shrink_to_hi(),
-            ),
+            [.., field] => {
+                // if last field has a trailing comma, use the comma
+                // as the span to avoid trailing comma in ultimate
+                // suggestion (Issue #78511)
+                let tail = field.span.shrink_to_hi().until(pat.span.shrink_to_hi());
+                let tail_through_comma = self.tcx.sess.source_map().span_through_char(tail, ',');
+                let sp = if tail_through_comma == tail {
+                    field.span.shrink_to_hi()
+                } else {
+                    tail_through_comma
+                };
+                (
+                    match pat.kind {
+                        PatKind::Struct(_, [_, ..], _) => ", ",
+                        _ => "",
+                    },
+                    "",
+                    sp,
+                )
+            }
         };
         err.span_suggestion(
             sp,