From 353d71ff423e2e7b7c286a780f03e9a39aa9ccb1 Mon Sep 17 00:00:00 2001 From: Frank King Date: Tue, 14 Nov 2023 20:19:20 +0800 Subject: [PATCH] Parse unnamed fields and anonymous structs or unions Anonymous structs or unions are parsed as a special type of field, instead of a gerenal kind of `Ty`. --- compiler/rustc_ast/src/ast.rs | 152 +++++++++++++++++- compiler/rustc_ast/src/mut_visit.rs | 21 ++- compiler/rustc_ast/src/visit.rs | 12 +- compiler/rustc_ast_lowering/src/item.rs | 36 +++-- compiler/rustc_ast_lowering/src/lib.rs | 13 +- .../rustc_ast_passes/src/ast_validation.rs | 56 +++---- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 - .../rustc_ast_pretty/src/pprust/state/item.rs | 12 +- .../src/deriving/clone.rs | 4 +- .../src/deriving/cmp/eq.rs | 4 +- .../src/deriving/generic/mod.rs | 10 +- compiler/rustc_expand/src/placeholders.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 56 ++++++- compiler/rustc_parse/src/parser/ty.rs | 46 ------ compiler/rustc_passes/src/hir_stats.rs | 2 - .../rustc_resolve/src/build_reduced_graph.rs | 9 +- .../clippy_lints/src/manual_non_exhaustive.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 2 +- src/tools/rustfmt/src/items.rs | 84 +++++++++- src/tools/rustfmt/src/spanned.rs | 2 +- src/tools/rustfmt/src/types.rs | 2 - tests/ui/stats/hir-stats.stderr | 20 +-- 22 files changed, 392 insertions(+), 163 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index c85ff6f5c4451..15162e54bb041 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2060,10 +2060,6 @@ pub enum TyKind { Never, /// A tuple (`(A, B, C, D,...)`). Tup(ThinVec>), - /// An anonymous struct type i.e. `struct { foo: Type }` - AnonStruct(ThinVec), - /// An anonymous union type i.e. `union { bar: Type }` - AnonUnion(ThinVec), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., ` as SomeTrait>::SomeType`. /// @@ -2689,6 +2685,93 @@ impl VisibilityKind { } } +#[derive(Clone, Copy, Encodable, Decodable, Debug)] +pub enum AnonRecordKind { + Struct, + Union, +} + +impl AnonRecordKind { + /// Returns the lowercase name. + pub fn name(self) -> &'static str { + match self { + Self::Struct => "struct", + Self::Union => "union", + } + } +} + +/// An anonymous struct or union, i.e. `struct { foo: Foo }` or `union { foo: Foo }`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct AnonRecord { + pub id: NodeId, + pub span: Span, + pub fields: ThinVec, + pub kind: AnonRecordKind, + pub recovered: bool, +} + +impl AnonRecord { + pub fn is_union(&self) -> bool { + matches!(self.kind, AnonRecordKind::Union) + } + pub fn is_struct(&self) -> bool { + matches!(self.kind, AnonRecordKind::Struct) + } +} + +/// Type of fields in a struct, variant or union. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum FieldTy { + Ty(P), + /// An anonymous struct or union, i.e. `struct { foo: Foo }` or `union { foo: Foo }`. + // AnonRecord(P), + AnonRecord(P), +} + +impl From> for FieldTy { + fn from(ty: P) -> Self { + Self::Ty(ty) + } +} + +impl From for FieldTy { + fn from(anon_record: AnonRecord) -> Self { + Self::AnonRecord(P(anon_record)) + } +} + +impl FieldTy { + pub fn id(&self) -> NodeId { + match self { + Self::Ty(ty) => ty.id, + Self::AnonRecord(anon_record) => anon_record.id, + } + } + + pub fn span(&self) -> Span { + match self { + Self::Ty(ty) => ty.span, + Self::AnonRecord(anon_record) => anon_record.span, + } + } + + pub fn as_ty(&self) -> Option<&P> { + match self { + Self::Ty(ty) => Some(ty), + _ => None, + } + } + + /// Expects a `Ty`, otherwise panics. + pub fn expect_ty(&self) -> &P { + let FieldTy::Ty(ty) = &self else { + panic!("expect a type, found {self:?}"); + }; + ty + } +} + /// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. @@ -2700,7 +2783,7 @@ pub struct FieldDef { pub vis: Visibility, pub ident: Option, - pub ty: P, + pub ty: FieldTy, pub is_placeholder: bool, } @@ -2730,6 +2813,27 @@ impl VariantData { } } + /// Return all fields of this variant, with anonymous structs or unions flattened. + pub fn all_fields(&self) -> AllFields<'_> { + AllFields { iters: smallvec::smallvec![self.fields().iter()] } + } + + /// Return whether this variant contains inner anonymous unions. + pub fn find_inner_union(&self) -> Option { + // We only check the record-like structs + let VariantData::Struct(fields, ..) = self else { return None }; + fn find_union(fields: &[FieldDef]) -> Option { + fields.iter().find_map(|field| { + let FieldTy::AnonRecord(anon_record) = &field.ty else { return None }; + anon_record + .is_union() + .then(|| anon_record.span) + .or_else(|| find_union(&anon_record.fields)) + }) + } + find_union(&fields) + } + /// Return the `NodeId` of this variant's constructor, if it has one. pub fn ctor_node_id(&self) -> Option { match *self { @@ -2739,6 +2843,44 @@ impl VariantData { } } +/// Iterator of all fields of a `VariantData`. +/// +/// It iterates on the field tree in preorder, where the unnamed fields with anonymous structs or unions +/// are flattened to their inner fields. +pub struct AllFields<'a> { + iters: smallvec::SmallVec<[std::slice::Iter<'a, FieldDef>; 1]>, +} + +impl<'a> Iterator for AllFields<'a> { + type Item = &'a FieldDef; + + fn next(&mut self) -> Option { + let mut top = self.iters.last_mut()?; + loop { + if let Some(field) = top.next() { + if let FieldTy::AnonRecord(anon_record) = &field.ty { + self.iters.push(anon_record.fields.iter()); + top = self.iters.last_mut().unwrap(); + continue; + } + return Some(field); + } + self.iters.pop(); + top = self.iters.last_mut()?; + } + } + + fn size_hint(&self) -> (usize, Option) { + match &self.iters[..] { + [] => (0, Some(0)), + [single] => (single.size_hint().0, None), + [first, .., last] => (first.size_hint().0 + last.size_hint().0, None), + } + } +} + +impl std::iter::FusedIterator for AllFields<'_> {} + /// An item definition. #[derive(Clone, Encodable, Decodable, Debug)] pub struct Item { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7c0a78253a218..eef62d1d84397 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -251,6 +251,10 @@ pub trait MutVisitor: Sized { noop_visit_variant_data(vdata, self); } + fn visit_anon_record(&mut self, anon_record: &mut AnonRecord) { + noop_visit_anon_record(anon_record, self); + } + fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> { noop_flat_map_generic_param(param, self) } @@ -514,9 +518,6 @@ pub fn noop_visit_ty(ty: &mut P, 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); @@ -982,6 +983,13 @@ pub fn noop_visit_variant_data(vdata: &mut VariantData, vis: &mut } } +pub fn noop_visit_anon_record(anon_record: &mut AnonRecord, vis: &mut T) { + let AnonRecord { span, id, fields, recovered: _recovered, kind: _kind } = anon_record; + vis.visit_span(span); + vis.visit_id(id); + fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); +} + pub fn noop_visit_trait_ref(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) { vis.visit_path(path); vis.visit_id(ref_id); @@ -1003,7 +1011,12 @@ pub fn noop_flat_map_field_def( visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_vis(vis); visitor.visit_id(id); - visitor.visit_ty(ty); + match ty { + FieldTy::Ty(ty) => { + visitor.visit_ty(ty); + } + FieldTy::AnonRecord(anon_record) => visitor.visit_anon_record(anon_record), + } visit_attrs(attrs, visitor); smallvec![fd] } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1caa39e2dd999..4201112d91305 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -441,9 +441,6 @@ 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) - } } } @@ -716,7 +713,14 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) if let Some(ident) = field.ident { visitor.visit_ident(ident); } - visitor.visit_ty(&field.ty); + match &field.ty { + FieldTy::Ty(ty) => { + visitor.visit_ty(ty); + } + FieldTy::AnonRecord(anon_record) => { + walk_list!(visitor, visit_field_def, &anon_record.fields); + } + } walk_list!(visitor, visit_attribute, &field.attrs); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 9a70e6d7c4a1e..5ce5b8a7f1b97 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -708,17 +708,31 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { - let ty = if let TyKind::Path(qself, path) = &f.ty.kind { - let t = self.lower_path_ty( - &f.ty, - qself, - path, - ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) - &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy), - ); - self.arena.alloc(t) - } else { - self.lower_ty(&f.ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)) + let ty = match &f.ty { + FieldTy::Ty(ty) if let TyKind::Path(qself, path) = &ty.kind => { + let t = self.lower_path_ty( + ty, + qself, + path, + ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) + &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy), + ); + self.arena.alloc(t) + } + FieldTy::Ty(ty) => { + self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)) + } + FieldTy::AnonRecord(anon_record ) => { + let struct_or_union = anon_record.kind.name(); + let hir_id = self.lower_node_id(anon_record.id); + let span = anon_record.span; + // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + let kind = hir::TyKind::Err(self.tcx.sess.span_err(span, format!("anonymous {struct_or_union}s are unimplemented"))); + let ty = hir::Ty { hir_id, kind, span }; + self.arena.alloc(ty) + } }; let hir_id = self.lower_node_id(f.id); self.lower_attrs(hir_id, &f.attrs); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a88493acf982f..f4c40e0c7efde 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,6 +35,7 @@ #![cfg_attr(not(bootstrap), doc(rust_logo))] #![feature(box_patterns)] #![feature(let_chains)] +#![feature(if_let_guard)] #![feature(never_type)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] @@ -1295,18 +1296,6 @@ 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/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 3d0513c89230d..773201eca9117 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -207,23 +207,22 @@ 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 + 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); + let ty = self.check_unnamed_field_ty(&field.ty, ident.span); + self.visit_vis(&field.vis); + self.visit_ident(ident); + if let Some(ty) = ty { + self.visit_ty_common(&ty); + self.walk_ty(&ty); + } + walk_list!(self, visit_attribute, &field.attrs); } else { self.visit_field_def(field); } @@ -266,29 +265,24 @@ 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; + fn check_unnamed_field_ty(&self, f: &'a FieldTy, span: Span) -> Option<&'a Ty> { + // We already checked for `kw::Underscore` before calling this function, + // so skip the check + let ty = &f.as_ty()?; + // 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 + if matches!(&ty.kind, | TyKind::Path(..)) { + return Some(ty); } self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span }); + Some(ty) } - 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_anon_struct_or_union(&self, f: &'a FieldTy) { + let FieldTy::AnonRecord(anon_record) = f else { return }; + let struct_or_union = anon_record.kind.name(); + let span = anon_record.span; + self.err_handler().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span }); } fn deny_unnamed_field(&self, field: &FieldDef) { @@ -787,7 +781,6 @@ 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 +796,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_field_def(&mut self, field: &'a FieldDef) { self.deny_unnamed_field(field); + self.deny_anon_struct_or_union(&field.ty); visit::walk_field_def(self, field) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 48421ff71409c..edc7d2be3aab6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1076,14 +1076,6 @@ 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 3393f034bc3b5..35bc59455e13c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -462,7 +462,15 @@ impl<'a> State<'a> { self.print_visibility(&field.vis); self.print_ident(field.ident.unwrap()); self.word_nbsp(":"); - self.print_type(&field.ty); + match &field.ty { + ast::FieldTy::Ty(ty) => { + self.print_type(&ty); + } + ast::FieldTy::AnonRecord(anon_record) => { + self.head(anon_record.kind.name()); + self.print_record_struct_body(&anon_record.fields, anon_record.span); + } + } self.word(","); } } @@ -488,7 +496,7 @@ impl<'a> State<'a> { s.maybe_print_comment(field.span.lo()); s.print_outer_attributes(&field.attrs); s.print_visibility(&field.vis); - s.print_type(&field.ty) + s.print_type(field.ty.expect_ty()) }); self.pclose(); } diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 1649cc76c8d52..94cc70bac6806 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -106,7 +106,7 @@ fn cs_clone_simple( // This basic redundancy checking only prevents duplication of // assertions like `AssertParamIsClone` where the type is a // simple name. That's enough to get a lot of cases, though. - if let Some(name) = field.ty.kind.is_simple_path() + if let Some(name) = field.ty.expect_ty().kind.is_simple_path() && !seen_type_names.insert(name) { // Already produced an assertion for this type. @@ -115,7 +115,7 @@ fn cs_clone_simple( super::assert_ty_bounds( cx, &mut stmts, - field.ty.clone(), + field.ty.expect_ty().clone(), field.span, &[sym::clone, sym::AssertParamIsClone], ); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 8a6d219379fca..7e5efd1dc54be 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -73,7 +73,7 @@ fn cs_total_eq_assert( // This basic redundancy checking only prevents duplication of // assertions like `AssertParamIsEq` where the type is a // simple name. That's enough to get a lot of cases, though. - if let Some(name) = field.ty.kind.is_simple_path() + if let Some(name) = field.ty.expect_ty().kind.is_simple_path() && !seen_type_names.insert(name) { // Already produced an assertion for this type. @@ -82,7 +82,7 @@ fn cs_total_eq_assert( super::assert_ty_bounds( cx, &mut stmts, - field.ty.clone(), + field.ty.expect_ty().clone(), field.span, &[sym::cmp, sym::AssertParamIsEq], ); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index aa1ce1b929d0f..71991409cd03d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -808,7 +808,7 @@ impl<'a> TraitDef<'a> { is_packed: bool, ) -> P { let field_tys: Vec> = - struct_def.fields().iter().map(|field| field.ty.clone()).collect(); + struct_def.fields().iter().map(|field| field.ty.expect_ty().clone()).collect(); let methods = self .methods @@ -863,7 +863,8 @@ impl<'a> TraitDef<'a> { let mut field_tys = Vec::new(); for variant in &enum_def.variants { - field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone())); + field_tys + .extend(variant.data.fields().iter().map(|field| field.ty.expect_ty().clone())); } let methods = self @@ -1608,11 +1609,12 @@ impl<'a> TraitDef<'a> { } }; - let exception = if let TyKind::Slice(ty) = &struct_field.ty.kind && + let struct_field_ty = struct_field.ty.expect_ty(); + let exception = if let TyKind::Slice(ty) = &struct_field_ty.kind && is_simple_path(ty, sym::u8) { Some("byte") - } else if is_simple_path(&struct_field.ty, sym::str) { + } else if is_simple_path(struct_field_ty, sym::str) { Some("string") } else { None diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 1292a8552303e..8842ae7044eec 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -168,7 +168,7 @@ pub fn placeholder( id, ident: None, span, - ty: ty(), + ty: ty().into(), vis, is_placeholder: true, }]), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 801860c212367..190d05a05be56 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1691,7 +1691,7 @@ impl<'a> Parser<'a> { vis, ident: None, id: DUMMY_NODE_ID, - ty, + ty: ty.into(), attrs, is_placeholder: false, }, @@ -1776,7 +1776,7 @@ impl<'a> Parser<'a> { // Try to recover extra trailing angle brackets let mut recovered = false; - if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { + if let FieldTy::Ty(ty) = &a_var.ty && let TyKind::Path(_, Path { segments, .. }) = &ty.kind { if let Some(last_segment) = segments.last() { recovered = self.check_trailing_angle_brackets( last_segment, @@ -1854,6 +1854,42 @@ impl<'a> Parser<'a> { Ok(()) } + 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)) + } + + /// 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, AnonRecord> { + let kind = match &self.token { + token if token.is_keyword(kw::Union) => AnonRecordKind::Union, + token if token.is_keyword(kw::Struct) => AnonRecordKind::Struct, + token => unreachable!("Expect `struct` or `union`, found {token:?}"), + }; + + let lo = self.token.span; + self.bump(); + + let (fields, recovered) = self.parse_record_struct_body(kind.name(), lo, false)?; + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + // This can be rejected during AST validation in `deny_anon_adt`. + Ok(AnonRecord { id: DUMMY_NODE_ID, span, kind, fields, recovered }) + } + /// Parses a structure field. fn parse_name_and_ty( &mut self, @@ -1871,7 +1907,19 @@ impl<'a> Parser<'a> { } } self.expect_field_ty_separator()?; - let ty = self.parse_ty_for_field_def()?; + if self.can_begin_anon_struct_or_union() { + let anon_record = self.parse_anon_struct_or_union()?; + return Ok(FieldDef { + span: lo.to(self.prev_token.span), + ident: Some(name), + vis, + id: DUMMY_NODE_ID, + ty: anon_record.into(), + attrs, + is_placeholder: false, + }); + } + let ty = self.parse_ty()?; 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 }); } @@ -1886,7 +1934,7 @@ impl<'a> Parser<'a> { ident: Some(name), vis, id: DUMMY_NODE_ID, - ty, + ty: ty.into(), attrs, is_placeholder: false, }) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index be2cbaf3020e6..f369a6007ac36 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -135,17 +135,6 @@ 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> { - 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. @@ -346,36 +335,6 @@ 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> { - 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. @@ -736,11 +695,6 @@ 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 f915c1057d6c4..a9e9558370662 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -587,8 +587,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { BareFn, Never, Tup, - AnonStruct, - AnonUnion, Path, TraitObject, ImplTrait, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 0407db528af68..792ae794cb044 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -320,15 +320,16 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { // The fields are not expanded yet. return; } - let def_ids = vdata.fields().iter().map(|field| self.r.local_def_id(field.id).to_def_id()); + let def_ids = vdata.all_fields().map(|field| self.r.local_def_id(field.id).to_def_id()); self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids)); } fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) { let field_vis = vdata - .fields() - .iter() - .map(|field| field.vis.span.until(field.ident.map_or(field.ty.span, |i| i.span))) + .all_fields() + .map(|field| { + field.vis.span.until(field.ident.map_or(field.ty.expect_ty().span, |i| i.span)) + }) .collect(); self.r.field_visibility_spans.insert(def_id, field_vis); } diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index fc8f23630013a..c8aa5891ac549 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -117,7 +117,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { }); if let Some(Ok(field)) = iter.next() && iter.next().is_none() - && field.ty.kind.is_unit() + && field.ty.expect_ty().kind.is_unit() && field.ident.map_or(true, |name| name.as_str().starts_with('_')) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index a2c61e07b70ad..5de5a9670a0df 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -556,7 +556,7 @@ pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r)) - && eq_ty(&l.ty, &r.ty) + && eq_ty(l.ty.expect_ty(), r.ty.expect_ty()) } pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index edb5a5b629a8f..f92b3204736cd 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1086,6 +1086,49 @@ impl<'a> StructParts<'a> { } } +pub(crate) struct StructStructParts<'a> { + prefix: &'a str, + ident: symbol::Ident, + vis: Option<&'a ast::Visibility>, + generics: Option<&'a ast::Generics>, + span: Span, +} + +impl<'a> StructStructParts<'a> { + fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String { + let vis = &ast::Visibility { + kind: ast::VisibilityKind::Inherited, + span: self.span.shrink_to_lo(), + tokens: None, + }; + format_header(context, self.prefix, self.ident, self.vis.unwrap_or(vis), offset) + } + fn from_struct(struct_parts: &'a StructParts<'a>) -> Self { + StructStructParts { + prefix: struct_parts.prefix, + ident: struct_parts.ident, + vis: Some(struct_parts.vis), + generics: struct_parts.generics, + span: struct_parts.span, + } + } + + fn from_anon_record(anon_record: &'a ast::AnonRecord) -> Self { + let prefix = match anon_record.kind { + ast::AnonRecordKind::Union => "union ", + ast::AnonRecordKind::Struct => "struct ", + }; + StructStructParts { + prefix, + ident: symbol::Ident::empty(), + vis: None, + generics: None, + span: anon_record.span, + } + } +} + + fn enum_variant_span(variant: &ast::Variant, context: &RewriteContext<'_>) -> Span { use ast::VariantData::*; if let Some(ref anon_const) = variant.disr_expr { @@ -1116,9 +1159,14 @@ fn format_struct( ast::VariantData::Tuple(ref fields, _) => { format_tuple_struct(context, struct_parts, fields, offset) } - ast::VariantData::Struct(ref fields, _) => { - format_struct_struct(context, struct_parts, fields, offset, one_line_width) - } + ast::VariantData::Struct(ref fields, _) => format_struct_struct( + context, + &StructStructParts::from_struct(struct_parts), + fields, + offset, + one_line_width, + false, + ), } } @@ -1390,15 +1438,17 @@ fn format_unit_struct( pub(crate) fn format_struct_struct( context: &RewriteContext<'_>, - struct_parts: &StructParts<'_>, + struct_parts: &StructStructParts<'_>, fields: &[ast::FieldDef], offset: Indent, one_line_width: Option, + is_anon: bool, ) -> Option { let mut result = String::with_capacity(1024); let span = struct_parts.span; - let header_str = struct_parts.format_header(context, offset); + let header_offset = if is_anon { Indent::empty() } else { offset }; + let header_str = struct_parts.format_header(context, header_offset); result.push_str(&header_str); let header_hi = struct_parts.ident.span.hi(); @@ -1907,7 +1957,7 @@ pub(crate) fn rewrite_struct_field( let is_prefix_empty = prefix.is_empty(); // We must use multiline. We are going to put attributes and a field on different lines. - let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; + let field_str = rewrite_assign_rhs(context, prefix, &field.ty, &RhsAssignKind::Ty, shape)?; // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. let field_str = if is_prefix_empty { field_str.trim_start() @@ -1917,6 +1967,28 @@ pub(crate) fn rewrite_struct_field( combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false) } +impl Rewrite for ast::FieldTy { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + match self { + ast::FieldTy::Ty(ty) => ty.rewrite(context, shape), + ast::FieldTy::AnonRecord(anon_record) => anon_record.rewrite(context, shape), + } + } +} + +impl Rewrite for ast::AnonRecord { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { + &StructStructParts::from_anon_record(self), + format_struct_struct( + context, + &self.fields, + shape.indent, + None, + true, + ) + } +} + pub(crate) struct StaticParts<'a> { prefix: &'a str, vis: &'a ast::Visibility, diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 2136cfeae1af1..f548f9dc297ba 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -138,7 +138,7 @@ impl Spanned for ast::GenericParam { impl Spanned for ast::FieldDef { fn span(&self) -> Span { - span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi()) + span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span().hi()) } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 127aff913e37e..f2e229d74772d 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -816,8 +816,6 @@ 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/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index 813e65e45a255..117e26f9dc282 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -15,21 +15,21 @@ ast-stats-1 Arm 96 ( 1.5%) 2 48 ast-stats-1 ForeignItem 96 ( 1.5%) 1 96 ast-stats-1 - Fn 96 ( 1.5%) 1 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 -ast-stats-1 FieldDef 160 ( 2.5%) 2 80 ast-stats-1 Stmt 160 ( 2.5%) 5 32 ast-stats-1 - Local 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - Expr 96 ( 1.5%) 3 ast-stats-1 Param 160 ( 2.5%) 4 40 +ast-stats-1 FieldDef 176 ( 2.7%) 2 88 ast-stats-1 Block 192 ( 3.0%) 6 32 ast-stats-1 Variant 208 ( 3.2%) 2 104 -ast-stats-1 GenericBound 224 ( 3.5%) 4 56 -ast-stats-1 - Trait 224 ( 3.5%) 4 +ast-stats-1 GenericBound 224 ( 3.4%) 4 56 +ast-stats-1 - Trait 224 ( 3.4%) 4 ast-stats-1 AssocItem 352 ( 5.4%) 4 88 ast-stats-1 - Type 176 ( 2.7%) 2 ast-stats-1 - Fn 176 ( 2.7%) 2 ast-stats-1 GenericParam 480 ( 7.4%) 5 96 -ast-stats-1 Pat 504 ( 7.8%) 7 72 +ast-stats-1 Pat 504 ( 7.7%) 7 72 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1 ast-stats-1 - Ident 360 ( 5.5%) 5 @@ -44,8 +44,8 @@ ast-stats-1 Ty 896 (13.8%) 14 64 ast-stats-1 - Ptr 64 ( 1.0%) 1 ast-stats-1 - Ref 64 ( 1.0%) 1 ast-stats-1 - ImplicitSelf 128 ( 2.0%) 2 -ast-stats-1 - Path 640 ( 9.9%) 10 -ast-stats-1 Item 1_224 (18.9%) 9 136 +ast-stats-1 - Path 640 ( 9.8%) 10 +ast-stats-1 Item 1_224 (18.8%) 9 136 ast-stats-1 - Trait 136 ( 2.1%) 1 ast-stats-1 - Enum 136 ( 2.1%) 1 ast-stats-1 - ForeignMod 136 ( 2.1%) 1 @@ -53,7 +53,7 @@ ast-stats-1 - Impl 136 ( 2.1%) 1 ast-stats-1 - Fn 272 ( 4.2%) 2 ast-stats-1 - Use 408 ( 6.3%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_488 +ast-stats-1 Total 6_504 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -73,12 +73,12 @@ ast-stats-2 FnDecl 120 ( 1.7%) 5 24 ast-stats-2 Attribute 128 ( 1.8%) 4 32 ast-stats-2 - DocComment 32 ( 0.5%) 1 ast-stats-2 - Normal 96 ( 1.4%) 3 -ast-stats-2 FieldDef 160 ( 2.3%) 2 80 ast-stats-2 Stmt 160 ( 2.3%) 5 32 ast-stats-2 - Local 32 ( 0.5%) 1 ast-stats-2 - Semi 32 ( 0.5%) 1 ast-stats-2 - Expr 96 ( 1.4%) 3 ast-stats-2 Param 160 ( 2.3%) 4 40 +ast-stats-2 FieldDef 176 ( 2.5%) 2 88 ast-stats-2 Block 192 ( 2.7%) 6 32 ast-stats-2 Variant 208 ( 2.9%) 2 104 ast-stats-2 GenericBound 224 ( 3.2%) 4 56 @@ -98,7 +98,7 @@ ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Block 216 ( 3.0%) 3 -ast-stats-2 PathSegment 792 (11.2%) 33 24 +ast-stats-2 PathSegment 792 (11.1%) 33 24 ast-stats-2 Ty 896 (12.6%) 14 64 ast-stats-2 - Ptr 64 ( 0.9%) 1 ast-stats-2 - Ref 64 ( 0.9%) 1 @@ -113,7 +113,7 @@ ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.8%) 2 ast-stats-2 - Use 544 ( 7.7%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_088 +ast-stats-2 Total 7_104 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size