]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_typeck/src/check/pat.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / pat.rs
index d7e69668e5eae409e558f1c9c902012e3884fb21..79c544bd3860556ff9de4e165ff1869daf9a587f 100644 (file)
@@ -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};
@@ -150,7 +151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// 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>,
@@ -679,7 +680,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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,
@@ -878,7 +879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             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(),
@@ -1150,7 +1151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         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>,
@@ -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::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();
@@ -1395,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     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) {
@@ -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::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.
     ///
@@ -1458,7 +1528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     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
@@ -1504,7 +1574,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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)