about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-03-23 09:27:22 -0500
committerAlex Crichton <alex@alexcrichton.com>2018-03-23 10:16:10 -0700
commit401a93096ddc002c867633a7d8d613e8249b6b06 (patch)
tree078f8838c07785dafb75660b4c11fe72a4a8dd4b
parentdb2dde9a41aeba6ab0886ebc09c2918b95dd9ddb (diff)
parent062a46fdd1d49b1ccdc4f713433521463224d7d9 (diff)
downloadrust-401a93096ddc002c867633a7d8d613e8249b6b06.tar.gz
rust-401a93096ddc002c867633a7d8d613e8249b6b06.zip
Rollup merge of #49160 - estebank:issue-47457-missing-fields, r=oli-obk
Reduce the diagnostic spam when multiple fields are missing in pattern

Fix #47457.
-rw-r--r--src/librustc_typeck/check/_match.rs93
-rw-r--r--src/test/compile-fail/struct-pat-derived-error.rs6
-rw-r--r--src/test/ui/error-codes/E0026-teach.stderr2
-rw-r--r--src/test/ui/error-codes/E0026.stderr2
-rw-r--r--src/test/ui/missing-fields-in-struct-pattern.rs19
-rw-r--r--src/test/ui/missing-fields-in-struct-pattern.stderr18
-rw-r--r--src/test/ui/numeric-fields.stderr2
-rw-r--r--src/test/ui/type-check/issue-41314.stderr2
-rw-r--r--src/test/ui/union/union-fields-2.stderr2
9 files changed, 104 insertions, 42 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 379fd93ba2b..7965806af5d 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -904,6 +904,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
         // Keep track of which fields have already appeared in the pattern.
         let mut used_fields = FxHashMap();
 
+        let mut inexistent_fields = vec![];
         // Typecheck each field.
         for &Spanned { node: ref field, span } in fields {
             let field_ty = match used_fields.entry(field.name) {
@@ -927,34 +928,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
                             self.field_ty(span, f, substs)
                         })
                         .unwrap_or_else(|| {
-                            let mut err = struct_span_err!(
-                                tcx.sess,
-                                span,
-                                E0026,
-                                "{} `{}` does not have a field named `{}`",
-                                kind_name,
-                                tcx.item_path_str(variant.did),
-                                field.name
-                            );
-                            err.span_label(span,
-                                           format!("{} `{}` does not have field `{}`",
-                                                   kind_name,
-                                                   tcx.item_path_str(variant.did),
-                                                   field.name));
-                            if tcx.sess.teach(&err.get_code().unwrap()) {
-                                err.note(
-                                    "This error indicates that a struct pattern attempted to \
-                                     extract a non-existent field from a struct. Struct fields \
-                                     are identified by the name used before the colon : so struct \
-                                     patterns should resemble the declaration of the struct type \
-                                     being matched.\n\n\
-                                     If you are using shorthand field patterns but want to refer \
-                                     to the struct field by a different name, you should rename \
-                                     it explicitly."
-                                );
-                            }
-                            err.emit();
-
+                            inexistent_fields.push((span, field.name));
                             tcx.types.err
                         })
                 }
@@ -963,6 +937,47 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
             self.check_pat_walk(&field.pat, field_ty, def_bm, true);
         }
 
+        if inexistent_fields.len() > 0 {
+            let (field_names, t, plural) = if inexistent_fields.len() == 1 {
+                (format!("a field named `{}`", inexistent_fields[0].1), "this", "")
+            } else {
+                (format!("fields named {}",
+                         inexistent_fields.iter()
+                            .map(|(_, name)| format!("`{}`", name))
+                            .collect::<Vec<String>>()
+                            .join(", ")), "these", "s")
+            };
+            let spans = inexistent_fields.iter().map(|(span, _)| *span).collect::<Vec<_>>();
+            let mut err = struct_span_err!(tcx.sess,
+                                           spans,
+                                           E0026,
+                                           "{} `{}` does not have {}",
+                                           kind_name,
+                                           tcx.item_path_str(variant.did),
+                                           field_names);
+            if let Some((span, _)) = inexistent_fields.last() {
+                err.span_label(*span,
+                               format!("{} `{}` does not have {} field{}",
+                                       kind_name,
+                                       tcx.item_path_str(variant.did),
+                                       t,
+                                       plural));
+            }
+            if tcx.sess.teach(&err.get_code().unwrap()) {
+                err.note(
+                    "This error indicates that a struct pattern attempted to \
+                     extract a non-existent field from a struct. Struct fields \
+                     are identified by the name used before the colon : so struct \
+                     patterns should resemble the declaration of the struct type \
+                     being matched.\n\n\
+                     If you are using shorthand field patterns but want to refer \
+                     to the struct field by a different name, you should rename \
+                     it explicitly."
+                );
+            }
+            err.emit();
+        }
+
         // Require `..` if struct has non_exhaustive attribute.
         if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc {
             span_err!(tcx.sess, span, E0638,
@@ -979,13 +994,25 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
                 tcx.sess.span_err(span, "`..` cannot be used in union patterns");
             }
         } else if !etc {
-            for field in variant.fields
+            let unmentioned_fields = variant.fields
                 .iter()
-                .filter(|field| !used_fields.contains_key(&field.name)) {
+                .map(|field| field.name)
+                .filter(|field| !used_fields.contains_key(&field))
+                .collect::<Vec<_>>();
+            if unmentioned_fields.len() > 0 {
+                let field_names = if unmentioned_fields.len() == 1 {
+                    format!("field `{}`", unmentioned_fields[0])
+                } else {
+                    format!("fields {}",
+                            unmentioned_fields.iter()
+                                .map(|name| format!("`{}`", name))
+                                .collect::<Vec<String>>()
+                                .join(", "))
+                };
                 let mut diag = struct_span_err!(tcx.sess, span, E0027,
-                                                "pattern does not mention field `{}`",
-                                                field.name);
-                diag.span_label(span, format!("missing field `{}`", field.name));
+                                                "pattern does not mention {}",
+                                                field_names);
+                diag.span_label(span, format!("missing {}", field_names));
                 if variant.ctor_kind == CtorKind::Fn {
                     diag.note("trying to match a tuple variant with a struct variant pattern");
                 }
diff --git a/src/test/compile-fail/struct-pat-derived-error.rs b/src/test/compile-fail/struct-pat-derived-error.rs
index f525ec37375..d3130c4e831 100644
--- a/src/test/compile-fail/struct-pat-derived-error.rs
+++ b/src/test/compile-fail/struct-pat-derived-error.rs
@@ -16,10 +16,8 @@ struct a {
 impl a {
     fn foo(&self) {
         let a { x, y } = self.d; //~ ERROR no field `d` on type `&a`
-        //~^ ERROR struct `a` does not have a field named `x`
-        //~^^ ERROR struct `a` does not have a field named `y`
-        //~^^^ ERROR pattern does not mention field `b`
-        //~^^^^ ERROR pattern does not mention field `c`
+        //~^ ERROR struct `a` does not have fields named `x`, `y`
+        //~| ERROR pattern does not mention fields `b`, `c`
     }
 }
 
diff --git a/src/test/ui/error-codes/E0026-teach.stderr b/src/test/ui/error-codes/E0026-teach.stderr
index 63d072fe03d..67ea32fba86 100644
--- a/src/test/ui/error-codes/E0026-teach.stderr
+++ b/src/test/ui/error-codes/E0026-teach.stderr
@@ -2,7 +2,7 @@ error[E0026]: struct `Thing` does not have a field named `z`
   --> $DIR/E0026-teach.rs:21:23
    |
 LL |         Thing { x, y, z } => {}
-   |                       ^ struct `Thing` does not have field `z`
+   |                       ^ struct `Thing` does not have this field
    |
    = note: This error indicates that a struct pattern attempted to extract a non-existent field from a struct. Struct fields are identified by the name used before the colon : so struct patterns should resemble the declaration of the struct type being matched.
            
diff --git a/src/test/ui/error-codes/E0026.stderr b/src/test/ui/error-codes/E0026.stderr
index af851951127..9dabbc8a775 100644
--- a/src/test/ui/error-codes/E0026.stderr
+++ b/src/test/ui/error-codes/E0026.stderr
@@ -2,7 +2,7 @@ error[E0026]: struct `Thing` does not have a field named `z`
   --> $DIR/E0026.rs:19:23
    |
 LL |         Thing { x, y, z } => {}
-   |                       ^ struct `Thing` does not have field `z`
+   |                       ^ struct `Thing` does not have this field
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/missing-fields-in-struct-pattern.rs b/src/test/ui/missing-fields-in-struct-pattern.rs
new file mode 100644
index 00000000000..dfde3799499
--- /dev/null
+++ b/src/test/ui/missing-fields-in-struct-pattern.rs
@@ -0,0 +1,19 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S(usize, usize, usize, usize);
+
+fn main() {
+    if let S { a, b, c, d } = S(1, 2, 3, 4) {
+    //~^ ERROR struct `S` does not have fields named `a`, `b`, `c`, `d` [E0026]
+    //~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
+        println!("hi");
+    }
+}
diff --git a/src/test/ui/missing-fields-in-struct-pattern.stderr b/src/test/ui/missing-fields-in-struct-pattern.stderr
new file mode 100644
index 00000000000..d1c3260f11e
--- /dev/null
+++ b/src/test/ui/missing-fields-in-struct-pattern.stderr
@@ -0,0 +1,18 @@
+error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
+  --> $DIR/missing-fields-in-struct-pattern.rs:14:16
+   |
+LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
+   |                ^  ^  ^  ^ struct `S` does not have these fields
+
+error[E0027]: pattern does not mention fields `0`, `1`, `2`, `3`
+  --> $DIR/missing-fields-in-struct-pattern.rs:14:12
+   |
+LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
+   |            ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
+   |
+   = note: trying to match a tuple variant with a struct variant pattern
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0026, E0027.
+For more information about an error, try `rustc --explain E0026`.
diff --git a/src/test/ui/numeric-fields.stderr b/src/test/ui/numeric-fields.stderr
index 607980ba3bf..68a87da8ded 100644
--- a/src/test/ui/numeric-fields.stderr
+++ b/src/test/ui/numeric-fields.stderr
@@ -10,7 +10,7 @@ error[E0026]: struct `S` does not have a field named `0x1`
   --> $DIR/numeric-fields.rs:17:17
    |
 LL |         S{0: a, 0x1: b, ..} => {}
-   |                 ^^^^^^ struct `S` does not have field `0x1`
+   |                 ^^^^^^ struct `S` does not have this field
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/type-check/issue-41314.stderr b/src/test/ui/type-check/issue-41314.stderr
index bcb0f9a99a7..f7d4bb9a02f 100644
--- a/src/test/ui/type-check/issue-41314.stderr
+++ b/src/test/ui/type-check/issue-41314.stderr
@@ -2,7 +2,7 @@ error[E0026]: variant `X::Y` does not have a field named `number`
   --> $DIR/issue-41314.rs:17:16
    |
 LL |         X::Y { number } => {} //~ ERROR does not have a field named `number`
-   |                ^^^^^^ variant `X::Y` does not have field `number`
+   |                ^^^^^^ variant `X::Y` does not have this field
 
 error[E0027]: pattern does not mention field `0`
   --> $DIR/issue-41314.rs:17:9
diff --git a/src/test/ui/union/union-fields-2.stderr b/src/test/ui/union/union-fields-2.stderr
index 3ea4d3426da..cfb5bc7520b 100644
--- a/src/test/ui/union/union-fields-2.stderr
+++ b/src/test/ui/union/union-fields-2.stderr
@@ -52,7 +52,7 @@ error[E0026]: union `U` does not have a field named `c`
   --> $DIR/union-fields-2.rs:28:19
    |
 LL |     let U { a, b, c } = u; //~ ERROR union patterns should have exactly one field
-   |                   ^ union `U` does not have field `c`
+   |                   ^ union `U` does not have this field
 
 error: union patterns should have exactly one field
   --> $DIR/union-fields-2.rs:28:9