mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-22 12:43:36 +00:00
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.
This commit is contained in:
parent
058a710165
commit
8a83c8f64f
@ -932,7 +932,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,7 +999,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) {
|
||||
|
@ -16,6 +16,7 @@ use rustc_span::lev_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_trait_selection::traits::{ObligationCause, Pattern};
|
||||
use ty::VariantDef;
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
@ -1209,14 +1210,68 @@ 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 `{}` uses a bare index in a struct pattern",
|
||||
path
|
||||
);
|
||||
err.span_suggestion(
|
||||
pat.span,
|
||||
"use the tuple variant pattern syntax instead",
|
||||
format!(
|
||||
"{}({})",
|
||||
path,
|
||||
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();
|
||||
@ -1356,16 +1411,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 {
|
||||
@ -1385,6 +1431,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.
|
||||
///
|
||||
|
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` uses a bare index in a struct pattern [E0769]
|
||||
}
|
||||
let y = S(1, 2.2);
|
||||
match y {
|
||||
S { } => {} //~ ERROR: tuple variant `S` written as struct variant [E0769]
|
||||
}
|
||||
}
|
15
src/test/ui/structs/struct-tuple-field-names.stderr
Normal file
15
src/test/ui/structs/struct-tuple-field-names.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0769]: tuple variant `E::S` uses a bare index in a struct pattern
|
||||
--> $DIR/struct-tuple-field-names.rs:8:9
|
||||
|
|
||||
LL | E::S { 0, 1 } => {}
|
||||
| ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `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: `S(_, _)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0769`.
|
Loading…
Reference in New Issue
Block a user