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};
///
/// Outside of this module, `check_pat_top` should always be used.
/// Conversely, inside this module, `check_pat_top` should never be used.
- #[instrument(skip(self, ti))]
+ #[instrument(level = "debug", skip(self, ti))]
fn check_pat(
&self,
pat: &'tcx Pat<'tcx>,
&self,
pat: &'tcx Pat<'tcx>,
qpath: &hir::QPath<'_>,
- fields: &'tcx [hir::FieldPat<'tcx>],
+ fields: &'tcx [hir::PatField<'tcx>],
etc: bool,
expected: Ty<'tcx>,
def_bm: BindingMode,
let sm = tcx.sess.source_map();
let path_str = sm
.span_to_snippet(sm.span_until_char(pat.span, '('))
- .map_or(String::new(), |s| format!(" `{}`", s.trim_end()));
+ .map_or_else(|_| String::new(), |s| format!(" `{}`", s.trim_end()));
let msg = format!(
"expected tuple struct or tuple variant, found {}{}",
res.descr(),
adt_ty: Ty<'tcx>,
pat: &'tcx Pat<'tcx>,
variant: &'tcx ty::VariantDef,
- fields: &'tcx [hir::FieldPat<'tcx>],
+ fields: &'tcx [hir::PatField<'tcx>],
etc: bool,
def_bm: BindingMode,
ti: TopInfo<'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::PatField<'_>],
+ ) -> 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();
fn error_tuple_variant_as_struct_pat(
&self,
pat: &Pat<'_>,
- fields: &'tcx [hir::FieldPat<'tcx>],
+ fields: &'tcx [hir::PatField<'tcx>],
variant: &ty::VariantDef,
) -> Option<DiagnosticBuilder<'tcx>> {
if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) {
);
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 {
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);
None
}
+ fn get_suggested_tuple_struct_pattern(
+ &self,
+ fields: &[hir::PatField<'_>],
+ 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.
///
fn error_no_accessible_fields(
&self,
pat: &Pat<'_>,
- fields: &'tcx [hir::FieldPat<'tcx>],
+ fields: &'tcx [hir::PatField<'tcx>],
) -> DiagnosticBuilder<'tcx> {
let mut err = self
.tcx
&self,
pat: &Pat<'_>,
unmentioned_fields: &[(&ty::FieldDef, Ident)],
- fields: &'tcx [hir::FieldPat<'tcx>],
+ fields: &'tcx [hir::PatField<'tcx>],
) -> DiagnosticBuilder<'tcx> {
let field_names = if unmentioned_fields.len() == 1 {
format!("field `{}`", unmentioned_fields[0].1)