From ddcee6f86e2e2e927ceaa8ab785686ff7c41a483 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 21 Apr 2021 13:24:12 -0500 Subject: [PATCH 1/5] Modify `FieldDef` to allow parsing unnamed fields --- compiler/rustc_ast/src/ast.rs | 28 ++++- compiler/rustc_ast/src/mut_visit.rs | 12 +- compiler/rustc_ast/src/visit.rs | 12 +- compiler/rustc_ast_lowering/src/item.rs | 57 ++++++---- compiler/rustc_ast_pretty/src/pprust/state.rs | 21 +++- .../src/deriving/clone.rs | 8 +- .../src/deriving/cmp/eq.rs | 8 +- .../src/deriving/generic/mod.rs | 34 +++++- compiler/rustc_expand/src/expand.rs | 12 +- compiler/rustc_expand/src/placeholders.rs | 6 +- compiler/rustc_parse/src/parser/item.rs | 107 ++++++++++++++---- .../rustc_resolve/src/build_reduced_graph.rs | 8 +- compiler/rustc_resolve/src/def_collector.rs | 13 ++- 13 files changed, 246 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e7f19f06ebef5..62b2d75cc3398 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2471,19 +2471,39 @@ impl VisibilityKind { } } +/// Named field type +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct NamedField { + // FIXME: Maybe remove option and use UnnamedField::Type instead of NamedField? + pub ident: Option, + pub ty: P, +} + +/// Unnamed field type +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum UnnamedField { + Struct(Vec), + Union(Vec), + Type(P), +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum FieldVariant { + Named(NamedField), + Unnamed(UnnamedField), +} + /// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct FieldDef { pub attrs: Vec, - pub id: NodeId, pub span: Span, pub vis: Visibility, - pub ident: Option, - - pub ty: P, + pub id: NodeId, pub is_placeholder: bool, + pub variant: FieldVariant, } /// Fields and constructor ids of enum variants and structs. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 05f57f978c7a4..3722075d407be 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -909,13 +909,19 @@ pub fn noop_flat_map_field_def( mut fd: FieldDef, visitor: &mut T, ) -> SmallVec<[FieldDef; 1]> { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; + let FieldDef { span, vis, id, attrs, is_placeholder: _, variant } = &mut fd; visitor.visit_span(span); - visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_vis(vis); visitor.visit_id(id); - visitor.visit_ty(ty); visit_attrs(attrs, visitor); + match variant { + FieldVariant::Named(NamedField { ident, ty }) => { + visit_opt(ident, |ident| visitor.visit_ident(ident)); + visitor.visit_ty(ty); + } + // FIXME: Handle Unnamed variant + _ => {} + } smallvec![fd] } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 3f35919ae6a2a..05a68c57048e0 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -673,10 +673,16 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: & pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) { visitor.visit_vis(&field.vis); - if let Some(ident) = field.ident { - visitor.visit_ident(ident); + match &field.variant { + FieldVariant::Named(NamedField { ident, ty }) => { + if let Some(ident) = ident { + visitor.visit_ident(*ident); + } + visitor.visit_ty(&ty); + } + // FIXME: Handle Unnamed variant + _ => {} } - visitor.visit_ty(&field.ty); 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 5fd8f7eb33a1f..93243fb3e6d4b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -792,31 +792,42 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { - let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind { - let t = self.lower_path_ty( - &f.ty, - qself, - path, - ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) - ImplTraitContext::disallowed(), - ); - self.arena.alloc(t) - } else { - self.lower_ty(&f.ty, ImplTraitContext::disallowed()) - }; let hir_id = self.lower_node_id(f.id); + + let (ident, ty) = match &f.variant { + FieldVariant::Named(NamedField { ident, ty }) => { + let ident = match ident { + Some(ident) => *ident, + // FIXME(jseyfried): positional field hygiene. + None => Ident::new(sym::integer(index), f.span), + }; + + let ty = if let TyKind::Path(ref qself, ref path) = ty.kind { + let t = self.lower_path_ty( + ty, + qself, + path, + ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) + ImplTraitContext::disallowed(), + ); + self.arena.alloc(t) + } else { + self.lower_ty(&ty, ImplTraitContext::disallowed()) + }; + + (ident, ty) + } + // FIXME: Handle Unnamed variant. Currently creates useless data to pass the typecheck + _ => { + let ident = Ident::from_str_and_span("", f.span); + let ty = self.arena.alloc(hir::Ty { hir_id, kind: hir::TyKind::Err, span: f.span }); + (ident, &*ty) + } + }; + self.lower_attrs(hir_id, &f.attrs); - hir::FieldDef { - span: f.span, - hir_id, - ident: match f.ident { - Some(ident) => ident, - // FIXME(jseyfried): positional field hygiene. - None => Ident::new(sym::integer(index), f.span), - }, - vis: self.lower_visibility(&f.vis, None), - ty, - } + + hir::FieldDef { span: f.span, hir_id, ident, vis: self.lower_visibility(&f.vis, None), ty } } fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 789d2c296e291..373e0bbc0f003 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1408,7 +1408,13 @@ 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) + match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => { + s.print_type(ty) + } + // FIXME: Handle Unnamed variant + _ => {} + } }); self.pclose(); } @@ -1430,10 +1436,15 @@ impl<'a> State<'a> { self.maybe_print_comment(field.span.lo()); self.print_outer_attributes(&field.attrs); self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.s.word(","); + match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident, ty }) => { + self.print_ident(ident.unwrap()); + self.word_nbsp(":"); + self.print_type(ty); + self.s.word(","); + } + _ => {} + } } self.bclose(span) diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index ca1226b445d97..2f2cac6b9c0dd 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -127,7 +127,13 @@ fn cs_clone_shallow( fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec, variant: &VariantData) { for field in variant.fields() { // let _: AssertParamIsClone; - assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); + match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => { + assert_ty_bounds(cx, stmts, ty.clone(), field.span, "AssertParamIsClone") + } + // FIXME: Handle Unnamed variant + _ => {} + } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 79f35ad5819f1..33146eccc62be 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -78,7 +78,13 @@ fn cs_total_eq_assert( ) { for field in variant.fields() { // let _: AssertParamIsEq; - assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => { + assert_ty_bounds(cx, stmts, ty.clone(), field.span, "AssertParamIsEq") + } + // FIXME: Handle Unnamed variant + _ => {} + } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index a3decff3ae7e1..afa66e08cc06d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -720,8 +720,15 @@ impl<'a> TraitDef<'a> { from_scratch: bool, use_temporaries: bool, ) -> P { - let field_tys: Vec> = - struct_def.fields().iter().map(|field| field.ty.clone()).collect(); + let field_tys: Vec> = struct_def + .fields() + .iter() + .filter_map(|field| match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => Some(ty.clone()), + // FIXME: Handle Unnamed variant + _ => None, + }) + .collect(); let methods = self .methods @@ -769,7 +776,13 @@ 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().filter_map( + |field| match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => Some(ty.clone()), + // FIXME: Handle Unnamed variant + _ => None, + }, + )) } let methods = self @@ -1515,8 +1528,11 @@ impl<'a> TraitDef<'a> { let mut just_spans = Vec::new(); for field in struct_def.fields() { let sp = field.span.with_ctxt(self.span.ctxt()); - match field.ident { - Some(ident) => named_idents.push((ident, sp)), + match field.variant { + ast::FieldVariant::Named(ast::NamedField { ident: Some(ident), ty: _ }) => { + named_idents.push((ident, sp)) + } + // FIXME: Handle Unnamed variant _ => just_spans.push(sp), } } @@ -1576,7 +1592,13 @@ impl<'a> TraitDef<'a> { let val = if use_temporaries { val } else { cx.expr_deref(sp, val) }; let val = cx.expr(sp, ast::ExprKind::Paren(val)); - ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); + match struct_field.variant { + ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => { + ident_exprs.push((sp, ident, val, &struct_field.attrs[..])) + } + // FIXME: Handle Unnamed variant + _ => {} + } } let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 529ef7e4611e2..176f8e99433ab 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -328,11 +328,15 @@ impl InvocationKind { // and it holds because only inert attributes are supported in this position. match self { InvocationKind::Attr { item: Annotatable::FieldDef(field), .. } - | InvocationKind::Derive { item: Annotatable::FieldDef(field), .. } - if field.ident.is_none() => + | InvocationKind::Derive { item: Annotatable::FieldDef(field), .. } => match field + .variant { - Some(field.vis.clone()) - } + ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) if ident.is_none() => { + Some(field.vis.clone()) + } + // FIXME: Handle Unnamed variant + _ => None, + }, _ => None, } } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 6586ba138fb99..c88408b3bacef 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -155,12 +155,12 @@ pub fn placeholder( }]), AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef { attrs: Default::default(), - id, - ident: None, span, - ty: ty(), vis, + id, is_placeholder: true, + // FIXME: Maybe change to UnnamedField? + variant: ast::FieldVariant::Named(ast::NamedField { ident: None, ty: ty() }) }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { attrs: Default::default(), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 299b9a959c56f..d6fdc10bea09f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1280,11 +1280,11 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, - ident: None, - id: DUMMY_NODE_ID, - ty, attrs, + id: DUMMY_NODE_ID, is_placeholder: false, + // FIXME: Maybe change to UnnamedField? + variant: FieldVariant::Named(NamedField { ident: None, ty }), }, TrailingToken::MaybeComma, )) @@ -1354,20 +1354,25 @@ 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 Some(last_segment) = segments.last() { - recovered = self.check_trailing_angle_brackets( - last_segment, - &[&token::Comma, &token::CloseDelim(token::Brace)], - ); - if recovered { - // Handle a case like `Vec>,` where we can continue parsing fields - // after the comma - self.eat(&token::Comma); - // `check_trailing_angle_brackets` already emitted a nicer error - err.cancel(); + match &a_var.variant { + FieldVariant::Named(NamedField { ident: _, ty }) => { + if let TyKind::Path(_, Path { segments, .. }) = &ty.kind { + if let Some(last_segment) = segments.last() { + recovered = self.check_trailing_angle_brackets( + last_segment, + &[&token::Comma, &token::CloseDelim(token::Brace)], + ); + if recovered { + // Handle a case like `Vec>,` where we can continue parsing fields + // after the comma + self.eat(&token::Comma); + // `check_trailing_angle_brackets` already emitted a nicer error + err.cancel(); + } + } } } + _ => {} } if self.token.is_ident() { @@ -1395,27 +1400,85 @@ impl<'a> Parser<'a> { Ok(a_var) } - /// Parses a structure field. - fn parse_name_and_ty( + fn parse_named_field_type( &mut self, lo: Span, vis: Visibility, attrs: Vec, + name: Ident, ) -> PResult<'a, FieldDef> { - let name = self.parse_ident_common(false)?; - self.expect(&token::Colon)?; let ty = self.parse_ty()?; Ok(FieldDef { + attrs, span: lo.to(self.prev_token.span), - ident: Some(name), vis, id: DUMMY_NODE_ID, - ty, - attrs, is_placeholder: false, + variant: FieldVariant::Named(NamedField { ident: Some(name), ty }), }) } + fn parse_unnamed_field_type( + &mut self, + lo: Span, + vis: Visibility, + attrs: Vec, + ) -> PResult<'a, FieldDef> { + if self.eat_keyword(kw::Struct) { + let (fields, _) = self.parse_record_struct_body()?; + Ok(FieldDef { + attrs, + span: lo.to(self.prev_token.span), + vis, + id: DUMMY_NODE_ID, + is_placeholder: false, + variant: FieldVariant::Unnamed(UnnamedField::Struct(fields)), + }) + } + // Can't directly eat_keyword because technically a union could + // be a user defined type, so disambiguate by looking ahead for a `{` + else if self.token.is_keyword(kw::Union) + && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) + { + self.bump(); // `union` + let (fields, _) = self.parse_record_struct_body()?; + Ok(FieldDef { + attrs, + span: lo.to(self.prev_token.span), + vis, + id: DUMMY_NODE_ID, + is_placeholder: false, + variant: FieldVariant::Unnamed(UnnamedField::Union(fields)), + }) + } else { + let ty = self.parse_ty()?; + Ok(FieldDef { + attrs, + span: lo.to(self.prev_token.span), + vis, + id: DUMMY_NODE_ID, + is_placeholder: false, + variant: FieldVariant::Unnamed(UnnamedField::Type(ty)), + }) + } + } + + /// Parses a structure field. + fn parse_name_and_ty( + &mut self, + lo: Span, + vis: Visibility, + attrs: Vec, + ) -> PResult<'a, FieldDef> { + let name = self.parse_ident_or_underscore()?; + self.expect(&token::Colon)?; + if name.name == kw::Underscore { + self.parse_unnamed_field_type(lo, vis, attrs) + } else { + self.parse_named_field_type(lo, vis, attrs, name) + } + } + /// Parses a declarative macro 2.0 definition. /// The `macro` keyword has already been parsed. /// ``` diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d77022a65e439..36bcd966911c3 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -342,7 +342,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let field_names = vdata .fields() .iter() - .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name))) + .filter_map(|field| match &field.variant { + ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => { + Some(respan(field.span, ident.map_or(kw::Empty, |ident| ident.name))) + } + // FIXME: Handle Unnamed variant + _ => None, + }) .collect(); self.insert_field_names(def_id, field_names); } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 17f0c39e39735..7f8501c9a6612 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -58,15 +58,20 @@ impl<'a, 'b> DefCollector<'a, 'b> { this.resolver.placeholder_field_indices[&node_id] }) }; - if field.is_placeholder { let old_index = self.resolver.placeholder_field_indices.insert(field.id, index(self)); assert!(old_index.is_none(), "placeholder field index is reset for a node ID"); self.visit_macro_invoc(field.id); } else { - let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); - let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span); - self.with_parent(def, |this| visit::walk_field_def(this, field)); + match field.variant { + ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => { + let name = ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); + let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span); + self.with_parent(def, |this| visit::walk_field_def(this, field)); + } + // FIXME: Handle Unnamed variant + _ => {} + } } } From 7cec0d7db1eda4d703ec4cb835c67533b555c209 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 21 Apr 2021 17:32:50 -0500 Subject: [PATCH 2/5] Add feature gate for `unnamed_fields` --- compiler/rustc_ast_passes/src/feature_gate.rs | 16 ++++++++++++++++ compiler/rustc_feature/src/active.rs | 4 ++++ compiler/rustc_span/src/symbol.rs | 1 + .../feature-gates/feature-gate-unnamed_fields.rs | 16 ++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 src/test/ui/feature-gates/feature-gate-unnamed_fields.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c803c8a83b15c..30b287209f6dd 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -300,6 +300,21 @@ impl<'a> PostExpansionVisitor<'a> { } impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { + fn visit_field_def(&mut self, s: &'a ast::FieldDef) { + match s.variant { + ast::FieldVariant::Unnamed(_) => { + gate_feature_post!( + &self, + unnamed_fields, + s.span, + "unnamed fields are not yet fully implemented" + ) + } + _ => {} + } + visit::walk_field_def(self, s); + } + fn visit_attribute(&mut self, attr: &ast::Attribute) { let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); @@ -692,6 +707,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(destructuring_assignment, "destructuring assignments are unstable"); } gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable"); + gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index a410826d3fda6..76bc4d4780715 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -642,6 +642,9 @@ declare_features! ( /// Allows `extern "wasm" fn` (active, wasm_abi, "1.53.0", Some(83788), None), + /// Allows unnamed fields of struct and union type + (active, unnamed_fields, "1.53.0", Some(49804), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -669,6 +672,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::const_generics_defaults, sym::inherent_associated_types, sym::type_alias_impl_trait, + sym::unnamed_fields, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 52270f0e6277b..2b1cd25a516d0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1254,6 +1254,7 @@ symbols! { unix, unlikely, unmarked_api, + unnamed_fields, unpin, unreachable, unreachable_code, diff --git a/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs b/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs new file mode 100644 index 0000000000000..b9f916bdea63a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs @@ -0,0 +1,16 @@ +struct Foo { + _: struct { //~ ERROR unnamed fields are not yet fully implemented + foo: u8 + } +} + +union Bar { + _: union { //~ ERROR unnamed fields are not yet fully implemented + bar: u8 + } +} + +struct S; +struct Baz { + _: S //~ ERROR unnamed fields are not yet fully implemented +} From a6ce268ad342a6036247a79b5c0ef7c09b8f6eab Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 21 Apr 2021 17:35:12 -0500 Subject: [PATCH 3/5] Fix clippy not compiling with new `FieldDef` --- .../clippy_lints/src/excessive_bools.rs | 8 +++++-- .../clippy_lints/src/manual_non_exhaustive.rs | 9 ++++++-- .../clippy/clippy_utils/src/ast_utils.rs | 22 ++++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 249ee27330bf9..8f878f9481ba4 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{in_macro, match_path_ast}; -use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; +use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind, FieldVariant, NamedField}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -145,7 +145,11 @@ impl EarlyLintPass for ExcessiveBools { let struct_bools = variant_data .fields() .iter() - .filter(|field| is_bool_ty(&field.ty)) + .filter(|field| match &field.variant { + FieldVariant::Named(NamedField{ty, ident:_}) if is_bool_ty(ty) => true, + // FIXME: Handle Unnamed variant + _ => false + }) .count() .try_into() .unwrap(); 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 dc19805b50abd..22cb2b771e5cd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; +use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind, FieldVariant, NamedField}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; @@ -141,7 +141,12 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: } fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + match &field.variant { + FieldVariant::Named(NamedField{ty, ident}) => + is_private(field) && ty.kind.is_unit() && ident.map_or(true, |n| n.as_str().starts_with('_')), + // FIXME: Handle Unnamed variant + _ => false + } } fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index eaea3e636f9c3..8cfe4bc426a5e 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -362,11 +362,23 @@ pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool { } pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { - l.is_placeholder == r.is_placeholder - && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) - && eq_vis(&l.vis, &r.vis) - && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r)) - && eq_ty(&l.ty, &r.ty) + if l.is_placeholder != r.is_placeholder + || !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + || !eq_vis(&l.vis, &r.vis) + { + false + } else { + match (&l.variant, &r.variant) { + (FieldVariant::Named(NamedField{ident: l_ident, ty: l_ty}), + FieldVariant::Named(NamedField{ident: r_ident, ty: r_ty})) => + both(l_ident, r_ident, |l, r| eq_id(*l, *r)) + && eq_ty(l_ty, r_ty), + // FIXME: Compare two unnamed fields and check for equality + (FieldVariant::Unnamed(_), + FieldVariant::Named(_)) => false, + _ => false + } + } } pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { From b951e6c18c7b9548d43439b06d9d161e7c54333c Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 21 Apr 2021 19:58:46 -0500 Subject: [PATCH 4/5] Fix recovery enabled on field name parsing --- compiler/rustc_parse/src/parser/item.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d6fdc10bea09f..33fbdd036294a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -832,16 +832,20 @@ impl<'a> Parser<'a> { } fn parse_rename(&mut self) -> PResult<'a, Option> { - if self.eat_keyword(kw::As) { self.parse_ident_or_underscore().map(Some) } else { Ok(None) } + if self.eat_keyword(kw::As) { + self.parse_ident_or_underscore(true).map(Some) + } else { + Ok(None) + } } - fn parse_ident_or_underscore(&mut self) -> PResult<'a, Ident> { + fn parse_ident_or_underscore(&mut self, recover: bool) -> PResult<'a, Ident> { match self.token.ident() { Some((ident @ Ident { name: kw::Underscore, .. }, false)) => { self.bump(); Ok(ident) } - _ => self.parse_ident(), + _ => self.parse_ident_common(recover), } } @@ -1056,7 +1060,8 @@ impl<'a> Parser<'a> { &mut self, m: Option, ) -> PResult<'a, (Ident, P, Option>)> { - let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?; + let id = + if m.is_none() { self.parse_ident_or_underscore(true) } else { self.parse_ident() }?; // Parse the type of a `const` or `static mut?` item. // That is, the `":" $ty` fragment. @@ -1470,7 +1475,7 @@ impl<'a> Parser<'a> { vis: Visibility, attrs: Vec, ) -> PResult<'a, FieldDef> { - let name = self.parse_ident_or_underscore()?; + let name = self.parse_ident_or_underscore(false)?; self.expect(&token::Colon)?; if name.name == kw::Underscore { self.parse_unnamed_field_type(lo, vis, attrs) From 07b13c8de5da312fd2fd12fa8506f85ca511a0e1 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 22 Apr 2021 13:18:29 -0500 Subject: [PATCH 5/5] Implement equality in clippy for `UnnamedField` --- src/tools/clippy/clippy_utils/src/ast_utils.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8cfe4bc426a5e..8b859d3a9f9a2 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -371,11 +371,18 @@ pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { match (&l.variant, &r.variant) { (FieldVariant::Named(NamedField{ident: l_ident, ty: l_ty}), FieldVariant::Named(NamedField{ident: r_ident, ty: r_ty})) => - both(l_ident, r_ident, |l, r| eq_id(*l, *r)) - && eq_ty(l_ty, r_ty), - // FIXME: Compare two unnamed fields and check for equality - (FieldVariant::Unnamed(_), - FieldVariant::Named(_)) => false, + both(l_ident, r_ident, |l, r| eq_id(*l, *r)) && eq_ty(l_ty, r_ty), + + (FieldVariant::Unnamed(UnnamedField::Struct(l_v)), + FieldVariant::Unnamed(UnnamedField::Struct(r_v))) + | (FieldVariant::Unnamed(UnnamedField::Union(l_v)), + FieldVariant::Unnamed(UnnamedField::Union(r_v))) => + l_v.iter().zip(r_v.iter()).all(|(l_f, r_f)| eq_struct_field(l_f, r_f)), + + (FieldVariant::Unnamed(UnnamedField::Type(l_ty)), + FieldVariant::Unnamed(UnnamedField::Type(r_ty))) => + eq_ty(l_ty, r_ty), + _ => false } }