about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-24 12:52:35 +0000
committerbors <bors@rust-lang.org>2023-08-24 12:52:35 +0000
commit18be2728bd0d59c8fc74311de365a52a2d5e3eee (patch)
tree5db356f964085a5d2901047315404a8b17a4fa44
parent8a6b67f988965f7e0ea5d4db6a56b3c195f723f4 (diff)
parent868706d9b50ddf27995a9ee4d863e163b78b7365 (diff)
downloadrust-18be2728bd0d59c8fc74311de365a52a2d5e3eee.tar.gz
rust-18be2728bd0d59c8fc74311de365a52a2d5e3eee.zip
Auto merge of #115131 - frank-king:feature/unnamed-fields-lite, r=petrochenkov
Parse unnamed fields and anonymous structs or unions (no-recovery)

It is part of #114782 which implements #49804. Only parse anonymous structs or unions in struct field definition positions.

r? `@petrochenkov`
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs3
-rw-r--r--compiler/rustc_ast/src/token.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs12
-rw-r--r--compiler/rustc_ast_passes/messages.ftl12
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs85
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs27
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs6
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs8
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs46
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/tools/rustfmt/src/types.rs2
-rw-r--r--src/tools/rustfmt/tests/target/anonymous-types.rs19
-rw-r--r--tests/ui/feature-gates/feature-gate-unnamed_fields.rs26
-rw-r--r--tests/ui/feature-gates/feature-gate-unnamed_fields.stderr84
-rw-r--r--tests/ui/parser/keyword-union-as-identifier.rs72
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs37
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr78
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs37
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr78
25 files changed, 650 insertions, 5 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 58725a08c7c..395540764ea 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2092,6 +2092,10 @@ pub enum TyKind {
     Never,
     /// A tuple (`(A, B, C, D,...)`).
     Tup(ThinVec<P<Ty>>),
+    /// An anonymous struct type i.e. `struct { foo: Type }`
+    AnonStruct(ThinVec<FieldDef>),
+    /// An anonymous union type i.e. `union { bar: Type }`
+    AnonUnion(ThinVec<FieldDef>),
     /// A path (`module::module::...::Type`), optionally
     /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
     ///
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 48e9b180b74..e3504a5638e 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -510,6 +510,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
+        TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => {
+            fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
+        }
     }
     vis.visit_span(span);
     visit_lazy_tts(tokens, vis);
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index f4ad0efa423..300b1486f9b 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -486,6 +486,8 @@ impl Token {
             Lt | BinOp(Shl)             | // associated path
             ModSep                      => true, // global path
             Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
+            // For anonymous structs or unions, which only appear in specific positions
+            // (type of struct fields or union fields), we don't consider them as regular types
             _ => false,
         }
     }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 6d474de2d15..ddbbf5a10bc 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -438,6 +438,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
         TyKind::MacCall(mac) => visitor.visit_mac_call(mac),
         TyKind::Never | TyKind::CVarArgs => {}
+        TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
+            walk_list!(visitor, visit_field_def, fields)
+        }
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 4a47de1280c..1827e42368f 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1293,6 +1293,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::Err => {
                 hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
             }
+            // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
+            #[allow(rustc::untranslatable_diagnostic)]
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            TyKind::AnonStruct(ref _fields) => hir::TyKind::Err(
+                self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
+            ),
+            // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
+            #[allow(rustc::untranslatable_diagnostic)]
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            TyKind::AnonUnion(ref _fields) => hir::TyKind::Err(
+                self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
+            ),
             TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
             TyKind::Ref(region, mt) => {
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index f323bb4c254..ee5007027de 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,3 +1,7 @@
+ast_passes_anon_struct_or_union_not_allowed =
+    anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
+    .label = anonymous {$struct_or_union} declared here
+
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
@@ -162,6 +166,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
 ast_passes_invalid_label =
     invalid label name `{$name}`
 
+ast_passes_invalid_unnamed_field =
+    unnamed fields are not allowed outside of structs or unions
+    .label = unnamed field declared here
+
+ast_passes_invalid_unnamed_field_ty =
+    unnamed fields can only have struct or union types
+    .label = not a struct or union
+
 ast_passes_item_underscore = `{$kind}` items in this context need a name
     .label = `_` is not a valid name for this `{$kind}` item
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index bd3e676daa4..ad367d05f04 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -223,10 +223,27 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
+            TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
+                walk_list!(self, visit_field_def, fields)
+            }
             _ => visit::walk_ty(self, t),
         }
     }
 
+    fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
+        if let Some(ident) = field.ident &&
+            ident.name == kw::Underscore {
+                self.check_unnamed_field_ty(&field.ty, ident.span);
+                self.visit_vis(&field.vis);
+                self.visit_ident(ident);
+                self.visit_ty_common(&field.ty);
+                self.walk_ty(&field.ty);
+                walk_list!(self, visit_attribute, &field.attrs);
+        } else {
+            self.visit_field_def(field);
+        }
+    }
+
     fn err_handler(&self) -> &rustc_errors::Handler {
         &self.session.diagnostic()
     }
@@ -264,6 +281,42 @@ impl<'a> AstValidator<'a> {
         }
     }
 
+    fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
+        if matches!(
+            &ty.kind,
+            // We already checked for `kw::Underscore` before calling this function,
+            // so skip the check
+            TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
+            // If the anonymous field contains a Path as type, we can't determine
+            // if the path is a valid struct or union, so skip the check
+            | TyKind::Path(..)
+        ) {
+            return;
+        }
+        self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
+    }
+
+    fn deny_anon_struct_or_union(&self, ty: &Ty) {
+        let struct_or_union = match &ty.kind {
+            TyKind::AnonStruct(..) => "struct",
+            TyKind::AnonUnion(..) => "union",
+            _ => return,
+        };
+        self.err_handler()
+            .emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
+    }
+
+    fn deny_unnamed_field(&self, field: &FieldDef) {
+        if let Some(ident) = field.ident &&
+            ident.name == kw::Underscore {
+                self.err_handler()
+                    .emit_err(errors::InvalidUnnamedField {
+                        span: field.span,
+                        ident_span: ident.span
+                    });
+        }
+    }
+
     fn check_trait_fn_not_const(&self, constness: Const) {
         if let Const::Yes(span) = constness {
             self.session.emit_err(errors::TraitFnConst { span });
@@ -789,6 +842,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
     fn visit_ty(&mut self, ty: &'a Ty) {
         self.visit_ty_common(ty);
+        self.deny_anon_struct_or_union(ty);
         self.walk_ty(ty)
     }
 
@@ -803,6 +857,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_field_def(&mut self, field: &'a FieldDef) {
+        self.deny_unnamed_field(field);
         visit::walk_field_def(self, field)
     }
 
@@ -995,10 +1050,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.check_mod_file_item_asciionly(item.ident);
                 }
             }
-            ItemKind::Union(vdata, ..) => {
+            ItemKind::Struct(vdata, generics) => match vdata {
+                // Duplicating the `Visitor` logic allows catching all cases
+                // of `Anonymous(Struct, Union)` outside of a field struct or union.
+                //
+                // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
+                // encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
+                // it uses `visit_ty_common`, which doesn't contain that specific check.
+                VariantData::Struct(fields, ..) => {
+                    self.visit_vis(&item.vis);
+                    self.visit_ident(item.ident);
+                    self.visit_generics(generics);
+                    walk_list!(self, visit_struct_field_def, fields);
+                    walk_list!(self, visit_attribute, &item.attrs);
+                    return;
+                }
+                _ => {}
+            },
+            ItemKind::Union(vdata, generics) => {
                 if vdata.fields().is_empty() {
                     self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
                 }
+                match vdata {
+                    VariantData::Struct(fields, ..) => {
+                        self.visit_vis(&item.vis);
+                        self.visit_ident(item.ident);
+                        self.visit_generics(generics);
+                        walk_list!(self, visit_struct_field_def, fields);
+                        walk_list!(self, visit_attribute, &item.attrs);
+                        return;
+                    }
+                    _ => {}
+                }
             }
             ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
                 self.check_defaultness(item.span, *defaultness);
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index a6f217d4780..74ab48c06ff 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -727,3 +727,30 @@ pub struct ConstraintOnNegativeBound {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_invalid_unnamed_field_ty)]
+pub struct InvalidUnnamedFieldTy {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub ty_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_invalid_unnamed_field)]
+pub struct InvalidUnnamedField {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub ident_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_anon_struct_or_union_not_allowed)]
+pub struct AnonStructOrUnionNotAllowed {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub struct_or_union: &'static str,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 10c9c3ef111..8405ae6ff8e 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
+    gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
 
     if !visitor.features.negative_bounds {
         for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 58ce73047bc..8b7e91882fc 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1064,6 +1064,14 @@ impl<'a> State<'a> {
                 }
                 self.pclose();
             }
+            ast::TyKind::AnonStruct(fields) => {
+                self.head("struct");
+                self.print_record_struct_body(&fields, ty.span);
+            }
+            ast::TyKind::AnonUnion(fields) => {
+                self.head("union");
+                self.print_record_struct_body(&fields, ty.span);
+            }
             ast::TyKind::Paren(typ) => {
                 self.popen();
                 self.print_type(typ);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index d27a44f1206..3393f034bc3 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -443,7 +443,11 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
+    pub(crate) fn print_record_struct_body(
+        &mut self,
+        fields: &[ast::FieldDef],
+        span: rustc_span::Span,
+    ) {
         self.nbsp();
         self.bopen();
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index f5bc140c049..1941390dc4a 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -584,6 +584,8 @@ declare_features! (
     (active, type_privacy_lints, "1.72.0", Some(48054), None),
     /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
     (active, unix_sigpipe, "1.65.0", Some(97889), None),
+    /// Allows unnamed fields of struct and union type
+    (incomplete, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), None),
     /// Allows unsized fn parameters.
     (active, unsized_fn_params, "1.49.0", Some(48055), None),
     /// Allows unsized rvalues at arguments and parameters.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 24c65d061f9..5db31c23478 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1594,7 +1594,7 @@ impl<'a> Parser<'a> {
         Ok((class_name, ItemKind::Union(vdata, generics)))
     }
 
-    fn parse_record_struct_body(
+    pub(crate) fn parse_record_struct_body(
         &mut self,
         adt_ty: &str,
         ident_span: Span,
@@ -1869,7 +1869,7 @@ impl<'a> Parser<'a> {
             }
         }
         self.expect_field_ty_separator()?;
-        let ty = self.parse_ty()?;
+        let ty = self.parse_ty_for_field_def()?;
         if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) {
             self.sess.emit_err(errors::SingleColonStructType { span: self.token.span });
         }
@@ -1894,7 +1894,9 @@ impl<'a> Parser<'a> {
     /// for better diagnostics and suggestions.
     fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
         let (ident, is_raw) = self.ident_or_err(true)?;
-        if !is_raw && ident.is_reserved() {
+        if ident.name == kw::Underscore {
+            self.sess.gated_spans.gate(sym::unnamed_fields, lo);
+        } else if !is_raw && ident.is_reserved() {
             let snapshot = self.create_snapshot_for_diagnostic();
             let err = if self.check_fn_front_matter(false, Case::Sensitive) {
                 let inherited_vis = Visibility {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 2d888efb1f3..661113666cd 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -136,6 +136,17 @@ impl<'a> Parser<'a> {
         )
     }
 
+    /// Parse a type suitable for a field defintion.
+    /// The difference from `parse_ty` is that this version
+    /// allows anonymous structs and unions.
+    pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P<Ty>> {
+        if self.can_begin_anon_struct_or_union() {
+            self.parse_anon_struct_or_union()
+        } else {
+            self.parse_ty()
+        }
+    }
+
     /// Parse a type suitable for a function or function pointer parameter.
     /// The difference from `parse_ty` is that this version allows `...`
     /// (`CVarArgs`) at the top level of the type.
@@ -336,6 +347,36 @@ impl<'a> Parser<'a> {
         if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
     }
 
+    /// Parse an anonymous struct or union (only for field definitions):
+    /// ```ignore (feature-not-ready)
+    /// #[repr(C)]
+    /// struct Foo {
+    ///     _: struct { // anonymous struct
+    ///         x: u32,
+    ///         y: f64,
+    ///     }
+    ///     _: union { // anonymous union
+    ///         z: u32,
+    ///         w: f64,
+    ///     }
+    /// }
+    /// ```
+    fn parse_anon_struct_or_union(&mut self) -> PResult<'a, P<Ty>> {
+        assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct));
+        let is_union = self.token.is_keyword(kw::Union);
+
+        let lo = self.token.span;
+        self.bump();
+
+        let (fields, _recovered) =
+            self.parse_record_struct_body(if is_union { "union" } else { "struct" }, lo, false)?;
+        let span = lo.to(self.prev_token.span);
+        self.sess.gated_spans.gate(sym::unnamed_fields, span);
+        // These can be rejected during AST validation in `deny_anon_struct_or_union`.
+        let kind = if is_union { TyKind::AnonUnion(fields) } else { TyKind::AnonStruct(fields) };
+        Ok(self.mk_ty(span, kind))
+    }
+
     /// Parses either:
     /// - `(TYPE)`, a parenthesized type.
     /// - `(TYPE,)`, a tuple with a single field of type TYPE.
@@ -696,6 +737,11 @@ impl<'a> Parser<'a> {
         Ok(bounds)
     }
 
+    pub(super) fn can_begin_anon_struct_or_union(&mut self) -> bool {
+        (self.token.is_keyword(kw::Struct) || self.token.is_keyword(kw::Union))
+            && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
+    }
+
     /// Can the current token begin a bound?
     fn can_begin_bound(&mut self) -> bool {
         // This needs to be synchronized with `TokenKind::can_begin_bound`.
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 5aa8aef6a85..24087a4eabb 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -587,6 +587,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
                 BareFn,
                 Never,
                 Tup,
+                AnonStruct,
+                AnonUnion,
                 Path,
                 TraitObject,
                 ImplTrait,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8aec12f128e..c0eff6dfd8f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1618,6 +1618,7 @@ symbols! {
         unix_sigpipe,
         unlikely,
         unmarked_api,
+        unnamed_fields,
         unpin,
         unreachable,
         unreachable_2015,
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 18a08f17ba0..5e8edd8f8bf 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -819,6 +819,8 @@ impl Rewrite for ast::Ty {
             ast::TyKind::Tup(ref items) => {
                 rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
             }
+            ast::TyKind::AnonStruct(_) => Some(context.snippet(self.span).to_owned()),
+            ast::TyKind::AnonUnion(_) => Some(context.snippet(self.span).to_owned()),
             ast::TyKind::Path(ref q_self, ref path) => {
                 rewrite_path(context, PathContext::Type, q_self, path, shape)
             }
diff --git a/src/tools/rustfmt/tests/target/anonymous-types.rs b/src/tools/rustfmt/tests/target/anonymous-types.rs
new file mode 100644
index 00000000000..8e08c314ed1
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/anonymous-types.rs
@@ -0,0 +1,19 @@
+// Test for issue 85480
+// Pretty print anonymous struct and union types
+
+// pp-exact
+// pretty-compare-only
+
+struct Foo {
+    _: union  {
+        _: struct  {
+            a: u8,
+            b: u16,
+        },
+        c: u32,
+    },
+    d: u64,
+    e: f32,
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.rs b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
new file mode 100644
index 00000000000..4bbd0c83bfb
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
@@ -0,0 +1,26 @@
+struct Foo {
+    foo: u8,
+    _: union { //~ ERROR unnamed fields are not yet fully implemented [E0658]
+    //~^ ERROR unnamed fields are not yet fully implemented [E0658]
+    //~| ERROR anonymous unions are unimplemented
+        bar: u8,
+        baz: u16
+    }
+}
+
+union Bar {
+    foobar: u8,
+    _: struct { //~ ERROR unnamed fields are not yet fully implemented [E0658]
+    //~^ ERROR unnamed fields are not yet fully implemented [E0658]
+    //~| ERROR anonymous structs are unimplemented
+        foobaz: u8,
+        barbaz: u16
+    }
+}
+
+struct S;
+struct Baz {
+    _: S //~ ERROR unnamed fields are not yet fully implemented [E0658]
+}
+
+fn main(){}
diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
new file mode 100644
index 00000000000..f026f2c3600
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
@@ -0,0 +1,84 @@
+error[E0658]: unnamed fields are not yet fully implemented
+  --> $DIR/feature-gate-unnamed_fields.rs:3:5
+   |
+LL |     _: union {
+   |     ^
+   |
+   = note: see issue #49804 <https://github.com/rust-lang/rust/issues/49804> for more information
+   = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+  --> $DIR/feature-gate-unnamed_fields.rs:3:8
+   |
+LL |       _: union {
+   |  ________^
+LL | |
+LL | |
+LL | |         bar: u8,
+LL | |         baz: u16
+LL | |     }
+   | |_____^
+   |
+   = note: see issue #49804 <https://github.com/rust-lang/rust/issues/49804> for more information
+   = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+  --> $DIR/feature-gate-unnamed_fields.rs:13:5
+   |
+LL |     _: struct {
+   |     ^
+   |
+   = note: see issue #49804 <https://github.com/rust-lang/rust/issues/49804> for more information
+   = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+  --> $DIR/feature-gate-unnamed_fields.rs:13:8
+   |
+LL |       _: struct {
+   |  ________^
+LL | |
+LL | |
+LL | |         foobaz: u8,
+LL | |         barbaz: u16
+LL | |     }
+   | |_____^
+   |
+   = note: see issue #49804 <https://github.com/rust-lang/rust/issues/49804> for more information
+   = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+  --> $DIR/feature-gate-unnamed_fields.rs:23:5
+   |
+LL |     _: S
+   |     ^
+   |
+   = note: see issue #49804 <https://github.com/rust-lang/rust/issues/49804> for more information
+   = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error: anonymous unions are unimplemented
+  --> $DIR/feature-gate-unnamed_fields.rs:3:8
+   |
+LL |       _: union {
+   |  ________^
+LL | |
+LL | |
+LL | |         bar: u8,
+LL | |         baz: u16
+LL | |     }
+   | |_____^
+
+error: anonymous structs are unimplemented
+  --> $DIR/feature-gate-unnamed_fields.rs:13:8
+   |
+LL |       _: struct {
+   |  ________^
+LL | |
+LL | |
+LL | |         foobaz: u8,
+LL | |         barbaz: u16
+LL | |     }
+   | |_____^
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs
new file mode 100644
index 00000000000..7062557d731
--- /dev/null
+++ b/tests/ui/parser/keyword-union-as-identifier.rs
@@ -0,0 +1,72 @@
+// check-pass
+
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+
+mod union {
+    type union = i32;
+
+    pub struct Bar {
+        pub union: union,
+    }
+
+    pub fn union() -> Bar {
+        Bar {
+            union: 5
+        }
+    }
+}
+
+mod struct_union {
+    pub struct union {
+        pub union: u32
+    }
+    static union: union = union { union: 0 };
+
+    impl union {
+        pub fn union<'union>() -> &'union union {
+            &union
+        }
+    }
+    impl union {}
+    trait Foo {}
+    impl Foo for union {}
+    trait Bar {
+        fn bar() {}
+    }
+    impl Bar for union {}
+}
+
+mod union_union {
+    pub union union {
+        pub union: u32
+    }
+    const union: union = union { union: 0 };
+    impl union {
+        pub fn union() -> union {
+            union
+        }
+    }
+}
+
+mod trait_union {
+    pub trait union {
+        fn union() {}
+    }
+    impl union for () {}
+}
+
+macro_rules! ty {
+    ($ty:ty { $($field:ident:$field_ty:ty)* }) => {};
+}
+
+fn main() {
+    let union = union::union();
+    let _ = union.union;
+    let _ = struct_union::union::union().union;
+    let union = union_union::union::union();
+    let _ = unsafe { union.union };
+    <() as trait_union::union>::union();
+    ty!(union {});
+    ty!(union { union: union });
+}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
new file mode 100644
index 00000000000..192bbba5a5b
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
@@ -0,0 +1,37 @@
+#![allow(incomplete_features)]
+#![feature(unnamed_fields)]
+
+struct F {
+    field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+    //~^ ERROR anonymous structs are unimplemented
+    _: struct { field: u8 },
+    //~^ ERROR anonymous structs are unimplemented
+}
+
+struct G {
+    _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+union H {
+    field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+    //~^ ERROR anonymous structs are unimplemented
+    _: struct { field: u8 },
+    //~^ ERROR anonymous structs are unimplemented
+}
+
+union I {
+    _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+enum K {
+    M {
+        _ : struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+        //~^ ERROR unnamed fields are not allowed outside of structs or unions
+        //~| ERROR anonymous structs are unimplemented
+    },
+    N {
+        _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
new file mode 100644
index 00000000000..fd731766c01
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
@@ -0,0 +1,78 @@
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_structs.rs:5:12
+   |
+LL |     field: struct { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields can only have struct or union types
+  --> $DIR/restrict_anonymous_structs.rs:12:5
+   |
+LL |     _: (u8, u8),
+   |     ^  -------- not a struct or union
+
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_structs.rs:16:12
+   |
+LL |     field: struct { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields can only have struct or union types
+  --> $DIR/restrict_anonymous_structs.rs:23:5
+   |
+LL |     _: (u8, u8),
+   |     ^  -------- not a struct or union
+
+error: unnamed fields are not allowed outside of structs or unions
+  --> $DIR/restrict_anonymous_structs.rs:28:9
+   |
+LL |         _ : struct { field: u8 },
+   |         -^^^^^^^^^^^^^^^^^^^^^^^
+   |         |
+   |         unnamed field declared here
+
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_structs.rs:28:13
+   |
+LL |         _ : struct { field: u8 },
+   |             ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields are not allowed outside of structs or unions
+  --> $DIR/restrict_anonymous_structs.rs:33:9
+   |
+LL |         _ : u8,
+   |         -^^^^^
+   |         |
+   |         unnamed field declared here
+
+error: anonymous structs are unimplemented
+  --> $DIR/restrict_anonymous_structs.rs:5:12
+   |
+LL |     field: struct { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+  --> $DIR/restrict_anonymous_structs.rs:7:8
+   |
+LL |     _: struct { field: u8 },
+   |        ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+  --> $DIR/restrict_anonymous_structs.rs:16:12
+   |
+LL |     field: struct { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+  --> $DIR/restrict_anonymous_structs.rs:18:8
+   |
+LL |     _: struct { field: u8 },
+   |        ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+  --> $DIR/restrict_anonymous_structs.rs:28:13
+   |
+LL |         _ : struct { field: u8 },
+   |             ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
new file mode 100644
index 00000000000..c69266089bb
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
@@ -0,0 +1,37 @@
+#![allow(incomplete_features)]
+#![feature(unnamed_fields)]
+
+struct F {
+    field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+    //~^ ERROR anonymous unions are unimplemented
+    _: union { field: u8 },
+    //~^ ERROR anonymous unions are unimplemented
+}
+
+struct G {
+    _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+union H {
+    field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+    //~^ ERROR anonymous unions are unimplemented
+    _: union { field: u8 },
+    //~^ ERROR anonymous unions are unimplemented
+}
+
+union I {
+    _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+enum K {
+    M {
+        _ : union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+        //~^ ERROR unnamed fields are not allowed outside of structs or unions
+        //~| ERROR anonymous unions are unimplemented
+    },
+    N {
+        _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
new file mode 100644
index 00000000000..c65cad775a9
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
@@ -0,0 +1,78 @@
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_unions.rs:5:12
+   |
+LL |     field: union { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields can only have struct or union types
+  --> $DIR/restrict_anonymous_unions.rs:12:5
+   |
+LL |     _: (u8, u8),
+   |     ^  -------- not a struct or union
+
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_unions.rs:16:12
+   |
+LL |     field: union { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields can only have struct or union types
+  --> $DIR/restrict_anonymous_unions.rs:23:5
+   |
+LL |     _: (u8, u8),
+   |     ^  -------- not a struct or union
+
+error: unnamed fields are not allowed outside of structs or unions
+  --> $DIR/restrict_anonymous_unions.rs:28:9
+   |
+LL |         _ : union { field: u8 },
+   |         -^^^^^^^^^^^^^^^^^^^^^^
+   |         |
+   |         unnamed field declared here
+
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+  --> $DIR/restrict_anonymous_unions.rs:28:13
+   |
+LL |         _ : union { field: u8 },
+   |             ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields are not allowed outside of structs or unions
+  --> $DIR/restrict_anonymous_unions.rs:33:9
+   |
+LL |         _ : u8,
+   |         -^^^^^
+   |         |
+   |         unnamed field declared here
+
+error: anonymous unions are unimplemented
+  --> $DIR/restrict_anonymous_unions.rs:5:12
+   |
+LL |     field: union { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+  --> $DIR/restrict_anonymous_unions.rs:7:8
+   |
+LL |     _: union { field: u8 },
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+  --> $DIR/restrict_anonymous_unions.rs:16:12
+   |
+LL |     field: union { field: u8 },
+   |            ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+  --> $DIR/restrict_anonymous_unions.rs:18:8
+   |
+LL |     _: union { field: u8 },
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+  --> $DIR/restrict_anonymous_unions.rs:28:13
+   |
+LL |         _ : union { field: u8 },
+   |             ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+