Skip to content
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

libsyntax: Disallow struct literals after if, while, match, and #14885

Merged
merged 1 commit into from
Jun 24, 2014
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
8 changes: 4 additions & 4 deletions src/doc/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -3004,7 +3004,7 @@ ten_times(|j| println!("hello, {}", j));
### While loops

~~~~ {.ebnf .gram}
while_expr : "while" expr '{' block '}' ;
while_expr : "while" no_struct_literal_expr '{' block '}' ;
~~~~

A `while` loop begins by evaluating the boolean loop conditional expression.
Expand Down Expand Up @@ -3071,7 +3071,7 @@ A `continue` expression is only permitted in the body of a loop.
### For expressions

~~~~ {.ebnf .gram}
for_expr : "for" pat "in" expr '{' block '}' ;
for_expr : "for" pat "in" no_struct_literal_expr '{' block '}' ;
~~~~

A `for` expression is a syntactic construct for looping over elements
Expand Down Expand Up @@ -3105,7 +3105,7 @@ for i in range(0u, 256) {
### If expressions

~~~~ {.ebnf .gram}
if_expr : "if" expr '{' block '}'
if_expr : "if" no_struct_literal_expr '{' block '}'
else_tail ? ;

else_tail : "else" [ if_expr
Expand All @@ -3126,7 +3126,7 @@ then any `else` block is executed.
### Match expressions

~~~~ {.ebnf .gram}
match_expr : "match" expr '{' match_arm * '}' ;
match_expr : "match" no_struct_literal_expr '{' match_arm * '}' ;

match_arm : attribute * match_pat "=>" [ expr "," | '{' block '}' ] ;

Expand Down
30 changes: 16 additions & 14 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub enum restriction {
RESTRICT_STMT_EXPR,
RESTRICT_NO_BAR_OP,
RESTRICT_NO_BAR_OR_DOUBLEBAR_OP,
RESTRICT_NO_STRUCT_LITERAL,
}

type ItemInfo = (Ident, Item_, Option<Vec<Attribute> >);
Expand Down Expand Up @@ -2024,8 +2025,9 @@ impl<'a> Parser<'a> {

return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT));
} else if self.token == token::LBRACE {
// This might be a struct literal.
if self.looking_at_struct_literal() {
// This is a struct literal, unless we're prohibited from
// parsing struct literals here.
if self.restriction != RESTRICT_NO_STRUCT_LITERAL {
// It's a struct literal.
self.bump();
let mut fields = Vec::new();
Expand All @@ -2042,6 +2044,14 @@ impl<'a> Parser<'a> {
&[token::COMMA], &[token::RBRACE]);
}

if fields.len() == 0 && base.is_none() {
let last_span = self.last_span;
self.span_err(last_span,
"structure literal must either have at \
least one field or use functional \
structure update syntax");
}

hi = self.span.hi;
self.expect(&token::RBRACE);
ex = ExprStruct(pth, fields, base);
Expand Down Expand Up @@ -2548,7 +2558,7 @@ impl<'a> Parser<'a> {
// parse an 'if' expression ('if' token already eaten)
pub fn parse_if_expr(&mut self) -> Gc<Expr> {
let lo = self.last_span.lo;
let cond = self.parse_expr();
let cond = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL);
let thn = self.parse_block();
let mut els: Option<Gc<Expr>> = None;
let mut hi = thn.span.hi;
Expand Down Expand Up @@ -2633,7 +2643,7 @@ impl<'a> Parser<'a> {
let lo = self.last_span.lo;
let pat = self.parse_pat();
self.expect_keyword(keywords::In);
let expr = self.parse_expr();
let expr = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL);
let loop_block = self.parse_block();
let hi = self.span.hi;

Expand All @@ -2642,7 +2652,7 @@ impl<'a> Parser<'a> {

pub fn parse_while_expr(&mut self) -> Gc<Expr> {
let lo = self.last_span.lo;
let cond = self.parse_expr();
let cond = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL);
let body = self.parse_block();
let hi = body.span.hi;
return self.mk_expr(lo, hi, ExprWhile(cond, body));
Expand All @@ -2655,17 +2665,9 @@ impl<'a> Parser<'a> {
self.mk_expr(lo, hi, ExprLoop(body, opt_ident))
}

// For distinguishing between struct literals and blocks
fn looking_at_struct_literal(&mut self) -> bool {
self.token == token::LBRACE &&
((self.look_ahead(1, |t| token::is_plain_ident(t)) &&
self.look_ahead(2, |t| *t == token::COLON))
|| self.look_ahead(1, |t| *t == token::DOTDOT))
}

fn parse_match_expr(&mut self) -> Gc<Expr> {
let lo = self.last_span.lo;
let discriminant = self.parse_expr();
let discriminant = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL);
self.commit_expr_expecting(discriminant, token::LBRACE);
let mut arms: Vec<Arm> = Vec::new();
while self.token != token::RBRACE {
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/bind-struct-early-modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

fn main() {
struct Foo { x: int }
match Foo { x: 10 } {
match (Foo { x: 10 }) {
Foo { ref x: ref x } => {}, //~ ERROR unexpected `:`
_ => {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn main() {
}
}

match S { bar: 1 } {
match (S { bar: 1 }) {
S { bar: x } => {
x += 1; //~ ERROR re-assignment of immutable variable `x`
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/borrowck-move-error-with-note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl Drop for S {
}

fn move_in_match() {
match S {f: "foo".to_string(), g: "bar".to_string()} {
match (S {f: "foo".to_string(), g: "bar".to_string()}) {
S { //~ ERROR cannot move out of type `S`, which defines the `Drop` trait
f: _s, //~ NOTE attempting to move value to here
g: _t //~ NOTE and here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Drop for S {
}

fn move_in_match() {
match S {f:"foo".to_string()} {
match (S {f:"foo".to_string()}) {
S {f:_s} => {}
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/match-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct S { a: int }
enum E { C(int) }

fn main() {
match S { a: 1 } {
match (S { a: 1 }) {
C(_) => (), //~ ERROR mismatched types: expected `S` but found `E`
_ => ()
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/non-exhaustive-pattern-witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ enum Color {
}

fn struct_with_a_nested_enum_and_vector() {
match Foo { first: true, second: None } {
match (Foo { first: true, second: None }) {
//~^ ERROR non-exhaustive patterns: `Foo{first: false, second: Some([_, _, _, _])}` not covered
Foo { first: true, second: None } => (),
Foo { first: true, second: Some(_) } => (),
Expand Down Expand Up @@ -71,4 +71,4 @@ fn main() {
struct_with_a_nested_enum_and_vector();
enum_with_multiple_missing_variants();
enum_struct_variant();
}
}
28 changes: 28 additions & 0 deletions src/test/compile-fail/struct-literal-in-for.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Foo {
x: int,
}

impl Foo {
fn hi(&self) -> bool {
true
}
}

fn main() {
for x in Foo {
x: 3 //~ ERROR expected one of `;`, `}`
}.hi() {
println!("yo");
}
}

28 changes: 28 additions & 0 deletions src/test/compile-fail/struct-literal-in-if.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Foo {
x: int,
}

impl Foo {
fn hi(&self) -> bool {
true
}
}

fn main() {
if Foo {
x: 3 //~ ERROR expected one of `;`, `}`
}.hi() {
println!("yo");
}
}

24 changes: 24 additions & 0 deletions src/test/compile-fail/struct-literal-in-match-discriminant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Foo {
x: int,
}

fn main() {
match Foo {
x: 3 //~ ERROR expected `=>`
} {
Foo {
x: x
} => {}
}
}

28 changes: 28 additions & 0 deletions src/test/compile-fail/struct-literal-in-while.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Foo {
x: int,
}

impl Foo {
fn hi(&self) -> bool {
true
}
}

fn main() {
while Foo {
x: 3 //~ ERROR expected one of `;`, `}`
}.hi() {
println!("yo");
}
}

2 changes: 1 addition & 1 deletion src/test/compile-fail/struct-no-fields-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct Foo;

fn f2() {
let _end_stmt = Foo { };
//~^ ERROR: unit-like struct construction is written with no trailing `{ }`
//~^ ERROR: structure literal must either have at least one field
}

fn main() {}
2 changes: 1 addition & 1 deletion src/test/compile-fail/struct-no-fields-3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct Foo;

fn g3() {
let _mid_tuple = (Foo { }, 2);
//~^ ERROR: unit-like struct construction is written with no trailing `{ }`
//~^ ERROR: structure literal must either have at least one field
Copy link

Choose a reason for hiding this comment

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

English - "either ..." or? should perhaps be:
structure literal must have at least one field (removing the 'either')
Same for below changes

}

fn main() {}
2 changes: 1 addition & 1 deletion src/test/compile-fail/struct-no-fields-4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct Foo;

fn h4() {
let _end_of_tuple = (3, Foo { });
//~^ ERROR: unit-like struct construction is written with no trailing `{ }`
//~^ ERROR: structure literal must either have at least one field
}

fn main() {}
2 changes: 1 addition & 1 deletion src/test/compile-fail/struct-no-fields-5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct Foo;

fn i5() {
let _end_of_block = { Foo { } };
//~^ ERROR: unit-like struct construction is written with no trailing `{ }`
//~^ ERROR: structure literal must either have at least one field
}

fn main() {}
6 changes: 3 additions & 3 deletions src/test/debuginfo/lexical-scope-in-match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ fn main() {
_ => {}
}

match Struct { x: 237, y: 238 } {
match (Struct { x: 237, y: 238 }) {
Struct { x: shadowed, y: local_to_arm } => {

zzz();
sentinel();
}
}

match Struct { x: 239, y: 240 } {
match (Struct { x: 239, y: 240 }) {
// ignored field
Struct { x: shadowed, .. } => {

Expand All @@ -122,7 +122,7 @@ fn main() {
}
}

match Struct { x: 241, y: 242 } {
match (Struct { x: 241, y: 242 }) {
// with literal
Struct { x: shadowed, y: 242 } => {

Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/guards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn main() {
assert_eq!(a, 2);

let b =
match Pair {x: 10, y: 20} {
match (Pair {x: 10, y: 20}) {
x if x.x < 5 && x.y < 5 => { 1 }
Pair {x: x, y: y} if x == 10 && y == 20 => { 2 }
Pair {x: _x, y: _y} => { 3 }
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/match-in-macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum Foo {

macro_rules! match_inside_expansion(
() => (
match B { b1:29 , bb1: 100} {
match (B { b1:29 , bb1: 100}) {
B { b1:b2 , bb1:bb2 } => b2+bb2
}
)
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/nested-exhaustive-match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
struct Foo { foo: bool, bar: Option<int>, baz: int }

pub fn main() {
match Foo{foo: true, bar: Some(10), baz: 20} {
match (Foo{foo: true, bar: Some(10), baz: 20}) {
Foo{foo: true, bar: Some(_), ..} => {}
Foo{foo: false, bar: None, ..} => {}
Foo{foo: true, bar: None, ..} => {}
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/nested-patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct D { a: int, d: C }
struct C { c: int }

pub fn main() {
match A {a: 10, b: 20} {
match (A {a: 10, b: 20}) {
x@A {a, b: 20} => { assert!(x.a == 10); assert!(a == 10); }
A {b: _b, ..} => { fail!(); }
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/struct_variant_xc_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extern crate struct_variant_xc_aux;
use struct_variant_xc_aux::{StructVariant, Variant};

pub fn main() {
let arg = match StructVariant { arg: 42 } {
let arg = match (StructVariant { arg: 42 }) {
Variant(_) => unreachable!(),
StructVariant { arg } => arg
};
Expand Down