From f6416465a40f6dfac11542351684c523a2d8a062 Mon Sep 17 00:00:00 2001 From: A C Date: Mon, 16 Sep 2019 21:45:13 +0100 Subject: [PATCH 1/2] Add a UI test for correct parsing --- .../ui/parser/stmt_expr_attrs_placement.rs | 22 +++++++++++++++++++ .../parser/stmt_expr_attrs_placement.stderr | 10 +++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/test/ui/parser/stmt_expr_attrs_placement.rs create mode 100644 src/test/ui/parser/stmt_expr_attrs_placement.stderr diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.rs b/src/test/ui/parser/stmt_expr_attrs_placement.rs new file mode 100644 index 0000000000000..b8a794f4b92fa --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.rs @@ -0,0 +1,22 @@ +#![feature(stmt_expr_attributes)] + +// Test that various placements of the inner attribute are parsed correctly, +// or not. + +fn main() { + let a = #![allow(warnings)] (1, 2); + //~^ ERROR an inner attribute is not permitted in this context + + let b = (#![allow(warnings)] 1, 2); + + let c = { + #![allow(warnings)] + (#![allow(warnings)] 1, 2) + }; + + let d = { + #![allow(warnings)] + let e = (#![allow(warnings)] 1, 2); + e + }; +} diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.stderr b/src/test/ui/parser/stmt_expr_attrs_placement.stderr new file mode 100644 index 0000000000000..1886a0f9ba0ba --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.stderr @@ -0,0 +1,10 @@ +error: an inner attribute is not permitted in this context + --> $DIR/stmt_expr_attrs_placement.rs:7:13 + | +LL | let a = #![allow(warnings)] (1, 2); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: aborting due to previous error + From abc30b235b74c6fe9a8ada4e2dfc61bebd22724d Mon Sep 17 00:00:00 2001 From: A C Date: Mon, 16 Sep 2019 21:45:43 +0100 Subject: [PATCH 2/2] Clean up `parse_bottom_expr` --- src/libsyntax/parse/parser/expr.rs | 99 ++++++++++++------------------ 1 file changed, 39 insertions(+), 60 deletions(-) diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 31b28443abbc3..41a2412f9e240 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -88,24 +88,29 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), None) } + fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P> { + match self.parse_expr() { + Ok(expr) => Ok(expr), + Err(mut err) => match self.token.kind { + token::Ident(name, false) + if name == kw::Underscore && self.look_ahead(1, |t| { + t == &token::Comma + }) => { + // Special-case handling of `foo(_, _, _)` + err.emit(); + let sp = self.token.span; + self.bump(); + Ok(self.mk_expr(sp, ExprKind::Err, ThinVec::new())) + } + _ => Err(err), + }, + } + } + + /// Parses a sequence of expressions bounded by parentheses. fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { self.parse_paren_comma_seq(|p| { - match p.parse_expr() { - Ok(expr) => Ok(expr), - Err(mut err) => match p.token.kind { - token::Ident(name, false) - if name == kw::Underscore && p.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `foo(_, _, _)` - err.emit(); - let sp = p.token.span; - p.bump(); - Ok(p.mk_expr(sp, ExprKind::Err, ThinVec::new())) - } - _ => Err(err), - }, - } + p.parse_expr_catch_underscore() }).map(|(r, _)| r) } @@ -786,51 +791,25 @@ impl<'a> Parser<'a> { parse_lit!() } token::OpenDelim(token::Paren) => { - self.bump(); - - attrs.extend(self.parse_inner_attributes()?); - - // `(e)` is parenthesized `e`. - // `(e,)` is a tuple with only one field, `e`. - let mut es = vec![]; - let mut trailing_comma = false; - let mut recovered = false; - while self.token != token::CloseDelim(token::Paren) { - es.push(match self.parse_expr() { - Ok(es) => es, - Err(mut err) => { - // Recover from parse error in tuple list. - match self.token.kind { - token::Ident(name, false) - if name == kw::Underscore && self.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `Foo<(_, _, _)>` - err.emit(); - let sp = self.token.span; - self.bump(); - self.mk_expr(sp, ExprKind::Err, ThinVec::new()) - } - _ => return Ok( - self.recover_seq_parse_error(token::Paren, lo, Err(err)), - ), - } - } - }); - recovered = self.expect_one_of( - &[], - &[token::Comma, token::CloseDelim(token::Paren)], - )?; - if self.eat(&token::Comma) { - trailing_comma = true; - } else { - trailing_comma = false; - break; + let mut first = true; + let parse_leading_attr_expr = |this: &mut Parser<'a>| { + if first { + attrs.extend(this.parse_inner_attributes()?); + first = false; } - } - if !recovered { - self.bump(); - } + this.parse_expr_catch_underscore() + }; + + // (e) is parenthesized e + // (e,) is a tuple with only one field, e + let (es, trailing_comma) = + match self.parse_paren_comma_seq(parse_leading_attr_expr) + { + Ok(x) => x, + Err(err) => return Ok( + self.recover_seq_parse_error(token::Paren, lo, Err(err)), + ), + }; hi = self.prev_span; ex = if es.len() == 1 && !trailing_comma {