diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 570ef67bbccfd..79e5fe7cc9a07 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4875,9 +4875,6 @@ impl<'a> Parser<'a> { self.span_err(ty.span, "`virtual` structs have been removed from the language"); } - let mut fields: Vec; - let is_tuple_like; - // 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: @@ -4892,68 +4889,102 @@ impl<'a> Parser<'a> { // Otherwise if we look ahead and see a paren we parse a tuple-style // struct. - // Will parse the where-clause if it precedes the brace. - self.parse_where_clause(&mut generics); + 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 where T: Copy;` style decl. + (Vec::new(), Some(ast::DUMMY_NODE_ID)) + } else { + // If we see: `struct Foo where T: Copy { ... }` + (self.parse_record_struct_body(&class_name), None) + } + // No `where` so: `struct Foo;` + } 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)) + }; + (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 { + let mut fields = Vec::new(); if self.eat(&token::OpenDelim(token::Brace)) { - // It's a record-like struct. - is_tuple_like = false; - fields = Vec::new(); 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 \ - written as `struct {};`", - token::get_ident(class_name))[]); + written as `struct {};`", + token::get_ident(class_name.clone()))[]); } + self.bump(); - } else if self.check(&token::OpenDelim(token::Paren)) { - // It's a tuple-like struct. - is_tuple_like = true; - fields = self.parse_unspanned_seq( + } 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 { + // This is the case where we find `struct Foo(T) where T: Copy;` + if self.check(&token::OpenDelim(token::Paren)) { + let fields = self.parse_unspanned_seq( &token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), seq_sep_trailing_allowed(token::Comma), |p| { - 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_) - }); + 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_) + }); + if fields.len() == 0 { self.fatal(format!("unit-like struct definition should be \ - written as `struct {};`", - token::get_ident(class_name))[]); + written as `struct {};`", + token::get_ident(class_name.clone()))[]); } - self.parse_where_clause(&mut generics); + + self.parse_where_clause(generics); self.expect(&token::Semi); - } else if self.token.is_keyword(keywords::Where) || self.eat(&token::Semi) { - // We can find a where clause here. - self.parse_where_clause(&mut generics); - // It's a unit-like struct. - is_tuple_like = true; - fields = Vec::new(); + fields + // This is the case where we just see struct Foo 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;` } else { let token_str = self.this_token_to_string(); - self.fatal(format!("expected `{}`, `(`, or `;` after struct \ - name, found `{}`", "{", - token_str)[]) + self.fatal(format!("expected `where`, `{}`, `(`, or `;` after struct \ + name, found `{}`", "{", token_str)[]); } - - 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) } /// Parse a structure field declaration diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9702c79719c64..2176cd13b287b 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1067,7 +1067,6 @@ impl<'a> State<'a> { span: codemap::Span) -> IoResult<()> { try!(self.print_ident(ident)); try!(self.print_generics(generics)); - try!(self.print_where_clause(generics)); if ast_util::struct_def_is_tuple_like(struct_def) { if !struct_def.fields.is_empty() { try!(self.popen()); @@ -1086,10 +1085,12 @@ impl<'a> State<'a> { )); try!(self.pclose()); } + try!(self.print_where_clause(generics)); try!(word(&mut self.s, ";")); try!(self.end()); self.end() // close the outer-box } else { + try!(self.print_where_clause(generics)); try!(self.nbsp()); try!(self.bopen()); try!(self.hardbreak_if_not_bol()); diff --git a/src/test/compile-fail/issue-17904.rs b/src/test/compile-fail/issue-17904.rs new file mode 100644 index 0000000000000..96ba712bbae8b --- /dev/null +++ b/src/test/compile-fail/issue-17904.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Baz where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax. +struct Baz where U: Eq(U) -> R; // Notice this parses as well. +struct Baz(U) where U: Eq; // This rightfully signals no error as well. +struct Foo where T: Copy, (T); //~ ERROR unexpected token in `where` clause +struct Bar { x: T } where T: Copy //~ ERROR expected item, found `where` + +fn main() {} diff --git a/src/test/compile-fail/unsized.rs b/src/test/compile-fail/unsized.rs index 43db4dfd395b0..92dbea0424b6f 100644 --- a/src/test/compile-fail/unsized.rs +++ b/src/test/compile-fail/unsized.rs @@ -10,7 +10,7 @@ // Test syntax checks for `type` keyword. -struct S1 for type; //~ ERROR expected `{`, `(`, or `;` after struct name, found `for` +struct S1 for type; //~ ERROR expected `where`, `{`, `(`, or `;` after struct name, found `for` pub fn main() { } diff --git a/src/test/run-pass/issue-17904.rs b/src/test/run-pass/issue-17904.rs index c2976378ddad8..3ce347d67e3d9 100644 --- a/src/test/run-pass/issue-17904.rs +++ b/src/test/run-pass/issue-17904.rs @@ -8,10 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that type assignability is used to search for instances when -// making method calls, but only if there aren't any matches without -// it. - struct Foo where T: Copy; struct Bar(T) where T: Copy; struct Bleh(T, U) where T: Copy, U: Sized;