From 654d6444feafaa7bae17057d8b98823c7556ea14 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 13 Jun 2014 19:09:12 -0700 Subject: [PATCH] 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] --- src/doc/rust.md | 8 ++--- src/libsyntax/parse/parser.rs | 30 ++++++++++--------- .../bind-struct-early-modifiers.rs | 2 +- .../borrowck-match-binding-is-assignment.rs | 2 +- .../borrowck-move-error-with-note.rs | 2 +- .../borrowck-move-out-of-struct-with-dtor.rs | 2 +- src/test/compile-fail/match-struct.rs | 2 +- .../non-exhaustive-pattern-witness.rs | 4 +-- .../compile-fail/struct-literal-in-for.rs | 28 +++++++++++++++++ src/test/compile-fail/struct-literal-in-if.rs | 28 +++++++++++++++++ .../struct-literal-in-match-discriminant.rs | 24 +++++++++++++++ .../compile-fail/struct-literal-in-while.rs | 28 +++++++++++++++++ src/test/compile-fail/struct-no-fields-2.rs | 2 +- src/test/compile-fail/struct-no-fields-3.rs | 2 +- src/test/compile-fail/struct-no-fields-4.rs | 2 +- src/test/compile-fail/struct-no-fields-5.rs | 2 +- src/test/debuginfo/lexical-scope-in-match.rs | 6 ++-- src/test/run-pass/guards.rs | 2 +- src/test/run-pass/match-in-macro.rs | 2 +- src/test/run-pass/nested-exhaustive-match.rs | 2 +- src/test/run-pass/nested-patterns.rs | 2 +- src/test/run-pass/struct_variant_xc_match.rs | 2 +- 22 files changed, 147 insertions(+), 37 deletions(-) create mode 100644 src/test/compile-fail/struct-literal-in-for.rs create mode 100644 src/test/compile-fail/struct-literal-in-if.rs create mode 100644 src/test/compile-fail/struct-literal-in-match-discriminant.rs create mode 100644 src/test/compile-fail/struct-literal-in-while.rs diff --git a/src/doc/rust.md b/src/doc/rust.md index e7b6941b622e8..28ccea5ad1773 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 4a75473ac1141..e3a028ed4d6c4 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 >); @@ -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 { 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> = 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 { 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 { 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 = 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 b9ae0d341d9a2..3671cf110d81e 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 72d9afd4d2b2b..6b5dd570e34e2 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 976a574a18f7d..167e78d7ed09e 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 e6cb2d67324a4..3d13cbe30c5a2 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 1ebb8ed5ddc62..4956528856b66 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 22e93d70858e4..d0f51bf2da43b 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 0000000000000..ccd711d83758d --- /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 or the MIT license +// , 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 0000000000000..d63c216c3bee4 --- /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 or the MIT license +// , 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 0000000000000..c740ba020629d --- /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 or the MIT license +// , 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 0000000000000..7b2c11e2597a2 --- /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 or the MIT license +// , 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 cd555f8d91788..4f973f81b1641 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 2595c5e8acf87..e594683feed17 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 fee5c35e85005..60a0a85d0abf1 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 bd196e881aa35..940fa9c7f273f 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 5f13c780ba865..b347afbbbcdb5 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 bf29fa603c7ec..8fa4af528567b 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 5772a2c437adb..c30c4a714b39d 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 e09ac9450ba3c..b2a47e0ccb88b 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, 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 1cbed9c29ece2..08816d345b400 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 8cb1cdd2a7ff3..e7bc61c1fb99c 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 };