about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-06-13 19:09:12 -0700
committerPatrick Walton <pcwalton@mimiga.net>2014-06-23 15:39:29 -0700
commit654d6444feafaa7bae17057d8b98823c7556ea14 (patch)
tree423bbab4e54f324b269942c6af7b1c01a1eeaa36
parent575710f6cee318e2806b4563eb2887c1f03aaa18 (diff)
downloadrust-654d6444feafaa7bae17057d8b98823c7556ea14.tar.gz
rust-654d6444feafaa7bae17057d8b98823c7556ea14.zip
libsyntax: Disallow struct literals after `if`, `while`, `match`, and
`for...in`.

Closes #14803.

If you used a structure literal after one of these keywords, surround it
in parentheses.

[breaking-change]
-rw-r--r--src/doc/rust.md8
-rw-r--r--src/libsyntax/parse/parser.rs30
-rw-r--r--src/test/compile-fail/bind-struct-early-modifiers.rs2
-rw-r--r--src/test/compile-fail/borrowck-match-binding-is-assignment.rs2
-rw-r--r--src/test/compile-fail/borrowck-move-error-with-note.rs2
-rw-r--r--src/test/compile-fail/borrowck-move-out-of-struct-with-dtor.rs2
-rw-r--r--src/test/compile-fail/match-struct.rs2
-rw-r--r--src/test/compile-fail/non-exhaustive-pattern-witness.rs4
-rw-r--r--src/test/compile-fail/struct-literal-in-for.rs28
-rw-r--r--src/test/compile-fail/struct-literal-in-if.rs28
-rw-r--r--src/test/compile-fail/struct-literal-in-match-discriminant.rs24
-rw-r--r--src/test/compile-fail/struct-literal-in-while.rs28
-rw-r--r--src/test/compile-fail/struct-no-fields-2.rs2
-rw-r--r--src/test/compile-fail/struct-no-fields-3.rs2
-rw-r--r--src/test/compile-fail/struct-no-fields-4.rs2
-rw-r--r--src/test/compile-fail/struct-no-fields-5.rs2
-rw-r--r--src/test/debuginfo/lexical-scope-in-match.rs6
-rw-r--r--src/test/run-pass/guards.rs2
-rw-r--r--src/test/run-pass/match-in-macro.rs2
-rw-r--r--src/test/run-pass/nested-exhaustive-match.rs2
-rw-r--r--src/test/run-pass/nested-patterns.rs2
-rw-r--r--src/test/run-pass/struct_variant_xc_match.rs2
22 files changed, 147 insertions, 37 deletions
diff --git a/src/doc/rust.md b/src/doc/rust.md
index e7b6941b622..28ccea5ad17 100644
--- a/src/doc/rust.md
+++ b/src/doc/rust.md
@@ -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.
@@ -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
@@ -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
@@ -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 '}' ] ;
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 4a75473ac11..e3a028ed4d6 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -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> >);
@@ -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();
@@ -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);
@@ -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;
@@ -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;
 
@@ -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));
@@ -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 {
diff --git a/src/test/compile-fail/bind-struct-early-modifiers.rs b/src/test/compile-fail/bind-struct-early-modifiers.rs
index b9ae0d341d9..3671cf110d8 100644
--- a/src/test/compile-fail/bind-struct-early-modifiers.rs
+++ b/src/test/compile-fail/bind-struct-early-modifiers.rs
@@ -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 `:`
         _ => {}
     }
diff --git a/src/test/compile-fail/borrowck-match-binding-is-assignment.rs b/src/test/compile-fail/borrowck-match-binding-is-assignment.rs
index 72d9afd4d2b..6b5dd570e34 100644
--- a/src/test/compile-fail/borrowck-match-binding-is-assignment.rs
+++ b/src/test/compile-fail/borrowck-match-binding-is-assignment.rs
@@ -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`
         }
diff --git a/src/test/compile-fail/borrowck-move-error-with-note.rs b/src/test/compile-fail/borrowck-move-error-with-note.rs
index 976a574a18f..167e78d7ed0 100644
--- a/src/test/compile-fail/borrowck-move-error-with-note.rs
+++ b/src/test/compile-fail/borrowck-move-error-with-note.rs
@@ -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
diff --git a/src/test/compile-fail/borrowck-move-out-of-struct-with-dtor.rs b/src/test/compile-fail/borrowck-move-out-of-struct-with-dtor.rs
index e6cb2d67324..3d13cbe30c5 100644
--- a/src/test/compile-fail/borrowck-move-out-of-struct-with-dtor.rs
+++ b/src/test/compile-fail/borrowck-move-out-of-struct-with-dtor.rs
@@ -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
     }
diff --git a/src/test/compile-fail/match-struct.rs b/src/test/compile-fail/match-struct.rs
index 1ebb8ed5ddc..4956528856b 100644
--- a/src/test/compile-fail/match-struct.rs
+++ b/src/test/compile-fail/match-struct.rs
@@ -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`
         _ => ()
     }
diff --git a/src/test/compile-fail/non-exhaustive-pattern-witness.rs b/src/test/compile-fail/non-exhaustive-pattern-witness.rs
index 22e93d70858..d0f51bf2da4 100644
--- a/src/test/compile-fail/non-exhaustive-pattern-witness.rs
+++ b/src/test/compile-fail/non-exhaustive-pattern-witness.rs
@@ -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(_) } => (),
@@ -71,4 +71,4 @@ fn main() {
     struct_with_a_nested_enum_and_vector();
     enum_with_multiple_missing_variants();
     enum_struct_variant();
-}
\ No newline at end of file
+}
diff --git a/src/test/compile-fail/struct-literal-in-for.rs b/src/test/compile-fail/struct-literal-in-for.rs
new file mode 100644
index 00000000000..ccd711d8375
--- /dev/null
+++ b/src/test/compile-fail/struct-literal-in-for.rs
@@ -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");
+    }
+}
+
diff --git a/src/test/compile-fail/struct-literal-in-if.rs b/src/test/compile-fail/struct-literal-in-if.rs
new file mode 100644
index 00000000000..d63c216c3be
--- /dev/null
+++ b/src/test/compile-fail/struct-literal-in-if.rs
@@ -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");
+    }
+}
+
diff --git a/src/test/compile-fail/struct-literal-in-match-discriminant.rs b/src/test/compile-fail/struct-literal-in-match-discriminant.rs
new file mode 100644
index 00000000000..c740ba02062
--- /dev/null
+++ b/src/test/compile-fail/struct-literal-in-match-discriminant.rs
@@ -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
+        } => {}
+    }
+}
+
diff --git a/src/test/compile-fail/struct-literal-in-while.rs b/src/test/compile-fail/struct-literal-in-while.rs
new file mode 100644
index 00000000000..7b2c11e2597
--- /dev/null
+++ b/src/test/compile-fail/struct-literal-in-while.rs
@@ -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");
+    }
+}
+
diff --git a/src/test/compile-fail/struct-no-fields-2.rs b/src/test/compile-fail/struct-no-fields-2.rs
index cd555f8d917..4f973f81b16 100644
--- a/src/test/compile-fail/struct-no-fields-2.rs
+++ b/src/test/compile-fail/struct-no-fields-2.rs
@@ -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() {}
diff --git a/src/test/compile-fail/struct-no-fields-3.rs b/src/test/compile-fail/struct-no-fields-3.rs
index 2595c5e8acf..e594683feed 100644
--- a/src/test/compile-fail/struct-no-fields-3.rs
+++ b/src/test/compile-fail/struct-no-fields-3.rs
@@ -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
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/struct-no-fields-4.rs b/src/test/compile-fail/struct-no-fields-4.rs
index fee5c35e850..60a0a85d0ab 100644
--- a/src/test/compile-fail/struct-no-fields-4.rs
+++ b/src/test/compile-fail/struct-no-fields-4.rs
@@ -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() {}
diff --git a/src/test/compile-fail/struct-no-fields-5.rs b/src/test/compile-fail/struct-no-fields-5.rs
index bd196e881aa..940fa9c7f27 100644
--- a/src/test/compile-fail/struct-no-fields-5.rs
+++ b/src/test/compile-fail/struct-no-fields-5.rs
@@ -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() {}
diff --git a/src/test/debuginfo/lexical-scope-in-match.rs b/src/test/debuginfo/lexical-scope-in-match.rs
index 5f13c780ba8..b347afbbbcd 100644
--- a/src/test/debuginfo/lexical-scope-in-match.rs
+++ b/src/test/debuginfo/lexical-scope-in-match.rs
@@ -105,7 +105,7 @@ fn main() {
         _ => {}
     }
 
-    match Struct { x: 237, y: 238 } {
+    match (Struct { x: 237, y: 238 }) {
         Struct { x: shadowed, y: local_to_arm } => {
 
             zzz();
@@ -113,7 +113,7 @@ fn main() {
         }
     }
 
-    match Struct { x: 239, y: 240 } {
+    match (Struct { x: 239, y: 240 }) {
         // ignored field
         Struct { x: shadowed, .. } => {
 
@@ -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 } => {
 
diff --git a/src/test/run-pass/guards.rs b/src/test/run-pass/guards.rs
index bf29fa603c7..8fa4af52856 100644
--- a/src/test/run-pass/guards.rs
+++ b/src/test/run-pass/guards.rs
@@ -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 }
diff --git a/src/test/run-pass/match-in-macro.rs b/src/test/run-pass/match-in-macro.rs
index 5772a2c437a..c30c4a714b3 100644
--- a/src/test/run-pass/match-in-macro.rs
+++ b/src/test/run-pass/match-in-macro.rs
@@ -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
         }
     )
diff --git a/src/test/run-pass/nested-exhaustive-match.rs b/src/test/run-pass/nested-exhaustive-match.rs
index e09ac9450ba..b2a47e0ccb8 100644
--- a/src/test/run-pass/nested-exhaustive-match.rs
+++ b/src/test/run-pass/nested-exhaustive-match.rs
@@ -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, ..} => {}
diff --git a/src/test/run-pass/nested-patterns.rs b/src/test/run-pass/nested-patterns.rs
index 1cbed9c29ec..08816d345b4 100644
--- a/src/test/run-pass/nested-patterns.rs
+++ b/src/test/run-pass/nested-patterns.rs
@@ -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!(); }
     }
diff --git a/src/test/run-pass/struct_variant_xc_match.rs b/src/test/run-pass/struct_variant_xc_match.rs
index 8cb1cdd2a7f..e7bc61c1fb9 100644
--- a/src/test/run-pass/struct_variant_xc_match.rs
+++ b/src/test/run-pass/struct_variant_xc_match.rs
@@ -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
     };