mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-22 12:43:36 +00:00
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``
This commit is contained in:
commit
8e51bd4315
@ -950,7 +950,7 @@ impl<'a> Parser<'a> {
|
||||
self.bump();
|
||||
Ok(Ident::new(symbol, self.prev_token.span))
|
||||
} else {
|
||||
self.parse_ident_common(false)
|
||||
self.parse_ident_common(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1028,7 +1028,7 @@ impl<'a> Parser<'a> {
|
||||
let boxed_span = self.token.span;
|
||||
let is_ref = self.eat_keyword(kw::Ref);
|
||||
let is_mut = self.eat_keyword(kw::Mut);
|
||||
let fieldname = self.parse_ident()?;
|
||||
let fieldname = self.parse_field_name()?;
|
||||
hi = self.prev_token.span;
|
||||
|
||||
let bind_type = match (is_ref, is_mut) {
|
||||
|
@ -17,6 +17,7 @@ use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{BytePos, DUMMY_SP};
|
||||
use rustc_trait_selection::traits::{ObligationCause, Pattern};
|
||||
use ty::VariantDef;
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
@ -1264,14 +1265,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
u.emit();
|
||||
}
|
||||
}
|
||||
(None, Some(mut err)) | (Some(mut err), None) => {
|
||||
(None, Some(mut u)) => {
|
||||
if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
|
||||
u.delay_as_bug();
|
||||
e.emit();
|
||||
} else {
|
||||
u.emit();
|
||||
}
|
||||
}
|
||||
(Some(mut err), None) => {
|
||||
err.emit();
|
||||
}
|
||||
(None, None) => {}
|
||||
(None, None) => {
|
||||
if let Some(mut err) =
|
||||
self.error_tuple_variant_index_shorthand(variant, pat, fields)
|
||||
{
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
no_field_errors
|
||||
}
|
||||
|
||||
fn error_tuple_variant_index_shorthand(
|
||||
&self,
|
||||
variant: &VariantDef,
|
||||
pat: &'_ Pat<'_>,
|
||||
fields: &[hir::FieldPat<'_>],
|
||||
) -> Option<DiagnosticBuilder<'_>> {
|
||||
// if this is a tuple struct, then all field names will be numbers
|
||||
// so if any fields in a struct pattern use shorthand syntax, they will
|
||||
// be invalid identifiers (for example, Foo { 0, 1 }).
|
||||
if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) =
|
||||
(variant.ctor_kind, &pat.kind)
|
||||
{
|
||||
let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
|
||||
if has_shorthand_field_name {
|
||||
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_qpath(qpath, false)
|
||||
});
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
pat.span,
|
||||
E0769,
|
||||
"tuple variant `{}` written as struct variant",
|
||||
path
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()),
|
||||
"use the tuple variant pattern syntax instead",
|
||||
format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return Some(err);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) {
|
||||
let sess = self.tcx.sess;
|
||||
let sm = sess.source_map();
|
||||
@ -1411,16 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
let (sugg, appl) = if fields.len() == variant.fields.len() {
|
||||
(
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
|
||||
Ok(f) => f,
|
||||
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_pat(f.pat)
|
||||
}),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
self.get_suggested_tuple_struct_pattern(fields, variant),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else {
|
||||
@ -1429,10 +1471,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
};
|
||||
err.span_suggestion(
|
||||
pat.span,
|
||||
err.span_suggestion_verbose(
|
||||
qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()),
|
||||
"use the tuple variant pattern syntax instead",
|
||||
format!("{}({})", path, sugg),
|
||||
format!("({})", sugg),
|
||||
appl,
|
||||
);
|
||||
return Some(err);
|
||||
@ -1440,6 +1482,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_suggested_tuple_struct_pattern(
|
||||
&self,
|
||||
fields: &[hir::FieldPat<'_>],
|
||||
variant: &VariantDef,
|
||||
) -> String {
|
||||
let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::<Vec<Ident>>();
|
||||
fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
match self.tcx.sess.source_map().span_to_snippet(field.pat.span) {
|
||||
Ok(f) => {
|
||||
// Field names are numbers, but numbers
|
||||
// are not valid identifiers
|
||||
if variant_field_idents.contains(&field.ident) {
|
||||
String::from("_")
|
||||
} else {
|
||||
f
|
||||
}
|
||||
}
|
||||
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_pat(field.pat)
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
/// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
|
||||
/// inaccessible fields.
|
||||
///
|
||||
|
@ -2,7 +2,12 @@ error[E0769]: tuple variant `MyOption::MySome` written as struct variant
|
||||
--> $DIR/issue-17800.rs:8:9
|
||||
|
|
||||
LL | MyOption::MySome { x: 42 } => (),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `MyOption::MySome(42)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | MyOption::MySome(42) => (),
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -2,7 +2,12 @@ error[E0769]: tuple variant `S` written as struct variant
|
||||
--> $DIR/missing-fields-in-struct-pattern.rs:4:12
|
||||
|
|
||||
LL | if let S { a, b, c, d } = S(1, 2, 3, 4) {
|
||||
| ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | if let S(a, b, c, d) = S(1, 2, 3, 4) {
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -22,7 +22,12 @@ error[E0769]: tuple variant `Enum::Bar` written as struct variant
|
||||
--> $DIR/recover-from-bad-variant.rs:12:9
|
||||
|
|
||||
LL | Enum::Bar { a, b } => {}
|
||||
| ^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `Enum::Bar(a, b)`
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | Enum::Bar(a, b) => {}
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
15
src/test/ui/structs/struct-tuple-field-names.rs
Normal file
15
src/test/ui/structs/struct-tuple-field-names.rs
Normal file
@ -0,0 +1,15 @@
|
||||
struct S(i32, f32);
|
||||
enum E {
|
||||
S(i32, f32),
|
||||
}
|
||||
fn main() {
|
||||
let x = E::S(1, 2.2);
|
||||
match x {
|
||||
E::S { 0, 1 } => {}
|
||||
//~^ ERROR tuple variant `E::S` written as struct variant [E0769]
|
||||
}
|
||||
let y = S(1, 2.2);
|
||||
match y {
|
||||
S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
|
||||
}
|
||||
}
|
25
src/test/ui/structs/struct-tuple-field-names.stderr
Normal file
25
src/test/ui/structs/struct-tuple-field-names.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
error[E0769]: tuple variant `E::S` written as struct variant
|
||||
--> $DIR/struct-tuple-field-names.rs:8:9
|
||||
|
|
||||
LL | E::S { 0, 1 } => {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | E::S(_, _) => {}
|
||||
| ^^^^^^
|
||||
|
||||
error[E0769]: tuple variant `S` written as struct variant
|
||||
--> $DIR/struct-tuple-field-names.rs:13:9
|
||||
|
|
||||
LL | S { } => {}
|
||||
| ^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | S(_, _) => {}
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0769`.
|
@ -2,7 +2,12 @@ error[E0769]: tuple variant `X::Y` written as struct variant
|
||||
--> $DIR/issue-41314.rs:7:9
|
||||
|
|
||||
LL | X::Y { number } => {}
|
||||
| ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use the tuple variant pattern syntax instead
|
||||
|
|
||||
LL | X::Y(number) => {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user