Skip to content

Commit 8e51bd4

Browse files
authored
Rollup merge of #81235 - reese:rw-tuple-diagnostics, r=estebank
Improve suggestion for tuple struct pattern matching errors. Closes #80174 This change allows numbers to be parsed as field names when pattern matching on structs, which allows us to provide better error messages when tuple structs are matched using a struct pattern. r? ``@estebank``
2 parents 72e6d51 + d8540ae commit 8e51bd4

File tree

9 files changed

+151
-21
lines changed

9 files changed

+151
-21
lines changed

compiler/rustc_parse/src/parser/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ impl<'a> Parser<'a> {
950950
self.bump();
951951
Ok(Ident::new(symbol, self.prev_token.span))
952952
} else {
953-
self.parse_ident_common(false)
953+
self.parse_ident_common(true)
954954
}
955955
}
956956

compiler/rustc_parse/src/parser/pat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ impl<'a> Parser<'a> {
10281028
let boxed_span = self.token.span;
10291029
let is_ref = self.eat_keyword(kw::Ref);
10301030
let is_mut = self.eat_keyword(kw::Mut);
1031-
let fieldname = self.parse_ident()?;
1031+
let fieldname = self.parse_field_name()?;
10321032
hi = self.prev_token.span;
10331033

10341034
let bind_type = match (is_ref, is_mut) {

compiler/rustc_typeck/src/check/pat.rs

+85-15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_span::source_map::{Span, Spanned};
1717
use rustc_span::symbol::Ident;
1818
use rustc_span::{BytePos, DUMMY_SP};
1919
use rustc_trait_selection::traits::{ObligationCause, Pattern};
20+
use ty::VariantDef;
2021

2122
use std::cmp;
2223
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -1264,14 +1265,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12641265
u.emit();
12651266
}
12661267
}
1267-
(None, Some(mut err)) | (Some(mut err), None) => {
1268+
(None, Some(mut u)) => {
1269+
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
1270+
u.delay_as_bug();
1271+
e.emit();
1272+
} else {
1273+
u.emit();
1274+
}
1275+
}
1276+
(Some(mut err), None) => {
12681277
err.emit();
12691278
}
1270-
(None, None) => {}
1279+
(None, None) => {
1280+
if let Some(mut err) =
1281+
self.error_tuple_variant_index_shorthand(variant, pat, fields)
1282+
{
1283+
err.emit();
1284+
}
1285+
}
12711286
}
12721287
no_field_errors
12731288
}
12741289

1290+
fn error_tuple_variant_index_shorthand(
1291+
&self,
1292+
variant: &VariantDef,
1293+
pat: &'_ Pat<'_>,
1294+
fields: &[hir::FieldPat<'_>],
1295+
) -> Option<DiagnosticBuilder<'_>> {
1296+
// if this is a tuple struct, then all field names will be numbers
1297+
// so if any fields in a struct pattern use shorthand syntax, they will
1298+
// be invalid identifiers (for example, Foo { 0, 1 }).
1299+
if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) =
1300+
(variant.ctor_kind, &pat.kind)
1301+
{
1302+
let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
1303+
if has_shorthand_field_name {
1304+
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1305+
s.print_qpath(qpath, false)
1306+
});
1307+
let mut err = struct_span_err!(
1308+
self.tcx.sess,
1309+
pat.span,
1310+
E0769,
1311+
"tuple variant `{}` written as struct variant",
1312+
path
1313+
);
1314+
err.span_suggestion_verbose(
1315+
qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()),
1316+
"use the tuple variant pattern syntax instead",
1317+
format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)),
1318+
Applicability::MaybeIncorrect,
1319+
);
1320+
return Some(err);
1321+
}
1322+
}
1323+
None
1324+
}
1325+
12751326
fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) {
12761327
let sess = self.tcx.sess;
12771328
let sm = sess.source_map();
@@ -1411,16 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14111462
);
14121463
let (sugg, appl) = if fields.len() == variant.fields.len() {
14131464
(
1414-
fields
1415-
.iter()
1416-
.map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
1417-
Ok(f) => f,
1418-
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1419-
s.print_pat(f.pat)
1420-
}),
1421-
})
1422-
.collect::<Vec<String>>()
1423-
.join(", "),
1465+
self.get_suggested_tuple_struct_pattern(fields, variant),
14241466
Applicability::MachineApplicable,
14251467
)
14261468
} else {
@@ -1429,17 +1471,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14291471
Applicability::MaybeIncorrect,
14301472
)
14311473
};
1432-
err.span_suggestion(
1433-
pat.span,
1474+
err.span_suggestion_verbose(
1475+
qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()),
14341476
"use the tuple variant pattern syntax instead",
1435-
format!("{}({})", path, sugg),
1477+
format!("({})", sugg),
14361478
appl,
14371479
);
14381480
return Some(err);
14391481
}
14401482
None
14411483
}
14421484

1485+
fn get_suggested_tuple_struct_pattern(
1486+
&self,
1487+
fields: &[hir::FieldPat<'_>],
1488+
variant: &VariantDef,
1489+
) -> String {
1490+
let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::<Vec<Ident>>();
1491+
fields
1492+
.iter()
1493+
.map(|field| {
1494+
match self.tcx.sess.source_map().span_to_snippet(field.pat.span) {
1495+
Ok(f) => {
1496+
// Field names are numbers, but numbers
1497+
// are not valid identifiers
1498+
if variant_field_idents.contains(&field.ident) {
1499+
String::from("_")
1500+
} else {
1501+
f
1502+
}
1503+
}
1504+
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1505+
s.print_pat(field.pat)
1506+
}),
1507+
}
1508+
})
1509+
.collect::<Vec<String>>()
1510+
.join(", ")
1511+
}
1512+
14431513
/// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
14441514
/// inaccessible fields.
14451515
///

src/test/ui/issues/issue-17800.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0769]: tuple variant `MyOption::MySome` written as struct variant
22
--> $DIR/issue-17800.rs:8:9
33
|
44
LL | MyOption::MySome { x: 42 } => (),
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `MyOption::MySome(42)`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: use the tuple variant pattern syntax instead
8+
|
9+
LL | MyOption::MySome(42) => (),
10+
| ^^^^
611

712
error: aborting due to previous error
813

src/test/ui/missing/missing-fields-in-struct-pattern.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0769]: tuple variant `S` written as struct variant
22
--> $DIR/missing-fields-in-struct-pattern.rs:4:12
33
|
44
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
5-
| ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
help: use the tuple variant pattern syntax instead
8+
|
9+
LL | if let S(a, b, c, d) = S(1, 2, 3, 4) {
10+
| ^^^^^^^^^^^^
611

712
error: aborting due to previous error
813

src/test/ui/parser/recover-from-bad-variant.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ error[E0769]: tuple variant `Enum::Bar` written as struct variant
2222
--> $DIR/recover-from-bad-variant.rs:12:9
2323
|
2424
LL | Enum::Bar { a, b } => {}
25-
| ^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `Enum::Bar(a, b)`
25+
| ^^^^^^^^^^^^^^^^^^
26+
|
27+
help: use the tuple variant pattern syntax instead
28+
|
29+
LL | Enum::Bar(a, b) => {}
30+
| ^^^^^^
2631

2732
error: aborting due to 3 previous errors
2833

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct S(i32, f32);
2+
enum E {
3+
S(i32, f32),
4+
}
5+
fn main() {
6+
let x = E::S(1, 2.2);
7+
match x {
8+
E::S { 0, 1 } => {}
9+
//~^ ERROR tuple variant `E::S` written as struct variant [E0769]
10+
}
11+
let y = S(1, 2.2);
12+
match y {
13+
S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0769]: tuple variant `E::S` written as struct variant
2+
--> $DIR/struct-tuple-field-names.rs:8:9
3+
|
4+
LL | E::S { 0, 1 } => {}
5+
| ^^^^^^^^^^^^^
6+
|
7+
help: use the tuple variant pattern syntax instead
8+
|
9+
LL | E::S(_, _) => {}
10+
| ^^^^^^
11+
12+
error[E0769]: tuple variant `S` written as struct variant
13+
--> $DIR/struct-tuple-field-names.rs:13:9
14+
|
15+
LL | S { } => {}
16+
| ^^^^^
17+
|
18+
help: use the tuple variant pattern syntax instead
19+
|
20+
LL | S(_, _) => {}
21+
| ^^^^^^
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0769`.

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0769]: tuple variant `X::Y` written as struct variant
22
--> $DIR/issue-41314.rs:7:9
33
|
44
LL | X::Y { number } => {}
5-
| ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
help: use the tuple variant pattern syntax instead
8+
|
9+
LL | X::Y(number) => {}
10+
| ^^^^^^^^
611

712
error: aborting due to previous error
813

0 commit comments

Comments
 (0)