about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-04-09 20:57:06 +0000
committerbors <bors@rust-lang.org>2023-04-09 20:57:06 +0000
commit696aaad58c57a589f6fb2ecff5bae2eec581cb71 (patch)
treee7e3dba6b267106715eac55d28834b6dec753cc8
parent39bf7777aab9ac1f6b0802cd52cd91d6e021aa91 (diff)
parent3b38dd91126a647334af07d772ad809422918e5a (diff)
downloadrust-696aaad58c57a589f6fb2ecff5bae2eec581cb71.tar.gz
rust-696aaad58c57a589f6fb2ecff5bae2eec581cb71.zip
Auto merge of #109760 - MaciejWas:struct-tuple-field-names-suggestion, r=jackh726
Better diagnostic when pattern matching tuple structs

Fixes #108284

When trying to pattern match a tuple struct we might get a flawed error message if there are missing fields. E.g.

```
let x = Foo(100, 200);
if let Foo { 0: bar } = x { ... }
```

Produces this error:

```
error[E0769]: tuple variant `Foo` written as struct variant
 --> hello.rs:5:12
  |
5 |     if let Foo { 0: foo } = x {
  |            ^^^^^^^^^^^^^^
  |
help: use the tuple variant pattern syntax instead
  |
5 |     if let Foo(_, _) = x {
  |               ~~~~~~
```

Which doesn't highlight that we can still use the struct syntax but we need to fill missing fields. This pr changes this error to:

```
error[E0027]: pattern does not mention field `1`
 --> hello.rs:5:12
  |
5 |     if let Foo { 0: foo } = x {
  |            ^^^^^^^^^^^^^^ missing field `1`
  |
help: include the missing field in the pattern
  |
5 |     if let Foo { 0: foo, 1: _ } = x {
  |                        ~~~~~~~~
help: if you don't care about this missing field, you can explicitly ignore it
  |
5 |     if let Foo { 0: foo, .. } = x {
  |                        ~~~~~~
```
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs23
-rw-r--r--tests/ui/structs/struct-tuple-field-names.rs3
-rw-r--r--tests/ui/structs/struct-tuple-field-names.stderr20
3 files changed, 42 insertions, 4 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 241535b29c5..af0bd26dec5 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -37,6 +37,10 @@ pointers. If you encounter this error you should try to avoid dereferencing the
 You can read more about trait objects in the Trait Objects section of the Reference: \
 https://doc.rust-lang.org/reference/types.html#trait-objects";
 
+fn is_number(text: &str) -> bool {
+    text.chars().all(|c: char| c.is_digit(10))
+}
+
 /// Information about the expected type at the top level of type checking a pattern.
 ///
 /// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic!
@@ -1673,7 +1677,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &'tcx [hir::PatField<'tcx>],
         variant: &ty::VariantDef,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        if let (Some(CtorKind::Fn), PatKind::Struct(qpath, ..)) = (variant.ctor_kind(), &pat.kind) {
+        if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) =
+            (variant.ctor_kind(), &pat.kind)
+        {
+            let is_tuple_struct_match = !pattern_fields.is_empty()
+                && pattern_fields.iter().map(|field| field.ident.name.as_str()).all(is_number);
+            if is_tuple_struct_match {
+                return None;
+            }
+
             let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
                 s.print_qpath(qpath, false)
             });
@@ -1895,7 +1907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 prefix,
                 unmentioned_fields
                     .iter()
-                    .map(|(_, name)| name.to_string())
+                    .map(|(_, name)| {
+                        let field_name = name.to_string();
+                        if is_number(&field_name) {
+                            format!("{}: _", field_name)
+                        } else {
+                            field_name
+                        }
+                    })
                     .collect::<Vec<_>>()
                     .join(", "),
                 if have_inaccessible_fields { ", .." } else { "" },
diff --git a/tests/ui/structs/struct-tuple-field-names.rs b/tests/ui/structs/struct-tuple-field-names.rs
index 7bd54af1dbe..33f264aa250 100644
--- a/tests/ui/structs/struct-tuple-field-names.rs
+++ b/tests/ui/structs/struct-tuple-field-names.rs
@@ -12,4 +12,7 @@ fn main() {
     match y {
         S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
     }
+
+    if let E::S { 0: a } = x { //~ ERROR: pattern does not mention field `1`
+    }
 }
diff --git a/tests/ui/structs/struct-tuple-field-names.stderr b/tests/ui/structs/struct-tuple-field-names.stderr
index 5494c29a6fd..0b837a47a82 100644
--- a/tests/ui/structs/struct-tuple-field-names.stderr
+++ b/tests/ui/structs/struct-tuple-field-names.stderr
@@ -20,6 +20,22 @@ help: use the tuple variant pattern syntax instead
 LL |         S(_, _) => {}
    |          ~~~~~~
 
-error: aborting due to 2 previous errors
+error[E0027]: pattern does not mention field `1`
+  --> $DIR/struct-tuple-field-names.rs:16:12
+   |
+LL |     if let E::S { 0: a } = x {
+   |            ^^^^^^^^^^^^^ missing field `1`
+   |
+help: include the missing field in the pattern
+   |
+LL |     if let E::S { 0: a, 1: _ } = x {
+   |                       ~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     if let E::S { 0: a, .. } = x {
+   |                       ~~~~~~
+
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0769`.
+Some errors have detailed explanations: E0027, E0769.
+For more information about an error, try `rustc --explain E0027`.