Skip to content

syntax: Merge parsing code for structures and variants #29714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,8 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
self.context.span_handler.span_err(span, "empty tuple structs and enum variants \
are not allowed, use unit structs and \
enum variants instead");
self.context.span_handler.span_help(span, "remove trailing `()` to make a unit \
struct or unit enum variant");
}
}
visit::walk_struct_def(self, s)
Expand Down
81 changes: 35 additions & 46 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ pub enum BoundParsingMode {
Modified,
}

/// `pub` should be parsed in struct fields and not parsed in variant fields
#[derive(Clone, Copy, PartialEq)]
pub enum ParsePub {
Yes,
No,
}

/// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
/// dropped into the token stream, which happens while parsing the result of
/// macro expansion). Placement of these is not as complex as I feared it would
Expand Down Expand Up @@ -4686,18 +4693,23 @@ impl<'a> Parser<'a> {
VariantData::Unit(ast::DUMMY_NODE_ID)
} else {
// If we see: `struct Foo<T> where T: Copy { ... }`
VariantData::Struct(try!(self.parse_record_struct_body()), ast::DUMMY_NODE_ID)
VariantData::Struct(try!(self.parse_record_struct_body(ParsePub::Yes)),
ast::DUMMY_NODE_ID)
}
// No `where` so: `struct Foo<T>;`
} else if try!(self.eat(&token::Semi) ){
VariantData::Unit(ast::DUMMY_NODE_ID)
// Record-style struct definition
} else if self.token == token::OpenDelim(token::Brace) {
VariantData::Struct(try!(self.parse_record_struct_body()), ast::DUMMY_NODE_ID)
VariantData::Struct(try!(self.parse_record_struct_body(ParsePub::Yes)),
ast::DUMMY_NODE_ID)
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(token::Paren) {
VariantData::Tuple(try!(self.parse_tuple_struct_body(&mut generics)),
ast::DUMMY_NODE_ID)
let body = VariantData::Tuple(try!(self.parse_tuple_struct_body(ParsePub::Yes)),
ast::DUMMY_NODE_ID);
generics.where_clause = try!(self.parse_where_clause());
try!(self.expect(&token::Semi));
body
} else {
let token_str = self.this_token_to_string();
return Err(self.fatal(&format!("expected `where`, `{{`, `(`, or `;` after struct \
Expand All @@ -4707,11 +4719,11 @@ impl<'a> Parser<'a> {
Ok((class_name, ItemStruct(vdata, generics), None))
}

pub fn parse_record_struct_body(&mut self) -> PResult<Vec<StructField>> {
pub fn parse_record_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
let mut fields = Vec::new();
if try!(self.eat(&token::OpenDelim(token::Brace)) ){
while self.token != token::CloseDelim(token::Brace) {
fields.push(try!(self.parse_struct_decl_field(true)));
fields.push(try!(self.parse_struct_decl_field(parse_pub)));
}

try!(self.bump());
Expand All @@ -4725,9 +4737,7 @@ impl<'a> Parser<'a> {
Ok(fields)
}

pub fn parse_tuple_struct_body(&mut self,
generics: &mut ast::Generics)
-> PResult<Vec<StructField>> {
pub fn parse_tuple_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
// This is the case where we find `struct Foo<T>(T) where T: Copy;`
// Unit like structs are handled in parse_item_struct function
let fields = try!(self.parse_unspanned_seq(
Expand All @@ -4738,16 +4748,20 @@ impl<'a> Parser<'a> {
let attrs = try!(p.parse_outer_attributes());
let lo = p.span.lo;
let struct_field_ = ast::StructField_ {
kind: UnnamedField(try!(p.parse_visibility())),
kind: UnnamedField (
if parse_pub == ParsePub::Yes {
try!(p.parse_visibility())
} else {
Inherited
}
),
id: ast::DUMMY_NODE_ID,
ty: try!(p.parse_ty_sum()),
attrs: attrs,
};
Ok(spanned(lo, p.span.hi, struct_field_))
}));

generics.where_clause = try!(self.parse_where_clause());
try!(self.expect(&token::Semi));
Ok(fields)
}

Expand Down Expand Up @@ -4775,12 +4789,12 @@ impl<'a> Parser<'a> {
}

/// Parse an element of a struct definition
fn parse_struct_decl_field(&mut self, allow_pub: bool) -> PResult<StructField> {
fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<StructField> {

let attrs = try!(self.parse_outer_attributes());

if try!(self.eat_keyword(keywords::Pub) ){
if !allow_pub {
if parse_pub == ParsePub::No {
let span = self.last_span;
self.span_err(span, "`pub` is not allowed here");
}
Expand Down Expand Up @@ -5133,18 +5147,6 @@ impl<'a> Parser<'a> {
Ok((ident, ItemTy(ty, tps), None))
}

/// Parse a structure-like enum variant definition
/// this should probably be renamed or refactored...
fn parse_struct_def(&mut self) -> PResult<VariantData> {
let mut fields: Vec<StructField> = Vec::new();
while self.token != token::CloseDelim(token::Brace) {
fields.push(try!(self.parse_struct_decl_field(false)));
}
try!(self.bump());

Ok(VariantData::Struct(fields, ast::DUMMY_NODE_ID))
}

/// Parse the part of an "enum" decl following the '{'
fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<EnumDef> {
let mut variants = Vec::new();
Expand All @@ -5157,34 +5159,21 @@ impl<'a> Parser<'a> {
let struct_def;
let mut disr_expr = None;
let ident = try!(self.parse_ident());
if try!(self.eat(&token::OpenDelim(token::Brace)) ){
if self.check(&token::OpenDelim(token::Brace)) {
// Parse a struct variant.
all_nullary = false;
struct_def = try!(self.parse_struct_def());
struct_def = VariantData::Struct(try!(self.parse_record_struct_body(ParsePub::No)),
ast::DUMMY_NODE_ID);
} else if self.check(&token::OpenDelim(token::Paren)) {
all_nullary = false;
let arg_tys = try!(self.parse_enum_variant_seq(
&token::OpenDelim(token::Paren),
&token::CloseDelim(token::Paren),
seq_sep_trailing_allowed(token::Comma),
|p| p.parse_ty_sum()
));
let mut fields = Vec::new();
for ty in arg_tys {
fields.push(Spanned { span: ty.span, node: ast::StructField_ {
ty: ty,
kind: ast::UnnamedField(ast::Inherited),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
}});
}
struct_def = ast::VariantData::Tuple(fields, ast::DUMMY_NODE_ID);
struct_def = VariantData::Tuple(try!(self.parse_tuple_struct_body(ParsePub::No)),
ast::DUMMY_NODE_ID);
} else if try!(self.eat(&token::Eq) ){
disr_expr = Some(try!(self.parse_expr_nopanic()));
any_disr = disr_expr.as_ref().map(|expr| expr.span);
struct_def = ast::VariantData::Unit(ast::DUMMY_NODE_ID);
struct_def = VariantData::Unit(ast::DUMMY_NODE_ID);
} else {
struct_def = ast::VariantData::Unit(ast::DUMMY_NODE_ID);
struct_def = VariantData::Unit(ast::DUMMY_NODE_ID);
}

let vr = ast::Variant_ {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -Z parse-only

// For style and consistency reasons, non-parametrized enum variants must
// be used simply as `ident` instead of `ident ()`.
// This test-case covers enum declaration.

enum Foo {
Bar(), //~ ERROR nullary enum variants are written with no trailing `( )`
Baz(), //~ ERROR nullary enum variants are written with no trailing `( )`
Bar(), //~ ERROR empty tuple structs and enum variants are not allowed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is pretty major regression to the error message. It was pretty clearly pointing out at the mistake before and telling how to fix it. Now it seems to suggest it is not allowed altogether, although only the syntax is incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a note then "note: Remove the trailing ()". Good enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tuple structs and enum variants with zero fields are indeed not allowed altogether, it's not just syntax.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a “help: remove trailing () if you meant to write empty enum variant” would be good enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

//~^ HELP remove trailing `()` to make a unit struct or unit enum variant
Baz(), //~ ERROR empty tuple structs and enum variants are not allowed
//~^ HELP remove trailing `()` to make a unit struct or unit enum variant
Bazar
}

Expand Down
12 changes: 10 additions & 2 deletions src/test/compile-fail/issue-16819.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@

struct TS ( //~ ERROR empty tuple structs and enum variants are not allowed
#[cfg(untrue)]
int,
i32,
);

enum E {
TV ( //~ ERROR empty tuple structs and enum variants are not allowed
#[cfg(untrue)]
i32,
)
}

fn main() {
let s = S;
let s = TS;
let tv = E::TV;
}