Skip to content

Commit 8a83c8f

Browse files
committed
Improve suggestion for tuple struct pattern matching errors.
Currently, when a user uses a struct pattern to pattern match on a tuple struct, the errors we emit generally suggest adding fields using their field names, which are numbers. However, numbers are not valid identifiers, so the suggestions, which use the shorthand notation, are not valid syntax. This commit changes those errors to suggest using the actual tuple struct pattern syntax instead, which is a more actionable suggestion.
1 parent 058a710 commit 8a83c8f

File tree

5 files changed

+118
-14
lines changed

5 files changed

+118
-14
lines changed

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ impl<'a> Parser<'a> {
932932
self.bump();
933933
Ok(Ident::new(symbol, self.prev_token.span))
934934
} else {
935-
self.parse_ident_common(false)
935+
self.parse_ident_common(true)
936936
}
937937
}
938938

compiler/rustc_parse/src/parser/pat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ impl<'a> Parser<'a> {
999999
let boxed_span = self.token.span;
10001000
let is_ref = self.eat_keyword(kw::Ref);
10011001
let is_mut = self.eat_keyword(kw::Mut);
1002-
let fieldname = self.parse_ident()?;
1002+
let fieldname = self.parse_field_name()?;
10031003
hi = self.prev_token.span;
10041004

10051005
let bind_type = match (is_ref, is_mut) {

compiler/rustc_typeck/src/check/pat.rs

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_span::lev_distance::find_best_match_for_name;
1616
use rustc_span::source_map::{Span, Spanned};
1717
use rustc_span::symbol::Ident;
1818
use rustc_trait_selection::traits::{ObligationCause, Pattern};
19+
use ty::VariantDef;
1920

2021
use std::cmp;
2122
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -1209,14 +1210,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12091210
u.emit();
12101211
}
12111212
}
1212-
(None, Some(mut err)) | (Some(mut err), None) => {
1213+
(None, Some(mut u)) => {
1214+
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
1215+
u.delay_as_bug();
1216+
e.emit();
1217+
} else {
1218+
u.emit();
1219+
}
1220+
}
1221+
(Some(mut err), None) => {
12131222
err.emit();
12141223
}
1215-
(None, None) => {}
1224+
(None, None) => {
1225+
if let Some(mut err) =
1226+
self.error_tuple_variant_index_shorthand(variant, pat, fields)
1227+
{
1228+
err.emit();
1229+
}
1230+
}
12161231
}
12171232
no_field_errors
12181233
}
12191234

1235+
fn error_tuple_variant_index_shorthand(
1236+
&self,
1237+
variant: &VariantDef,
1238+
pat: &'_ Pat<'_>,
1239+
fields: &[hir::FieldPat<'_>],
1240+
) -> Option<DiagnosticBuilder<'_>> {
1241+
// if this is a tuple struct, then all field names will be numbers
1242+
// so if any fields in a struct pattern use shorthand syntax, they will
1243+
// be invalid identifiers (for example, Foo { 0, 1 }).
1244+
if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) =
1245+
(variant.ctor_kind, &pat.kind)
1246+
{
1247+
let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
1248+
if has_shorthand_field_name {
1249+
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1250+
s.print_qpath(qpath, false)
1251+
});
1252+
let mut err = struct_span_err!(
1253+
self.tcx.sess,
1254+
pat.span,
1255+
E0769,
1256+
"tuple variant `{}` uses a bare index in a struct pattern",
1257+
path
1258+
);
1259+
err.span_suggestion(
1260+
pat.span,
1261+
"use the tuple variant pattern syntax instead",
1262+
format!(
1263+
"{}({})",
1264+
path,
1265+
self.get_suggested_tuple_struct_pattern(fields, variant)
1266+
),
1267+
Applicability::MaybeIncorrect,
1268+
);
1269+
return Some(err);
1270+
}
1271+
}
1272+
None
1273+
}
1274+
12201275
fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) {
12211276
let sess = self.tcx.sess;
12221277
let sm = sess.source_map();
@@ -1356,16 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13561411
);
13571412
let (sugg, appl) = if fields.len() == variant.fields.len() {
13581413
(
1359-
fields
1360-
.iter()
1361-
.map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
1362-
Ok(f) => f,
1363-
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1364-
s.print_pat(f.pat)
1365-
}),
1366-
})
1367-
.collect::<Vec<String>>()
1368-
.join(", "),
1414+
self.get_suggested_tuple_struct_pattern(fields, variant),
13691415
Applicability::MachineApplicable,
13701416
)
13711417
} else {
@@ -1385,6 +1431,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13851431
None
13861432
}
13871433

1434+
fn get_suggested_tuple_struct_pattern(
1435+
&self,
1436+
fields: &[hir::FieldPat<'_>],
1437+
variant: &VariantDef,
1438+
) -> String {
1439+
let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::<Vec<Ident>>();
1440+
fields
1441+
.iter()
1442+
.map(|field| {
1443+
match self.tcx.sess.source_map().span_to_snippet(field.pat.span) {
1444+
Ok(f) => {
1445+
// Field names are numbers, but numbers
1446+
// are not valid identifiers
1447+
if variant_field_idents.contains(&field.ident) {
1448+
String::from("_")
1449+
} else {
1450+
f
1451+
}
1452+
}
1453+
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
1454+
s.print_pat(field.pat)
1455+
}),
1456+
}
1457+
})
1458+
.collect::<Vec<String>>()
1459+
.join(", ")
1460+
}
1461+
13881462
/// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
13891463
/// inaccessible fields.
13901464
///
Lines changed: 15 additions & 0 deletions
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` uses a bare index in a struct pattern [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+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0769]: tuple variant `E::S` uses a bare index in a struct pattern
2+
--> $DIR/struct-tuple-field-names.rs:8:9
3+
|
4+
LL | E::S { 0, 1 } => {}
5+
| ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `E::S(_, _)`
6+
7+
error[E0769]: tuple variant `S` written as struct variant
8+
--> $DIR/struct-tuple-field-names.rs:13:9
9+
|
10+
LL | S { } => {}
11+
| ^^^^^ help: use the tuple variant pattern syntax instead: `S(_, _)`
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0769`.

0 commit comments

Comments
 (0)