From 210dd611aa1bd80ed2f4e663b3c4b87b3cea069a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 28 Feb 2016 17:38:48 -0500 Subject: [PATCH 1/2] implement the `?` operator The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining: `File::open("foo")?.metadata()?.is_dir()`. `?` is accepted on any *expression* that can return a `Result`, e.g. `x()?`, `y!()?`, `{z}?`, `(w)?`, etc. And binds more tightly than unary operators, e.g. `!x?` is parsed as `!(x?)`. cc #31436 --- src/librustc/middle/check_match.rs | 4 + src/librustc_front/hir.rs | 1 + src/librustc_front/lowering.rs | 69 ++++++ src/libsyntax/ast.rs | 3 + src/libsyntax/feature_gate.rs | 9 + src/libsyntax/fold.rs | 3 +- src/libsyntax/parse/parser.rs | 7 +- src/libsyntax/print/pprust.rs | 4 + src/libsyntax/visit.rs | 3 + .../compile-fail/feature-gate-try-operator.rs | 20 ++ src/test/run-pass/try-operator-hygiene.rs | 34 +++ src/test/run-pass/try-operator.rs | 200 ++++++++++++++++++ 12 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-try-operator.rs create mode 100644 src/test/run-pass/try-operator-hygiene.rs create mode 100644 src/test/run-pass/try-operator.rs diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index be8793bac5dbf..94b2b12dca31c 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -344,6 +344,10 @@ fn check_arms(cx: &MatchCheckCtxt, hir::MatchSource::Normal => { span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern") }, + + hir::MatchSource::TryDesugar => { + cx.tcx.sess.span_bug(pat.span, "unreachable try pattern") + }, } } Useful => (), diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index 44e7fa05073a1..ece62364376fc 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -835,6 +835,7 @@ pub enum MatchSource { }, WhileLetDesugar, ForLoopDesugar, + TryDesugar, } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index acc7c6164b54a..291df66755e7d 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -1605,6 +1605,63 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { }); } + // Desugar ExprKind::Try + // From: `?` + ExprKind::Try(ref sub_expr) => { + // to: + // + // { + // match { + // Ok(val) => val, + // Err(err) => { + // return Err(From::from(err)) + // } + // } + // } + + return cache_ids(lctx, e.id, |lctx| { + // expand + let sub_expr = lower_expr(lctx, sub_expr); + + // Ok(val) => val + let ok_arm = { + let val_ident = lctx.str_to_ident("val"); + let val_pat = pat_ident(lctx, e.span, val_ident); + let val_expr = expr_ident(lctx, e.span, val_ident, None); + let ok_pat = pat_ok(lctx, e.span, val_pat); + + arm(hir_vec![ok_pat], val_expr) + }; + + // Err(err) => return Err(From::from(err)) + let err_arm = { + let err_ident = lctx.str_to_ident("err"); + let from_expr = { + let path = std_path(lctx, &["convert", "From", "from"]); + let path = path_global(e.span, path); + let from = expr_path(lctx, path, None); + let err_expr = expr_ident(lctx, e.span, err_ident, None); + + expr_call(lctx, e.span, from, hir_vec![err_expr], None) + }; + let err_expr = { + let path = std_path(lctx, &["result", "Result", "Err"]); + let path = path_global(e.span, path); + let err_ctor = expr_path(lctx, path, None); + expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None) + }; + let err_pat = pat_err(lctx, e.span, pat_ident(lctx, e.span, err_ident)); + let ret_expr = expr(lctx, e.span, + hir::Expr_::ExprRet(Some(err_expr)), None); + + arm(hir_vec![err_pat], ret_expr) + }; + + expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm], + hir::MatchSource::TryDesugar, None) + }) + } + ExprKind::Mac(_) => panic!("Shouldn't exist here"), }, span: e.span, @@ -1819,6 +1876,18 @@ fn block_all(lctx: &LoweringContext, }) } +fn pat_ok(lctx: &LoweringContext, span: Span, pat: P) -> P { + let ok = std_path(lctx, &["result", "Result", "Ok"]); + let path = path_global(span, ok); + pat_enum(lctx, span, path, hir_vec![pat]) +} + +fn pat_err(lctx: &LoweringContext, span: Span, pat: P) -> P { + let err = std_path(lctx, &["result", "Result", "Err"]); + let path = path_global(span, err); + pat_enum(lctx, span, path, hir_vec![pat]) +} + fn pat_some(lctx: &LoweringContext, span: Span, pat: P) -> P { let some = std_path(lctx, &["option", "Option", "Some"]); let path = path_global(span, some); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0dbfb2c7be654..342ba60e553b0 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1022,6 +1022,9 @@ pub enum ExprKind { /// No-op: used solely so we can pretty-print faithfully Paren(P), + + /// `expr?` + Try(P), } /// The explicit Self type in a "qualified path". The actual diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 2302548914223..14a3f93738a32 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -245,6 +245,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // a...b and ...b ("inclusive_range_syntax", "1.7.0", Some(28237), Active), + + // `expr?` + ("question_mark", "1.9.0", Some(31436), Active) ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -570,6 +573,7 @@ pub struct Features { pub staged_api: bool, pub stmt_expr_attributes: bool, pub deprecated: bool, + pub question_mark: bool, } impl Features { @@ -603,6 +607,7 @@ impl Features { staged_api: false, stmt_expr_attributes: false, deprecated: false, + question_mark: false, } } } @@ -1001,6 +1006,9 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { e.span, "inclusive range syntax is experimental"); } + ast::ExprKind::Try(..) => { + self.gate_feature("question_mark", e.span, "the `?` operator is not stable"); + } _ => {} } visit::walk_expr(self, e); @@ -1203,6 +1211,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, staged_api: cx.has_feature("staged_api"), stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), deprecated: cx.has_feature("deprecated"), + question_mark: cx.has_feature("question_mark"), } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 591c1295d6641..9056103d30086 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1332,7 +1332,8 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu fields.move_map(|x| folder.fold_field(x)), maybe_expr.map(|x| folder.fold_expr(x))) }, - ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)) + ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)), + ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)), }, span: folder.new_span(span), attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d9714cc1e25e0..53b5341542987 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2534,6 +2534,12 @@ impl<'a> Parser<'a> { let mut e = e0; let mut hi; loop { + // expr? + while self.eat(&token::Question) { + let hi = self.span.hi; + e = self.mk_expr(lo, hi, ExprKind::Try(e), None); + } + // expr.f if self.eat(&token::Dot) { match self.token { @@ -2907,7 +2913,6 @@ impl<'a> Parser<'a> { } }; - if self.expr_is_complete(&lhs) { // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 return Ok(lhs); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 55c1af44cab85..2cfed1f82f7ec 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2277,6 +2277,10 @@ impl<'a> State<'a> { try!(self.print_inner_attributes_inline(attrs)); try!(self.print_expr(&e)); try!(self.pclose()); + }, + ast::ExprKind::Try(ref e) => { + try!(self.print_expr(e)); + try!(word(&mut self.s, "?")) } } try!(self.ann.post(self, NodeExpr(expr))); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 73ad488e55c93..25aee09e26c8e 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -793,6 +793,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(&output.expr) } } + ExprKind::Try(ref subexpression) => { + visitor.visit_expr(subexpression) + } } visitor.visit_expr_post(expression) diff --git a/src/test/compile-fail/feature-gate-try-operator.rs b/src/test/compile-fail/feature-gate-try-operator.rs new file mode 100644 index 0000000000000..184aa63b23426 --- /dev/null +++ b/src/test/compile-fail/feature-gate-try-operator.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +macro_rules! id { + ($e:expr) => { $e } +} + +fn main() { + id!(x?); //~ error: the `?` operator is not stable (see issue #31436) + //~^ help: add #![feature(question_mark)] to the crate attributes to enable + y?; //~ error: the `?` operator is not stable (see issue #31436) + //~^ help: add #![feature(question_mark)] to the crate attributes to enable +} diff --git a/src/test/run-pass/try-operator-hygiene.rs b/src/test/run-pass/try-operator-hygiene.rs new file mode 100644 index 0000000000000..233c03df4e57e --- /dev/null +++ b/src/test/run-pass/try-operator-hygiene.rs @@ -0,0 +1,34 @@ +// Copyright 2016 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. + +// `expr?` expands to: +// +// match expr { +// Ok(val) => val, +// Err(err) => return From::from(err), +// } +// +// This test verifies that the expansion is hygienic, i.e. it's not affected by other `val` and +// `err` bindings that may be in scope. + +#![feature(question_mark)] + +use std::num::ParseIntError; + +fn main() { + assert_eq!(parse(), Ok(1)); +} + +fn parse() -> Result { + const val: char = 'a'; + const err: char = 'b'; + + Ok("1".parse::()?) +} diff --git a/src/test/run-pass/try-operator.rs b/src/test/run-pass/try-operator.rs new file mode 100644 index 0000000000000..de5ccf09c5923 --- /dev/null +++ b/src/test/run-pass/try-operator.rs @@ -0,0 +1,200 @@ +// Copyright 2016 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. + +#![feature(question_mark)] + +use std::fs::File; +use std::io::{Read, self}; +use std::num::ParseIntError; +use std::str::FromStr; + +fn on_method() -> Result { + Ok("1".parse::()? + "2".parse::()?) +} + +fn in_chain() -> Result { + Ok("3".parse::()?.to_string()) +} + +fn on_call() -> Result { + fn parse(s: &str) -> Result { + s.parse() + } + + Ok(parse("4")?) +} + +fn nested() -> Result { + Ok("5".parse::()?.to_string().parse()?) +} + +fn on_path() -> Result { + let x = "6".parse::(); + + Ok(x?) +} + +fn on_macro() -> Result { + macro_rules! id { + ($e:expr) => { $e } + } + + Ok(id!("7".parse::())?) +} + +fn on_parens() -> Result { + let x = "8".parse::(); + + Ok((x)?) +} + +fn on_block() -> Result { + let x = "9".parse::(); + + Ok({x}?) +} + +fn on_field() -> Result { + struct Pair { a: A, b: B } + + let x = Pair { a: "10".parse::(), b: 0 }; + + Ok(x.a?) +} + +fn on_tuple_field() -> Result { + let x = ("11".parse::(), 0); + + Ok(x.0?) +} + +fn on_try() -> Result { + let x = "12".parse::().map(|i| i.to_string().parse::()); + + Ok(x??) +} + +fn on_binary_op() -> Result { + let x = 13 - "14".parse::()?; + let y = "15".parse::()? - 16; + let z = "17".parse::()? - "18".parse::()?; + + Ok(x + y + z) +} + +fn on_index() -> Result { + let x = [19]; + let y = "0".parse::(); + + Ok(x[y?]) +} + +fn on_args() -> Result { + fn sub(x: i32, y: i32) -> i32 { x - y } + + let x = "20".parse(); + let y = "21".parse(); + + Ok(sub(x?, y?)) +} + +fn on_if() -> Result { + Ok(if true { + "22".parse::() + } else { + "23".parse::() + }?) +} + +fn on_if_let() -> Result { + Ok(if let Ok(..) = "24".parse::() { + "25".parse::() + } else { + "26".parse::() + }?) +} + +fn on_match() -> Result { + Ok(match "27".parse::() { + Err(..) => "28".parse::(), + Ok(..) => "29".parse::(), + }?) +} + +fn tight_binding() -> Result { + fn ok(x: T) -> Result { Ok(x) } + + let x = ok(true); + Ok(!x?) +} + +// just type check +fn merge_error() -> Result { + let mut s = String::new(); + + File::open("foo.txt")?.read_to_string(&mut s)?; + + Ok(s.parse::()? + 1) +} + +fn main() { + assert_eq!(Ok(3), on_method()); + + assert_eq!(Ok("3".to_string()), in_chain()); + + assert_eq!(Ok(4), on_call()); + + assert_eq!(Ok(5), nested()); + + assert_eq!(Ok(6), on_path()); + + assert_eq!(Ok(7), on_macro()); + + assert_eq!(Ok(8), on_parens()); + + assert_eq!(Ok(9), on_block()); + + assert_eq!(Ok(10), on_field()); + + assert_eq!(Ok(11), on_tuple_field()); + + assert_eq!(Ok(12), on_try()); + + assert_eq!(Ok(-3), on_binary_op()); + + assert_eq!(Ok(19), on_index()); + + assert_eq!(Ok(-1), on_args()); + + assert_eq!(Ok(22), on_if()); + + assert_eq!(Ok(25), on_if_let()); + + assert_eq!(Ok(29), on_match()); + + assert_eq!(Ok(false), tight_binding()); +} + +enum Error { + Io(io::Error), + Parse(ParseIntError), +} + +impl From for Error { + fn from(e: io::Error) -> Error { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: ParseIntError) -> Error { + Error::Parse(e) + } +} From 2de4932453a99a19e9033edb47db7a66a612188c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 7 Mar 2016 21:17:31 -0500 Subject: [PATCH 2/2] update error messages in parse-fail tests --- src/test/parse-fail/issue-19096.rs | 2 +- src/test/parse-fail/issue-3036.rs | 2 +- src/test/parse-fail/macros-no-semicolon.rs | 2 +- src/test/parse-fail/match-refactor-to-expr.rs | 2 +- src/test/parse-fail/range-3.rs | 2 +- src/test/parse-fail/range-4.rs | 2 +- src/test/parse-fail/raw-str-unbalanced.rs | 2 +- src/test/parse-fail/removed-syntax-mut-vec-expr.rs | 2 +- src/test/parse-fail/removed-syntax-uniq-mut-expr.rs | 2 +- src/test/parse-fail/removed-syntax-with-1.rs | 2 +- src/test/parse-fail/struct-literal-in-for.rs | 2 +- src/test/parse-fail/struct-literal-in-if.rs | 2 +- src/test/parse-fail/struct-literal-in-match-discriminant.rs | 2 +- src/test/parse-fail/struct-literal-in-while.rs | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/test/parse-fail/issue-19096.rs b/src/test/parse-fail/issue-19096.rs index 0d9a111045a99..6ba0fb5f15bb1 100644 --- a/src/test/parse-fail/issue-19096.rs +++ b/src/test/parse-fail/issue-19096.rs @@ -12,5 +12,5 @@ fn main() { let t = (42, 42); - t.0::; //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `::` + t.0::; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `::` } diff --git a/src/test/parse-fail/issue-3036.rs b/src/test/parse-fail/issue-3036.rs index 1946e984e5dc9..229b12136fc6c 100644 --- a/src/test/parse-fail/issue-3036.rs +++ b/src/test/parse-fail/issue-3036.rs @@ -15,4 +15,4 @@ fn main() { let x = 3 -} //~ ERROR: expected one of `.`, `;`, or an operator, found `}` +} //~ ERROR: expected one of `.`, `;`, `?`, or an operator, found `}` diff --git a/src/test/parse-fail/macros-no-semicolon.rs b/src/test/parse-fail/macros-no-semicolon.rs index 5931631cceefc..1c55d70f60737 100644 --- a/src/test/parse-fail/macros-no-semicolon.rs +++ b/src/test/parse-fail/macros-no-semicolon.rs @@ -12,6 +12,6 @@ fn main() { assert_eq!(1, 2) - assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `assert_eq` + assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq` println!("hello"); } diff --git a/src/test/parse-fail/match-refactor-to-expr.rs b/src/test/parse-fail/match-refactor-to-expr.rs index b99d0493ff733..37b66601e7092 100644 --- a/src/test/parse-fail/match-refactor-to-expr.rs +++ b/src/test/parse-fail/match-refactor-to-expr.rs @@ -14,7 +14,7 @@ fn main() { let foo = match //~ NOTE did you mean to remove this `match` keyword? Some(4).unwrap_or_else(5) - ; //~ ERROR expected one of `.`, `{`, or an operator, found `;` + ; //~ ERROR expected one of `.`, `?`, `{`, or an operator, found `;` println!("{}", foo) } diff --git a/src/test/parse-fail/range-3.rs b/src/test/parse-fail/range-3.rs index 284cdb8c6531a..95aa71b0cdfdb 100644 --- a/src/test/parse-fail/range-3.rs +++ b/src/test/parse-fail/range-3.rs @@ -14,5 +14,5 @@ pub fn main() { let r = 1..2..3; - //~^ ERROR expected one of `.`, `;`, or an operator, found `..` + //~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..` } diff --git a/src/test/parse-fail/range-4.rs b/src/test/parse-fail/range-4.rs index 69898612771e7..4500df116a2de 100644 --- a/src/test/parse-fail/range-4.rs +++ b/src/test/parse-fail/range-4.rs @@ -14,5 +14,5 @@ pub fn main() { let r = ..1..2; - //~^ ERROR expected one of `.`, `;`, or an operator, found `..` + //~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..` } diff --git a/src/test/parse-fail/raw-str-unbalanced.rs b/src/test/parse-fail/raw-str-unbalanced.rs index ce8960edde12f..5c09f68970bc4 100644 --- a/src/test/parse-fail/raw-str-unbalanced.rs +++ b/src/test/parse-fail/raw-str-unbalanced.rs @@ -12,5 +12,5 @@ static s: &'static str = r#" - "## //~ ERROR expected one of `.`, `;`, or an operator, found `#` + "## //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `#` ; diff --git a/src/test/parse-fail/removed-syntax-mut-vec-expr.rs b/src/test/parse-fail/removed-syntax-mut-vec-expr.rs index ab9ff7ac19e5e..301bd0e8b1c9c 100644 --- a/src/test/parse-fail/removed-syntax-mut-vec-expr.rs +++ b/src/test/parse-fail/removed-syntax-mut-vec-expr.rs @@ -13,5 +13,5 @@ fn f() { let v = [mut 1, 2, 3, 4]; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `]`, `{`, or an operator, found `1` + //~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `?`, `]`, `{`, or an operator, found `1` } diff --git a/src/test/parse-fail/removed-syntax-uniq-mut-expr.rs b/src/test/parse-fail/removed-syntax-uniq-mut-expr.rs index ea686aeb6e05a..2f637cf0b4e76 100644 --- a/src/test/parse-fail/removed-syntax-uniq-mut-expr.rs +++ b/src/test/parse-fail/removed-syntax-uniq-mut-expr.rs @@ -13,5 +13,5 @@ fn f() { let a_box = box mut 42; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, or an operator, found `42` + //~^^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `42` } diff --git a/src/test/parse-fail/removed-syntax-with-1.rs b/src/test/parse-fail/removed-syntax-with-1.rs index e9de52c013b3a..156b172a94402 100644 --- a/src/test/parse-fail/removed-syntax-with-1.rs +++ b/src/test/parse-fail/removed-syntax-with-1.rs @@ -18,5 +18,5 @@ fn removed_with() { let a = S { foo: (), bar: () }; let b = S { foo: () with a }; - //~^ ERROR expected one of `,`, `.`, `}`, or an operator, found `with` + //~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with` } diff --git a/src/test/parse-fail/struct-literal-in-for.rs b/src/test/parse-fail/struct-literal-in-for.rs index e57298f72804a..930984555600b 100644 --- a/src/test/parse-fail/struct-literal-in-for.rs +++ b/src/test/parse-fail/struct-literal-in-for.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { for x in Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` + }.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{` println!("yo"); } } diff --git a/src/test/parse-fail/struct-literal-in-if.rs b/src/test/parse-fail/struct-literal-in-if.rs index 6bf41b7a450af..db6a360a5675a 100644 --- a/src/test/parse-fail/struct-literal-in-if.rs +++ b/src/test/parse-fail/struct-literal-in-if.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { if Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` + }.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{` println!("yo"); } } diff --git a/src/test/parse-fail/struct-literal-in-match-discriminant.rs b/src/test/parse-fail/struct-literal-in-match-discriminant.rs index 679f4542824fb..7038cc798c415 100644 --- a/src/test/parse-fail/struct-literal-in-match-discriminant.rs +++ b/src/test/parse-fail/struct-literal-in-match-discriminant.rs @@ -20,6 +20,6 @@ fn main() { } { Foo { x: x - } => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>` + } => {} //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `=>` } } diff --git a/src/test/parse-fail/struct-literal-in-while.rs b/src/test/parse-fail/struct-literal-in-while.rs index b388aac2c5487..75e4eb3de070b 100644 --- a/src/test/parse-fail/struct-literal-in-while.rs +++ b/src/test/parse-fail/struct-literal-in-while.rs @@ -23,7 +23,7 @@ impl Foo { fn main() { while Foo { x: 3 //~ ERROR expected type, found `3` - }.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{` + }.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{` println!("yo"); } }