about summary refs log tree commit diff
path: root/src/librustc_parse/parser
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-03-11 13:05:52 +0000
committerbors <bors@rust-lang.org>2020-03-11 13:05:52 +0000
commitc20d7eecbc0928b57da8fe30b2ef8528e2bdd5be (patch)
treee8f110a9249dbcf8ce290bda1b3c7702488247fa /src/librustc_parse/parser
parent303d8aff6092709edd4dbd35b1c88e9aa40bf6d8 (diff)
parenta77206fa4a35e7926fb4404bd167b06e342b7626 (diff)
downloadrust-c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be.tar.gz
rust-c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be.zip
Auto merge of #69919 - Centril:rollup-fxo33zs, r=Centril
Rollup of 8 pull requests

Successful merges:

 - #66472 (--show-coverage json)
 - #69603 (tidy: replace `make check` with `./x.py test` in documentation)
 - #69760 (Improve expression & attribute parsing)
 - #69828 (fix memory leak when vec::IntoIter panics during drop)
 - #69850 (panic_bounds_check: use caller_location, like PanicFnLangItem)
 - #69876 (Add long error explanation for E0739)
 - #69888 ([Miri] Use a session variable instead of checking for an env var always)
 - #69893 (librustc_codegen_llvm: Use slices instead of 0-terminated strings)

Failed merges:

r? @ghost
Diffstat (limited to 'src/librustc_parse/parser')
-rw-r--r--src/librustc_parse/parser/attr.rs231
-rw-r--r--src/librustc_parse/parser/expr.rs131
-rw-r--r--src/librustc_parse/parser/item.rs44
-rw-r--r--src/librustc_parse/parser/pat.rs2
-rw-r--r--src/librustc_parse/parser/stmt.rs57
-rw-r--r--src/librustc_parse/parser/ty.rs56
6 files changed, 281 insertions, 240 deletions
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index bdd78e671a8..b56dd30739d 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -1,4 +1,4 @@
-use super::{Parser, PathStyle, TokenType};
+use super::{Parser, PathStyle};
 use rustc_ast::ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
@@ -10,14 +10,20 @@ use rustc_span::{Span, Symbol};
 use log::debug;
 
 #[derive(Debug)]
-enum InnerAttributeParsePolicy<'a> {
+pub(super) enum InnerAttrPolicy<'a> {
     Permitted,
-    NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
+    Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
 }
 
 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
                                                      permitted in this context";
 
+pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
+    reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
+    saw_doc_comment: false,
+    prev_attr_sp: None,
+};
+
 impl<'a> Parser<'a> {
     /// Parses attributes that appear before an item.
     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
@@ -25,48 +31,44 @@ impl<'a> Parser<'a> {
         let mut just_parsed_doc_comment = false;
         loop {
             debug!("parse_outer_attributes: self.token={:?}", self.token);
-            match self.token.kind {
-                token::Pound => {
-                    let inner_error_reason = if just_parsed_doc_comment {
-                        "an inner attribute is not permitted following an outer doc comment"
-                    } else if !attrs.is_empty() {
-                        "an inner attribute is not permitted following an outer attribute"
-                    } else {
-                        DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
-                    };
-                    let inner_parse_policy = InnerAttributeParsePolicy::NotPermitted {
-                        reason: inner_error_reason,
-                        saw_doc_comment: just_parsed_doc_comment,
-                        prev_attr_sp: attrs.last().map(|a| a.span),
-                    };
-                    let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
-                    attrs.push(attr);
-                    just_parsed_doc_comment = false;
-                }
-                token::DocComment(s) => {
-                    let attr = self.mk_doc_comment(s);
-                    if attr.style != ast::AttrStyle::Outer {
-                        let span = self.token.span;
-                        let mut err = self.struct_span_err(span, "expected outer doc comment");
-                        err.note(
+            if self.check(&token::Pound) {
+                let inner_error_reason = if just_parsed_doc_comment {
+                    "an inner attribute is not permitted following an outer doc comment"
+                } else if !attrs.is_empty() {
+                    "an inner attribute is not permitted following an outer attribute"
+                } else {
+                    DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
+                };
+                let inner_parse_policy = InnerAttrPolicy::Forbidden {
+                    reason: inner_error_reason,
+                    saw_doc_comment: just_parsed_doc_comment,
+                    prev_attr_sp: attrs.last().map(|a| a.span),
+                };
+                let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
+                attrs.push(attr);
+                just_parsed_doc_comment = false;
+            } else if let token::DocComment(s) = self.token.kind {
+                let attr = self.mk_doc_comment(s);
+                if attr.style != ast::AttrStyle::Outer {
+                    self.struct_span_err(self.token.span, "expected outer doc comment")
+                        .note(
                             "inner doc comments like this (starting with \
-                                  `//!` or `/*!`) can only appear before items",
-                        );
-                        return Err(err);
-                    }
-                    attrs.push(attr);
-                    self.bump();
-                    just_parsed_doc_comment = true;
+                              `//!` or `/*!`) can only appear before items",
+                        )
+                        .emit();
                 }
-                _ => break,
+                attrs.push(attr);
+                self.bump();
+                just_parsed_doc_comment = true;
+            } else {
+                break;
             }
         }
         Ok(attrs)
     }
 
     fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
-        let style = comments::doc_comment_style(&s.as_str());
-        attr::mk_doc_comment(style, s, self.token.span)
+        attr::mk_doc_comment(comments::doc_comment_style(&s.as_str()), s, self.token.span)
     }
 
     /// Matches `attribute = # ! [ meta_item ]`.
@@ -75,96 +77,67 @@ impl<'a> Parser<'a> {
     /// attribute.
     pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
         debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token);
-        let inner_parse_policy = if permit_inner {
-            InnerAttributeParsePolicy::Permitted
-        } else {
-            InnerAttributeParsePolicy::NotPermitted {
-                reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
-                saw_doc_comment: false,
-                prev_attr_sp: None,
-            }
-        };
+        let inner_parse_policy =
+            if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN };
         self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
     }
 
-    /// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
+    /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy`
     /// that prescribes how to handle inner attributes.
     fn parse_attribute_with_inner_parse_policy(
         &mut self,
-        inner_parse_policy: InnerAttributeParsePolicy<'_>,
+        inner_parse_policy: InnerAttrPolicy<'_>,
     ) -> PResult<'a, ast::Attribute> {
         debug!(
             "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
             inner_parse_policy, self.token
         );
-        let (span, item, style) = match self.token.kind {
-            token::Pound => {
-                let lo = self.token.span;
-                self.bump();
-
-                if let InnerAttributeParsePolicy::Permitted = inner_parse_policy {
-                    self.expected_tokens.push(TokenType::Token(token::Not));
-                }
-
-                let style = if self.token == token::Not {
-                    self.bump();
-                    ast::AttrStyle::Inner
-                } else {
-                    ast::AttrStyle::Outer
-                };
+        let lo = self.token.span;
+        let (span, item, style) = if self.eat(&token::Pound) {
+            let style =
+                if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
 
-                self.expect(&token::OpenDelim(token::Bracket))?;
-                let item = self.parse_attr_item()?;
-                self.expect(&token::CloseDelim(token::Bracket))?;
-                let hi = self.prev_token.span;
-
-                let attr_sp = lo.to(hi);
-
-                // Emit error if inner attribute is encountered and not permitted
-                if style == ast::AttrStyle::Inner {
-                    if let InnerAttributeParsePolicy::NotPermitted {
-                        reason,
-                        saw_doc_comment,
-                        prev_attr_sp,
-                    } = inner_parse_policy
-                    {
-                        let prev_attr_note = if saw_doc_comment {
-                            "previous doc comment"
-                        } else {
-                            "previous outer attribute"
-                        };
-
-                        let mut diagnostic = self.struct_span_err(attr_sp, reason);
-
-                        if let Some(prev_attr_sp) = prev_attr_sp {
-                            diagnostic
-                                .span_label(attr_sp, "not permitted following an outer attribute")
-                                .span_label(prev_attr_sp, prev_attr_note);
-                        }
-
-                        diagnostic
-                            .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.",
-                            )
-                            .emit();
-                    }
-                }
+            self.expect(&token::OpenDelim(token::Bracket))?;
+            let item = self.parse_attr_item()?;
+            self.expect(&token::CloseDelim(token::Bracket))?;
+            let attr_sp = lo.to(self.prev_token.span);
 
-                (attr_sp, item, style)
-            }
-            _ => {
-                let token_str = pprust::token_to_string(&self.token);
-                let msg = &format!("expected `#`, found `{}`", token_str);
-                return Err(self.struct_span_err(self.token.span, msg));
+            // Emit error if inner attribute is encountered and forbidden.
+            if style == ast::AttrStyle::Inner {
+                self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
             }
+
+            (attr_sp, item, style)
+        } else {
+            let token_str = pprust::token_to_string(&self.token);
+            let msg = &format!("expected `#`, found `{}`", token_str);
+            return Err(self.struct_span_err(self.token.span, msg));
         };
 
         Ok(attr::mk_attr_from_item(style, item, span))
     }
 
+    pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
+        if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
+            let prev_attr_note =
+                if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
+
+            let mut diag = self.struct_span_err(attr_sp, reason);
+
+            if let Some(prev_attr_sp) = prev_attr_sp {
+                diag.span_label(attr_sp, "not permitted following an outer attribute")
+                    .span_label(prev_attr_sp, prev_attr_note);
+            }
+
+            diag.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.",
+            )
+            .emit();
+        }
+    }
+
     /// Parses an inner part of an attribute (the path and following tokens).
     /// The tokens must be either a delimited token stream, or empty token stream,
     /// or the "legacy" key-value form.
@@ -200,28 +173,22 @@ impl<'a> Parser<'a> {
     crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
-            match self.token.kind {
-                token::Pound => {
-                    // Don't even try to parse if it's not an inner attribute.
-                    if !self.look_ahead(1, |t| t == &token::Not) {
-                        break;
-                    }
-
-                    let attr = self.parse_attribute(true)?;
-                    assert_eq!(attr.style, ast::AttrStyle::Inner);
+            // Only try to parse if it is an inner attribute (has `!`).
+            if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
+                let attr = self.parse_attribute(true)?;
+                assert_eq!(attr.style, ast::AttrStyle::Inner);
+                attrs.push(attr);
+            } else if let token::DocComment(s) = self.token.kind {
+                // We need to get the position of this token before we bump.
+                let attr = self.mk_doc_comment(s);
+                if attr.style == ast::AttrStyle::Inner {
                     attrs.push(attr);
+                    self.bump();
+                } else {
+                    break;
                 }
-                token::DocComment(s) => {
-                    // We need to get the position of this token before we bump.
-                    let attr = self.mk_doc_comment(s);
-                    if attr.style == ast::AttrStyle::Inner {
-                        attrs.push(attr);
-                        self.bump();
-                    } else {
-                        break;
-                    }
-                }
-                _ => break,
+            } else {
+                break;
             }
         }
         Ok(attrs)
@@ -232,12 +199,10 @@ impl<'a> Parser<'a> {
         debug!("checking if {:?} is unusuffixed", lit);
 
         if !lit.kind.is_unsuffixed() {
-            let msg = "suffixed literals are not allowed in attributes";
-            self.struct_span_err(lit.span, msg)
+            self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
                 .help(
-                    "instead of using a suffixed literal \
-                                    (`1u8`, `1.0f32`, etc.), use an unsuffixed version \
-                                    (`1`, `1.0`, etc.)",
+                    "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
+                    use an unsuffixed version (`1`, `1.0`, etc.)",
                 )
                 .emit();
         }
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index 16ea2773b20..7f6f90431fc 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -673,10 +673,28 @@ impl<'a> Parser<'a> {
     /// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.
     fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
         self.expect_and()?;
+        let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);
+        let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.
         let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo);
         let expr = self.parse_prefix_expr(None);
-        let (span, expr) = self.interpolated_or_expr_span(expr)?;
-        Ok((lo.to(span), ExprKind::AddrOf(borrow_kind, mutbl, expr)))
+        let (hi, expr) = self.interpolated_or_expr_span(expr)?;
+        let span = lo.to(hi);
+        if let Some(lt) = lifetime {
+            self.error_remove_borrow_lifetime(span, lt.ident.span);
+        }
+        Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))
+    }
+
+    fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
+        self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes")
+            .span_label(lt_span, "annotated with lifetime here")
+            .span_suggestion(
+                lt_span,
+                "remove the lifetime annotation",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
     }
 
     /// Parse `mut?` or `raw [ const | mut ]`.
@@ -901,7 +919,7 @@ impl<'a> Parser<'a> {
         } else if self.eat_lt() {
             let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
             Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs))
-        } else if self.token.is_path_start() {
+        } else if self.check_path() {
             self.parse_path_start_expr(attrs)
         } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
             self.parse_closure_expr(attrs)
@@ -1067,26 +1085,44 @@ impl<'a> Parser<'a> {
         self.maybe_recover_from_bad_qpath(expr, true)
     }
 
+    /// Parse `'label: $expr`. The label is already parsed.
     fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = label.ident.span;
-        self.expect(&token::Colon)?;
-        if self.eat_keyword(kw::While) {
-            return self.parse_while_expr(Some(label), lo, attrs);
-        }
-        if self.eat_keyword(kw::For) {
-            return self.parse_for_expr(Some(label), lo, attrs);
-        }
-        if self.eat_keyword(kw::Loop) {
-            return self.parse_loop_expr(Some(label), lo, attrs);
-        }
-        if self.token == token::OpenDelim(token::Brace) {
-            return self.parse_block_expr(Some(label), lo, BlockCheckMode::Default, attrs);
+        let label = Some(label);
+        let ate_colon = self.eat(&token::Colon);
+        let expr = if self.eat_keyword(kw::While) {
+            self.parse_while_expr(label, lo, attrs)
+        } else if self.eat_keyword(kw::For) {
+            self.parse_for_expr(label, lo, attrs)
+        } else if self.eat_keyword(kw::Loop) {
+            self.parse_loop_expr(label, lo, attrs)
+        } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+            self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
+        } else {
+            let msg = "expected `while`, `for`, `loop` or `{` after a label";
+            self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+            // Continue as an expression in an effort to recover on `'label: non_block_expr`.
+            self.parse_expr()
+        }?;
+
+        if !ate_colon {
+            self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
         }
 
-        let msg = "expected `while`, `for`, `loop` or `{` after a label";
-        self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
-        // Continue as an expression in an effort to recover on `'label: non_block_expr`.
-        self.parse_expr()
+        Ok(expr)
+    }
+
+    fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) {
+        self.struct_span_err(span, "labeled expression must be followed by `:`")
+            .span_label(lo, "the label")
+            .span_suggestion_short(
+                lo.shrink_to_hi(),
+                "add `:` after the label",
+                ": ".to_string(),
+                Applicability::MachineApplicable,
+            )
+            .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")
+            .emit();
     }
 
     /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
@@ -1363,18 +1399,20 @@ impl<'a> Parser<'a> {
         opt_label: Option<Label>,
         lo: Span,
         blk_mode: BlockCheckMode,
-        outer_attrs: AttrVec,
+        mut attrs: AttrVec,
     ) -> PResult<'a, P<Expr>> {
         if let Some(label) = opt_label {
             self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
         }
 
-        self.expect(&token::OpenDelim(token::Brace))?;
-
-        let mut attrs = outer_attrs;
-        attrs.extend(self.parse_inner_attributes()?);
+        if self.token.is_whole_block() {
+            self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
+                .span_label(lo.to(self.token.span), "the `block` fragment is within this context")
+                .emit();
+        }
 
-        let blk = self.parse_block_tail(lo, blk_mode)?;
+        let (inner_attrs, blk) = self.parse_block_common(lo, blk_mode)?;
+        attrs.extend(inner_attrs);
         Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs))
     }
 
@@ -1476,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))
@@ -1524,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 a9c4de04c0a..3932bbd7564 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -218,7 +218,7 @@ impl<'a> Parser<'a> {
         } else if vis.node.is_pub() && self.isnt_macro_invocation() {
             self.recover_missing_kw_before_item()?;
             return Ok(None);
-        } else if macros_allowed && self.token.is_path_start() {
+        } else if macros_allowed && self.check_path() {
             // MACRO INVOCATION ITEM
             (Ident::invalid(), ItemKind::Mac(self.parse_item_macro(vis)?))
         } else {
@@ -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"
@@ -1411,23 +1410,28 @@ impl<'a> Parser<'a> {
     /// This can either be `;` when there's no body,
     /// or e.g. a block when the function is a provided one.
     fn parse_fn_body(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, Option<P<Block>>> {
-        let (inner_attrs, body) = match self.token.kind {
-            token::Semi => {
-                self.bump();
-                (Vec::new(), None)
-            }
-            token::OpenDelim(token::Brace) => {
-                let (attrs, body) = self.parse_inner_attrs_and_block()?;
-                (attrs, Some(body))
-            }
-            token::Interpolated(ref nt) => match **nt {
-                token::NtBlock(..) => {
-                    let (attrs, body) = self.parse_inner_attrs_and_block()?;
-                    (attrs, Some(body))
-                }
-                _ => return self.expected_semi_or_open_brace(),
-            },
-            _ => return self.expected_semi_or_open_brace(),
+        let (inner_attrs, body) = if self.check(&token::Semi) {
+            self.bump(); // `;`
+            (Vec::new(), None)
+        } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+            self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
+        } else if self.token.kind == token::Eq {
+            // Recover `fn foo() = $expr;`.
+            self.bump(); // `=`
+            let eq_sp = self.prev_token.span;
+            let _ = self.parse_expr()?;
+            self.expect_semi()?; // `;`
+            let span = eq_sp.to(self.prev_token.span);
+            self.struct_span_err(span, "function body cannot be `= expression;`")
+                .multipart_suggestion(
+                    "surround the expression with `{` and `}` instead of `=` and `;`",
+                    vec![(eq_sp, "{".to_string()), (self.prev_token.span, " }".to_string())],
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+            (Vec::new(), Some(self.mk_block_err(span)))
+        } else {
+            return self.expected_semi_or_open_brace();
         };
         attrs.extend(inner_attrs);
         Ok(body)
diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs
index f52a91ff598..5f2b3b03488 100644
--- a/src/librustc_parse/parser/pat.rs
+++ b/src/librustc_parse/parser/pat.rs
@@ -704,7 +704,7 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
-        if self.token.is_path_start() {
+        if self.check_path() {
             let lo = self.token.span;
             let (qself, path) = if self.eat_lt() {
                 // Parse a qualified path
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index 3864ec3aaa1..489549a5750 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -1,3 +1,4 @@
+use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
 use super::diagnostics::Error;
 use super::expr::LhsExpr;
 use super::pat::GateOr;
@@ -47,10 +48,7 @@ impl<'a> Parser<'a> {
             self.bump(); // `var`
             let msg = "write `let` instead of `var` to introduce a new variable";
             self.recover_stmt_local(lo, attrs.into(), msg, "let")?
-        } else if self.token.is_path_start()
-            && !self.token.is_qpath_start()
-            && !self.is_path_start_item()
-        {
+        } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
             // We have avoided contextual keywords like `union`, items with `crate` visibility,
             // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
             // that starts like a path (1 token), but it fact not a path.
@@ -238,15 +236,11 @@ impl<'a> Parser<'a> {
 
     /// Parses a block. No inner attributes are allowed.
     pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
-        maybe_whole!(self, NtBlock, |x| x);
-
-        let lo = self.token.span;
-
-        if !self.eat(&token::OpenDelim(token::Brace)) {
-            return self.error_block_no_opening_brace();
+        let (attrs, block) = self.parse_inner_attrs_and_block()?;
+        if let [.., last] = &*attrs {
+            self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN);
         }
-
-        self.parse_block_tail(lo, BlockCheckMode::Default)
+        Ok(block)
     }
 
     fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
@@ -262,16 +256,14 @@ impl<'a> Parser<'a> {
         //
         // which is valid in other languages, but not Rust.
         match self.parse_stmt_without_recovery() {
-            Ok(Some(stmt)) => {
+            // If the next token is an open brace (e.g., `if a b {`), the place-
+            // inside-a-block suggestion would be more likely wrong than right.
+            Ok(Some(_))
                 if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
-                    || do_not_suggest_help
-                {
-                    // If the next token is an open brace (e.g., `if a b {`), the place-
-                    // inside-a-block suggestion would be more likely wrong than right.
-                    e.span_label(sp, "expected `{`");
-                    return Err(e);
-                }
-                let stmt_span = if self.eat(&token::Semi) {
+                    || do_not_suggest_help => {}
+            Ok(Some(stmt)) => {
+                let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp);
+                let stmt_span = if stmt_own_line && self.eat(&token::Semi) {
                     // Expand the span to include the semicolon.
                     stmt.span.with_hi(self.prev_token.span.hi())
                 } else {
@@ -301,20 +293,27 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_inner_attrs_and_block(
         &mut self,
     ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
+        self.parse_block_common(self.token.span, BlockCheckMode::Default)
+    }
+
+    /// Parses a block. Inner attributes are allowed.
+    pub(super) fn parse_block_common(
+        &mut self,
+        lo: Span,
+        blk_mode: BlockCheckMode,
+    ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
         maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
 
-        let lo = self.token.span;
-        self.expect(&token::OpenDelim(token::Brace))?;
-        Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, BlockCheckMode::Default)?))
+        if !self.eat(&token::OpenDelim(token::Brace)) {
+            return self.error_block_no_opening_brace();
+        }
+
+        Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, blk_mode)?))
     }
 
     /// Parses the rest of a block expression or function body.
     /// Precondition: already parsed the '{'.
-    pub(super) fn parse_block_tail(
-        &mut self,
-        lo: Span,
-        s: BlockCheckMode,
-    ) -> PResult<'a, P<Block>> {
+    fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P<Block>> {
         let mut stmts = vec![];
         while !self.eat(&token::CloseDelim(token::Brace)) {
             if self.token == token::Eof {
diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs
index 16adf5c05a4..3dd415bf372 100644
--- a/src/librustc_parse/parser/ty.rs
+++ b/src/librustc_parse/parser/ty.rs
@@ -142,24 +142,20 @@ impl<'a> Parser<'a> {
             } else {
                 let path = self.parse_path(PathStyle::Type)?;
                 let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
-                self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
+                self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?
             }
         } else if self.eat_keyword(kw::Impl) {
             self.parse_impl_ty(&mut impl_dyn_multi)?
         } else if self.is_explicit_dyn_type() {
             self.parse_dyn_ty(&mut impl_dyn_multi)?
-        } else if self.check(&token::Question)
-            || self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus())
-        {
-            // Bound list (trait object type)
-            let bounds = self.parse_generic_bounds_common(allow_plus, None)?;
-            TyKind::TraitObject(bounds, TraitObjectSyntax::None)
         } else if self.eat_lt() {
             // Qualified path
             let (qself, path) = self.parse_qpath(PathStyle::Type)?;
             TyKind::Path(Some(qself), path)
-        } else if self.token.is_path_start() {
+        } else if self.check_path() {
             self.parse_path_start_ty(lo, allow_plus)?
+        } else if self.can_begin_bound() {
+            self.parse_bare_trait_object(lo, allow_plus)?
         } else if self.eat(&token::DotDotDot) {
             if allow_c_variadic == AllowCVariadic::Yes {
                 TyKind::CVarArgs
@@ -203,21 +199,12 @@ impl<'a> Parser<'a> {
             match ty.kind {
                 // `(TY_BOUND_NOPAREN) + BOUND + ...`.
                 TyKind::Path(None, path) if maybe_bounds => {
-                    self.parse_remaining_bounds(Vec::new(), path, lo, true)
+                    self.parse_remaining_bounds_path(Vec::new(), path, lo, true)
                 }
-                TyKind::TraitObject(mut bounds, TraitObjectSyntax::None)
+                TyKind::TraitObject(bounds, TraitObjectSyntax::None)
                     if maybe_bounds && bounds.len() == 1 && !trailing_plus =>
                 {
-                    let path = match bounds.remove(0) {
-                        GenericBound::Trait(pt, ..) => pt.trait_ref.path,
-                        GenericBound::Outlives(..) => {
-                            return Err(self.struct_span_err(
-                                ty.span,
-                                "expected trait bound, not lifetime bound",
-                            ));
-                        }
-                    };
-                    self.parse_remaining_bounds(Vec::new(), path, lo, true)
+                    self.parse_remaining_bounds(bounds, true)
                 }
                 // `(TYPE)`
                 _ => Ok(TyKind::Paren(P(ty))),
@@ -227,18 +214,35 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_remaining_bounds(
+    fn parse_bare_trait_object(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> {
+        let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus());
+        let bounds = self.parse_generic_bounds_common(allow_plus, None)?;
+        if lt_no_plus {
+            self.struct_span_err(lo, "lifetime in trait object type must be followed by `+`").emit()
+        }
+        Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
+    }
+
+    fn parse_remaining_bounds_path(
         &mut self,
         generic_params: Vec<GenericParam>,
         path: ast::Path,
         lo: Span,
         parse_plus: bool,
     ) -> PResult<'a, TyKind> {
-        assert_ne!(self.token, token::Question);
-
         let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_token.span));
-        let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
-        if parse_plus {
+        let bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
+        self.parse_remaining_bounds(bounds, parse_plus)
+    }
+
+    /// Parse the remainder of a bare trait object type given an already parsed list.
+    fn parse_remaining_bounds(
+        &mut self,
+        mut bounds: GenericBounds,
+        plus: bool,
+    ) -> PResult<'a, TyKind> {
+        assert_ne!(self.token, token::Question);
+        if plus {
             self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
             bounds.append(&mut self.parse_generic_bounds(Some(self.prev_token.span))?);
         }
@@ -358,7 +362,7 @@ impl<'a> Parser<'a> {
             }))
         } else if allow_plus == AllowPlus::Yes && self.check_plus() {
             // `Trait1 + Trait2 + 'a`
-            self.parse_remaining_bounds(Vec::new(), path, lo, true)
+            self.parse_remaining_bounds_path(Vec::new(), path, lo, true)
         } else {
             // Just a type path.
             Ok(TyKind::Path(None, path))