Skip to content

Commit c783019

Browse files
authored
Rollup merge of rust-lang#49160 - estebank:issue-47457-missing-fields, r=oli-obk
Reduce the diagnostic spam when multiple fields are missing in pattern Fix rust-lang#47457.
2 parents a662d20 + 062a46f commit c783019

9 files changed

+104
-42
lines changed

src/librustc_typeck/check/_match.rs

+60-33
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
904904
// Keep track of which fields have already appeared in the pattern.
905905
let mut used_fields = FxHashMap();
906906

907+
let mut inexistent_fields = vec![];
907908
// Typecheck each field.
908909
for &Spanned { node: ref field, span } in fields {
909910
let field_ty = match used_fields.entry(field.name) {
@@ -927,34 +928,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
927928
self.field_ty(span, f, substs)
928929
})
929930
.unwrap_or_else(|| {
930-
let mut err = struct_span_err!(
931-
tcx.sess,
932-
span,
933-
E0026,
934-
"{} `{}` does not have a field named `{}`",
935-
kind_name,
936-
tcx.item_path_str(variant.did),
937-
field.name
938-
);
939-
err.span_label(span,
940-
format!("{} `{}` does not have field `{}`",
941-
kind_name,
942-
tcx.item_path_str(variant.did),
943-
field.name));
944-
if tcx.sess.teach(&err.get_code().unwrap()) {
945-
err.note(
946-
"This error indicates that a struct pattern attempted to \
947-
extract a non-existent field from a struct. Struct fields \
948-
are identified by the name used before the colon : so struct \
949-
patterns should resemble the declaration of the struct type \
950-
being matched.\n\n\
951-
If you are using shorthand field patterns but want to refer \
952-
to the struct field by a different name, you should rename \
953-
it explicitly."
954-
);
955-
}
956-
err.emit();
957-
931+
inexistent_fields.push((span, field.name));
958932
tcx.types.err
959933
})
960934
}
@@ -963,6 +937,47 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
963937
self.check_pat_walk(&field.pat, field_ty, def_bm, true);
964938
}
965939

940+
if inexistent_fields.len() > 0 {
941+
let (field_names, t, plural) = if inexistent_fields.len() == 1 {
942+
(format!("a field named `{}`", inexistent_fields[0].1), "this", "")
943+
} else {
944+
(format!("fields named {}",
945+
inexistent_fields.iter()
946+
.map(|(_, name)| format!("`{}`", name))
947+
.collect::<Vec<String>>()
948+
.join(", ")), "these", "s")
949+
};
950+
let spans = inexistent_fields.iter().map(|(span, _)| *span).collect::<Vec<_>>();
951+
let mut err = struct_span_err!(tcx.sess,
952+
spans,
953+
E0026,
954+
"{} `{}` does not have {}",
955+
kind_name,
956+
tcx.item_path_str(variant.did),
957+
field_names);
958+
if let Some((span, _)) = inexistent_fields.last() {
959+
err.span_label(*span,
960+
format!("{} `{}` does not have {} field{}",
961+
kind_name,
962+
tcx.item_path_str(variant.did),
963+
t,
964+
plural));
965+
}
966+
if tcx.sess.teach(&err.get_code().unwrap()) {
967+
err.note(
968+
"This error indicates that a struct pattern attempted to \
969+
extract a non-existent field from a struct. Struct fields \
970+
are identified by the name used before the colon : so struct \
971+
patterns should resemble the declaration of the struct type \
972+
being matched.\n\n\
973+
If you are using shorthand field patterns but want to refer \
974+
to the struct field by a different name, you should rename \
975+
it explicitly."
976+
);
977+
}
978+
err.emit();
979+
}
980+
966981
// Require `..` if struct has non_exhaustive attribute.
967982
if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc {
968983
span_err!(tcx.sess, span, E0638,
@@ -979,13 +994,25 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
979994
tcx.sess.span_err(span, "`..` cannot be used in union patterns");
980995
}
981996
} else if !etc {
982-
for field in variant.fields
997+
let unmentioned_fields = variant.fields
983998
.iter()
984-
.filter(|field| !used_fields.contains_key(&field.name)) {
999+
.map(|field| field.name)
1000+
.filter(|field| !used_fields.contains_key(&field))
1001+
.collect::<Vec<_>>();
1002+
if unmentioned_fields.len() > 0 {
1003+
let field_names = if unmentioned_fields.len() == 1 {
1004+
format!("field `{}`", unmentioned_fields[0])
1005+
} else {
1006+
format!("fields {}",
1007+
unmentioned_fields.iter()
1008+
.map(|name| format!("`{}`", name))
1009+
.collect::<Vec<String>>()
1010+
.join(", "))
1011+
};
9851012
let mut diag = struct_span_err!(tcx.sess, span, E0027,
986-
"pattern does not mention field `{}`",
987-
field.name);
988-
diag.span_label(span, format!("missing field `{}`", field.name));
1013+
"pattern does not mention {}",
1014+
field_names);
1015+
diag.span_label(span, format!("missing {}", field_names));
9891016
if variant.ctor_kind == CtorKind::Fn {
9901017
diag.note("trying to match a tuple variant with a struct variant pattern");
9911018
}

src/test/compile-fail/struct-pat-derived-error.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ struct a {
1616
impl a {
1717
fn foo(&self) {
1818
let a { x, y } = self.d; //~ ERROR no field `d` on type `&a`
19-
//~^ ERROR struct `a` does not have a field named `x`
20-
//~^^ ERROR struct `a` does not have a field named `y`
21-
//~^^^ ERROR pattern does not mention field `b`
22-
//~^^^^ ERROR pattern does not mention field `c`
19+
//~^ ERROR struct `a` does not have fields named `x`, `y`
20+
//~| ERROR pattern does not mention fields `b`, `c`
2321
}
2422
}
2523

src/test/ui/error-codes/E0026-teach.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0026]: struct `Thing` does not have a field named `z`
22
--> $DIR/E0026-teach.rs:21:23
33
|
44
LL | Thing { x, y, z } => {}
5-
| ^ struct `Thing` does not have field `z`
5+
| ^ struct `Thing` does not have this field
66
|
77
= 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.
88

src/test/ui/error-codes/E0026.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0026]: struct `Thing` does not have a field named `z`
22
--> $DIR/E0026.rs:19:23
33
|
44
LL | Thing { x, y, z } => {}
5-
| ^ struct `Thing` does not have field `z`
5+
| ^ struct `Thing` does not have this field
66

77
error: aborting due to previous error
88

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct S(usize, usize, usize, usize);
12+
13+
fn main() {
14+
if let S { a, b, c, d } = S(1, 2, 3, 4) {
15+
//~^ ERROR struct `S` does not have fields named `a`, `b`, `c`, `d` [E0026]
16+
//~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
17+
println!("hi");
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
2+
--> $DIR/missing-fields-in-struct-pattern.rs:14:16
3+
|
4+
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
5+
| ^ ^ ^ ^ struct `S` does not have these fields
6+
7+
error[E0027]: pattern does not mention fields `0`, `1`, `2`, `3`
8+
--> $DIR/missing-fields-in-struct-pattern.rs:14:12
9+
|
10+
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
11+
| ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
12+
|
13+
= note: trying to match a tuple variant with a struct variant pattern
14+
15+
error: aborting due to 2 previous errors
16+
17+
Some errors occurred: E0026, E0027.
18+
For more information about an error, try `rustc --explain E0026`.

src/test/ui/numeric-fields.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error[E0026]: struct `S` does not have a field named `0x1`
1010
--> $DIR/numeric-fields.rs:17:17
1111
|
1212
LL | S{0: a, 0x1: b, ..} => {}
13-
| ^^^^^^ struct `S` does not have field `0x1`
13+
| ^^^^^^ struct `S` does not have this field
1414

1515
error: aborting due to 2 previous errors
1616

src/test/ui/type-check/issue-41314.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0026]: variant `X::Y` does not have a field named `number`
22
--> $DIR/issue-41314.rs:17:16
33
|
44
LL | X::Y { number } => {} //~ ERROR does not have a field named `number`
5-
| ^^^^^^ variant `X::Y` does not have field `number`
5+
| ^^^^^^ variant `X::Y` does not have this field
66

77
error[E0027]: pattern does not mention field `0`
88
--> $DIR/issue-41314.rs:17:9

src/test/ui/union/union-fields-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ error[E0026]: union `U` does not have a field named `c`
5252
--> $DIR/union-fields-2.rs:28:19
5353
|
5454
LL | let U { a, b, c } = u; //~ ERROR union patterns should have exactly one field
55-
| ^ union `U` does not have field `c`
55+
| ^ union `U` does not have this field
5656

5757
error: union patterns should have exactly one field
5858
--> $DIR/union-fields-2.rs:28:9

0 commit comments

Comments
 (0)