diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 10d524776a1..fe75062ee50 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -2,6 +2,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(bindings_after_at)] use rustc_ast::ast; use rustc_ast::token::{self, Nonterminal}; diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index a41beb77119..7f6f90431fc 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1514,13 +1514,16 @@ impl<'a> Parser<'a> { let thn = if self.eat_keyword(kw::Else) || !cond.returns() { self.error_missing_if_cond(lo, cond.span) } else { + let attrs = self.parse_outer_attributes()?; // For recovery. let not_block = self.token != token::OpenDelim(token::Brace); - self.parse_block().map_err(|mut err| { + let block = self.parse_block().map_err(|mut err| { if not_block { err.span_label(lo, "this `if` expression has a condition, but no block"); } err - })? + })?; + self.error_on_if_block_attrs(lo, false, block.span, &attrs); + block }; let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None }; Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs)) @@ -1562,12 +1565,40 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P> { - if self.eat_keyword(kw::If) { - self.parse_if_expr(AttrVec::new()) + let ctx_span = self.prev_token.span; // `else` + let attrs = self.parse_outer_attributes()?; // For recovery. + let expr = if self.eat_keyword(kw::If) { + self.parse_if_expr(AttrVec::new())? } else { let blk = self.parse_block()?; - Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) - } + self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()) + }; + self.error_on_if_block_attrs(ctx_span, true, expr.span, &attrs); + Ok(expr) + } + + fn error_on_if_block_attrs( + &self, + ctx_span: Span, + is_ctx_else: bool, + branch_span: Span, + attrs: &[ast::Attribute], + ) { + let (span, last) = match attrs { + [] => return, + [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), + }; + let ctx = if is_ctx_else { "else" } else { "if" }; + self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches") + .span_label(branch_span, "the attributes are attached to this branch") + .span_label(ctx_span, format!("the branch belongs to this `{}`", ctx)) + .span_suggestion( + span, + "remove the attributes", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); } /// Parses `for in ` (`for` token already eaten). diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 5add3fcc802..15cafef97fb 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -352,8 +352,7 @@ impl<'a> Parser<'a> { fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> { let (start, end) = match attrs { [] => return Ok(()), - [x0] => (x0, x0), - [x0, .., xn] => (x0, xn), + [x0 @ xn] | [x0, .., xn] => (x0, xn), }; let msg = if end.is_doc_comment() { "expected item after doc comment" diff --git a/src/test/ui/if-attrs/else-attrs.rs b/src/test/ui/if-attrs/else-attrs.rs index 4394b2100c1..85da7cf6bb8 100644 --- a/src/test/ui/if-attrs/else-attrs.rs +++ b/src/test/ui/if-attrs/else-attrs.rs @@ -8,7 +8,7 @@ fn if_else_parse_error() { #[cfg(FALSE)] fn else_attr_ifparse_error() { if true { - } else #[attr] if false { //~ ERROR expected + } else #[attr] if false { //~ ERROR outer attributes are not allowed } else { } } diff --git a/src/test/ui/if-attrs/else-attrs.stderr b/src/test/ui/if-attrs/else-attrs.stderr index af25b6abc0a..2733377054d 100644 --- a/src/test/ui/if-attrs/else-attrs.stderr +++ b/src/test/ui/if-attrs/else-attrs.stderr @@ -4,18 +4,17 @@ error: expected expression, found keyword `else` LL | } #[attr] else if false { | ^^^^ expected expression -error: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/else-attrs.rs:11:12 | -LL | } else #[attr] if false { - | ^ expected `{` - | -help: try placing this code inside a block - | -LL | } else #[attr] { if false { -LL | } else { -LL | } } - | +LL | } else #[attr] if false { + | _______----_^^^^^^^_- + | | | | + | | | help: remove the attributes + | | the branch belongs to this `else` +LL | | } else { +LL | | } + | |_____- the attributes are attached to this branch error: expected expression, found keyword `else` --> $DIR/else-attrs.rs:20:15 diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs index f3980a59648..09f494bdc2f 100644 --- a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs @@ -39,35 +39,35 @@ fn main() {} #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } //~^ ERROR expected one of #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } //~^ ERROR expected one of #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } -//~^ ERROR expected `{`, found `#` +//~^ ERROR outer attributes are not allowed on `if` #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr index af490150913..6dfe7aad6ea 100644 --- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr @@ -136,14 +136,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } | = 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: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:41:37 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } - | -- ^ -- help: try placing this code inside a block: `{ {} }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:43:38 @@ -159,13 +159,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:47:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } - | ^ -- help: try placing this code inside a block: `{ {} }` - | | - | expected `{` + | ---- ^^^^^^^ -- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:49:46 @@ -175,22 +176,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } | = 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: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } - | ^ ------- help: try placing this code inside a block: `{ if 0 {} }` - | | - | expected `{` + | ---- ^^^^^^^ ------- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` -error: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:53:50 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } - | -- ^ -- help: try placing this code inside a block: `{ {} }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:55:51 @@ -200,14 +202,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } | = 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: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:57:45 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } - | -- ^ -- help: try placing this code inside a block: `{ {} }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:59:46 @@ -223,13 +225,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:63:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } - | ^ -- help: try placing this code inside a block: `{ {} }` - | | - | expected `{` + | ---- ^^^^^^^ -- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:65:54 @@ -239,22 +242,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } | = 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: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:67:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } - | ^ --------------- help: try placing this code inside a block: `{ if let _ = 0 {} }` - | | - | expected `{` + | ---- ^^^^^^^ --------------- the attributes are attached to this branch + | | | + | | help: remove the attributes + | the branch belongs to this `else` -error: expected `{`, found `#` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:69:66 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } - | -- ^ -- help: try placing this code inside a block: `{ {} }` + | -- ^^^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:71:67 diff --git a/src/test/ui/parser/doc-comment-in-if-statement.rs b/src/test/ui/parser/doc-comment-in-if-statement.rs index 69b174b4cb3..343eac1b81f 100644 --- a/src/test/ui/parser/doc-comment-in-if-statement.rs +++ b/src/test/ui/parser/doc-comment-in-if-statement.rs @@ -1,5 +1,5 @@ fn main() { if true /*!*/ {} - //~^ ERROR expected `{`, found doc comment `/*!*/` + //~^ ERROR outer attributes are not allowed on //~| ERROR expected outer doc comment } diff --git a/src/test/ui/parser/doc-comment-in-if-statement.stderr b/src/test/ui/parser/doc-comment-in-if-statement.stderr index 1fe3735daf0..af21b78733f 100644 --- a/src/test/ui/parser/doc-comment-in-if-statement.stderr +++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr @@ -6,14 +6,14 @@ LL | if true /*!*/ {} | = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items -error: expected `{`, found doc comment `/*!*/` +error: outer attributes are not allowed on `if` and `else` branches --> $DIR/doc-comment-in-if-statement.rs:2:13 | LL | if true /*!*/ {} - | -- ^^^^^ -- help: try placing this code inside a block: `{ {} }` + | -- ^^^^^ -- the attributes are attached to this branch | | | - | | expected `{` - | this `if` expression has a condition, but no block + | | help: remove the attributes + | the branch belongs to this `if` error: aborting due to 2 previous errors