about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2020-03-07 17:16:29 +0100
committerMazdak Farrokhzad <twingoow@gmail.com>2020-03-10 08:55:18 +0100
commit458383dd337f276d091fa2de81bb7c1d31dde5fa (patch)
tree064c57b7be465e8364441365612d52d8bcc084ea /src
parentba3ae46de972f3e70319c25a5da69378454cb88d (diff)
downloadrust-458383dd337f276d091fa2de81bb7c1d31dde5fa.tar.gz
rust-458383dd337f276d091fa2de81bb7c1d31dde5fa.zip
parse_if_expr: recover on attributes
Diffstat (limited to 'src')
-rw-r--r--src/librustc_parse/lib.rs1
-rw-r--r--src/librustc_parse/parser/expr.rs43
-rw-r--r--src/librustc_parse/parser/item.rs3
-rw-r--r--src/test/ui/if-attrs/else-attrs.rs2
-rw-r--r--src/test/ui/if-attrs/else-attrs.stderr19
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad.rs16
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad.stderr68
-rw-r--r--src/test/ui/parser/doc-comment-in-if-statement.rs2
-rw-r--r--src/test/ui/parser/doc-comment-in-if-statement.stderr8
9 files changed, 98 insertions, 64 deletions
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<Expr>> {
-        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 <src_pat> in <src_expr> <src_loop_block>` (`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