self.span_err(ty.span, "`virtual` structs have been removed from the language"); } <del> self.parse_where_clause(&mut generics); </del> <ins> // There is a special case worth noting here, as reported in issue #17904. // If we are parsing a tuple struct it is the case that the where clause // should follow the field list. Like so: // // struct Foo<T>(T) where T: Copy; // // If we are parsing a normal record-style struct it is the case // that the where clause comes before the body, and after the generics. // So if we look ahead and see a brace or a where-clause we begin // parsing a record style struct. // // Otherwise if we look ahead and see a paren we parse a tuple-style // struct. let (fields, ctor_id) = if self.token.is_keyword(keywords::Where) { self.parse_where_clause(&mut generics); if self.eat(&token::Semi) { // If we see a: `struct Foo<T> where T: Copy;` style decl. (Vec::new(), Some(ast::DUMMY_NODE_ID)) } else { // If we see: `struct Foo<T> where T: Copy { ... }` (self.parse_record_struct_body(&class_name), None) } // No `where` so: `struct Foo<T>;` } else if self.eat(&token::Semi) { (Vec::new(), Some(ast::DUMMY_NODE_ID)) // Record-style struct definition } else if self.token == token::OpenDelim(token::Brace) { let fields = self.parse_record_struct_body(&class_name); (fields, None) // Tuple-style struct definition with optional where-clause. } else { let fields = self.parse_tuple_struct_body(&class_name, &mut generics); (fields, Some(ast::DUMMY_NODE_ID)) }; </ins> <del> let mut fields: Vec<StructField>; let is_tuple_like; </del> <ins> (class_name, ItemStruct(P(ast::StructDef { fields: fields, ctor_id: ctor_id, }), generics), None) } pub fn parse_record_struct_body(&mut self, class_name: &ast::Ident) -> Vec<StructField> { let mut fields = Vec::new(); </ins> if self.eat(&token::OpenDelim(token::Brace)) { <del> // It's a record-like struct. is_tuple_like = false; fields = Vec::new(); </del> while self.token != token::CloseDelim(token::Brace) { fields.push(self.parse_struct_decl_field(true)); } if fields.len() == 0 { self.fatal(format!("unit-like struct definition should be  <del> written as `struct {};`", token::get_ident(class_name))[]); </del> <ins> written as `struct {};`", token::get_ident(class_name.clone()))[]); </ins> } self.bump(); <del> } else if self.check(&token::OpenDelim(token::Paren)) { // It's a tuple-like struct. is_tuple_like = true; fields = self.parse_unspanned_seq( </del> <ins> } else { let token_str = self.this_token_to_string(); self.fatal(format!("expected `where`, or `{}` after struct  name, found `{}`", "{", token_str)[]); } fields } pub fn parse_tuple_struct_body(&mut self, class_name: &ast::Ident, generics: &mut ast::Generics) -> Vec<StructField> { // This is the case where we find `struct Foo<T>(T) where T: Copy;` if self.check(&token::OpenDelim(token::Paren)) { let fields = self.parse_unspanned_seq( </ins> &token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), seq_sep_trailing_allowed(token::Comma), |p| { <del> let attrs = p.parse_outer_attributes(); let lo = p.span.lo; let struct_field_ = ast::StructField_ { kind: UnnamedField(p.parse_visibility()), id: ast::DUMMY_NODE_ID, ty: p.parse_ty_sum(), attrs: attrs, }; spanned(lo, p.span.hi, struct_field_) }); </del> <ins> let attrs = p.parse_outer_attributes(); let lo = p.span.lo; let struct_field_ = ast::StructField_ { kind: UnnamedField(p.parse_visibility()), id: ast::DUMMY_NODE_ID, ty: p.parse_ty_sum(), attrs: attrs, }; spanned(lo, p.span.hi, struct_field_) }); </ins> if fields.len() == 0 { self.fatal(format!("unit-like struct definition should be  <del> written as `struct {};`", token::get_ident(class_name))[]); </del> <ins> written as `struct {};`", token::get_ident(class_name.clone()))[]); </ins> } <ins> self.parse_where_clause(generics); </ins> self.expect(&token::Semi); <del> } else if self.eat(&token::Semi) { // It's a unit-like struct. is_tuple_like = true; fields = Vec::new(); </del> <ins> fields // This is the case where we just see struct Foo<T> where T: Copy; } else if self.token.is_keyword(keywords::Where) { self.parse_where_clause(generics); self.expect(&token::Semi); Vec::new() // This case is where we see: `struct Foo<T>;` </ins> } else { let token_str = self.this_token_to_string(); <del> self.fatal(format!("expected `{}`, `(`, or `;` after struct  name, found `{}`", "{", token_str)[]) </del> <ins> self.fatal(format!("expected `where`, `{}`, `(`, or `;` after struct  name, found `{}`", "{", token_str)[]); </ins> } <del> let _ = ast::DUMMY_NODE_ID;  // FIXME: Workaround for crazy bug. let new_id = ast::DUMMY_NODE_ID; (class_name, ItemStruct(P(ast::StructDef { fields: fields, ctor_id: if is_tuple_like { Some(new_id) } else { None }, }), generics), None) </del> } /// Parse a structure field declaration