diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index 377d43dec21a2..4b7f1e9a4d833 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -2,6 +2,7 @@ use super::{Parser, PathStyle, TokenType};
 use rustc_errors::PResult;
 use syntax::ast;
 use syntax::attr;
+use syntax::print::pprust;
 use syntax::token::{self, Nonterminal};
 use syntax::util::comments;
 use syntax_pos::{Span, Symbol};
@@ -154,7 +155,7 @@ impl<'a> Parser<'a> {
                 (attr_sp, item, style)
             }
             _ => {
-                let token_str = self.this_token_to_string();
+                let token_str = pprust::token_to_string(&self.token);
                 return Err(self.fatal(&format!("expected `#`, found `{}`", token_str)));
             }
         };
@@ -329,7 +330,7 @@ impl<'a> Parser<'a> {
             Err(ref mut err) => err.cancel(),
         }
 
-        let found = self.this_token_to_string();
+        let found = pprust::token_to_string(&self.token);
         let msg = format!("expected unsuffixed literal or identifier, found `{}`", found);
         Err(self.diagnostic().struct_span_err(self.token.span, &msg))
     }
diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs
index f58b9a4c14441..578f816be58c8 100644
--- a/src/librustc_parse/parser/diagnostics.rs
+++ b/src/librustc_parse/parser/diagnostics.rs
@@ -200,7 +200,7 @@ impl<'a> Parser<'a> {
     pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
         let mut err = self.struct_span_err(
             self.token.span,
-            &format!("expected identifier, found {}", self.this_token_descr()),
+            &format!("expected identifier, found {}", super::token_descr(&self.token)),
         );
         let valid_follow = &[
             TokenKind::Eq,
@@ -225,7 +225,7 @@ impl<'a> Parser<'a> {
                 );
             }
         }
-        if let Some(token_descr) = self.token_descr() {
+        if let Some(token_descr) = super::token_descr_opt(&self.token) {
             err.span_label(self.token.span, format!("expected identifier, found {}", token_descr));
         } else {
             err.span_label(self.token.span, "expected identifier");
@@ -272,7 +272,7 @@ impl<'a> Parser<'a> {
         expected.sort_by_cached_key(|x| x.to_string());
         expected.dedup();
         let expect = tokens_to_string(&expected[..]);
-        let actual = self.this_token_descr();
+        let actual = super::token_descr(&self.token);
         let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
             let short_expect = if expected.len() > 6 {
                 format!("{} possible tokens", expected.len())
@@ -815,7 +815,7 @@ impl<'a> Parser<'a> {
         t: &TokenKind,
     ) -> PResult<'a, bool /* recovered */> {
         let token_str = pprust::token_kind_to_string(t);
-        let this_token_str = self.this_token_descr();
+        let this_token_str = super::token_descr(&self.token);
         let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {
             // Point at the end of the macro call when reaching end of macro arguments.
             (token::Eof, Some(_)) => {
@@ -862,7 +862,7 @@ impl<'a> Parser<'a> {
             return Ok(());
         }
         let sm = self.sess.source_map();
-        let msg = format!("expected `;`, found `{}`", self.this_token_descr());
+        let msg = format!("expected `;`, found `{}`", super::token_descr(&self.token));
         let appl = Applicability::MachineApplicable;
         if self.token.span == DUMMY_SP || self.prev_span == DUMMY_SP {
             // Likely inside a macro, can't provide meaninful suggestions.
@@ -1270,7 +1270,7 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn expected_semi_or_open_brace<T>(&mut self) -> PResult<'a, T> {
-        let token_str = self.this_token_descr();
+        let token_str = super::token_descr(&self.token);
         let mut err = self.fatal(&format!("expected `;` or `{{`, found {}", token_str));
         err.span_label(self.token.span, "expected `;` or `{`");
         Err(err)
@@ -1447,7 +1447,7 @@ impl<'a> Parser<'a> {
             }
             _ => (
                 self.token.span,
-                format!("expected expression, found {}", self.this_token_descr(),),
+                format!("expected expression, found {}", super::token_descr(&self.token),),
             ),
         };
         let mut err = self.struct_span_err(span, &msg);
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index e0eb841f2c0cf..b51a4465b159e 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -210,30 +210,12 @@ impl<'a> Parser<'a> {
                 lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
                 continue;
             } else if op == AssocOp::Colon {
-                let maybe_path = self.could_ascription_be_path(&lhs.kind);
-                self.last_type_ascription = Some((self.prev_span, maybe_path));
-
-                lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
-                self.sess.gated_spans.gate(sym::type_ascription, lhs.span);
+                lhs = self.parse_assoc_op_ascribe(lhs, lhs_span)?;
                 continue;
             } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
                 // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to
                 // generalise it to the Fixity::None code.
-                //
-                // We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other
-                // two variants are handled with `parse_prefix_range_expr` call above.
-                let rhs = if self.is_at_start_of_range_notation_rhs() {
-                    Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?)
-                } else {
-                    None
-                };
-                let (lhs_span, rhs_span) =
-                    (lhs.span, if let Some(ref x) = rhs { x.span } else { cur_op_span });
-                let limits =
-                    if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
-
-                let r = self.mk_range(Some(lhs), rhs, limits)?;
-                lhs = self.mk_expr(lhs_span.to(rhs_span), r, AttrVec::new());
+                lhs = self.parse_range_expr(prec, lhs, op, cur_op_span)?;
                 break;
             }
 
@@ -395,6 +377,27 @@ impl<'a> Parser<'a> {
             && !classify::expr_requires_semi_to_be_stmt(e)
     }
 
+    /// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
+    /// The other two variants are handled in `parse_prefix_range_expr` below.
+    fn parse_range_expr(
+        &mut self,
+        prec: usize,
+        lhs: P<Expr>,
+        op: AssocOp,
+        cur_op_span: Span,
+    ) -> PResult<'a, P<Expr>> {
+        let rhs = if self.is_at_start_of_range_notation_rhs() {
+            Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?)
+        } else {
+            None
+        };
+        let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);
+        let span = lhs.span.to(rhs_span);
+        let limits =
+            if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
+        Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new()))
+    }
+
     fn is_at_start_of_range_notation_rhs(&self) -> bool {
         if self.token.can_begin_expr() {
             // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
@@ -408,10 +411,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`.
-    fn parse_prefix_range_expr(
-        &mut self,
-        already_parsed_attrs: Option<AttrVec>,
-    ) -> PResult<'a, P<Expr>> {
+    fn parse_prefix_range_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
         // Check for deprecated `...` syntax.
         if self.token == token::DotDotDot {
             self.err_dotdotdot_syntax(self.token.span);
@@ -422,118 +422,107 @@ impl<'a> Parser<'a> {
             "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq",
             self.token
         );
-        let tok = self.token.clone();
-        let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
+
+        let limits = match self.token.kind {
+            token::DotDot => RangeLimits::HalfOpen,
+            _ => RangeLimits::Closed,
+        };
+        let op = AssocOp::from_token(&self.token);
+        let attrs = self.parse_or_use_outer_attributes(attrs)?;
         let lo = self.token.span;
-        let mut hi = self.token.span;
         self.bump();
-        let opt_end = if self.is_at_start_of_range_notation_rhs() {
+        let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() {
             // RHS must be parsed with more associativity than the dots.
-            let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1;
-            Some(self.parse_assoc_expr_with(next_prec, LhsExpr::NotYetParsed).map(|x| {
-                hi = x.span;
-                x
-            })?)
+            self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed)
+                .map(|x| (lo.to(x.span), Some(x)))?
         } else {
-            None
+            (lo, None)
         };
-        let limits = if tok == token::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
-
-        let r = self.mk_range(None, opt_end, limits)?;
-        Ok(self.mk_expr(lo.to(hi), r, attrs))
+        Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs))
     }
 
     /// Parses a prefix-unary-operator expr.
-    fn parse_prefix_expr(&mut self, already_parsed_attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
-        let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
+    fn parse_prefix_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
+        let attrs = self.parse_or_use_outer_attributes(attrs)?;
         let lo = self.token.span;
         // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()
         let (hi, ex) = match self.token.kind {
-            token::Not => {
-                self.bump();
-                let e = self.parse_prefix_expr(None);
-                let (span, e) = self.interpolated_or_expr_span(e)?;
-                (lo.to(span), self.mk_unary(UnOp::Not, e))
-            }
-            // Suggest `!` for bitwise negation when encountering a `~`
-            token::Tilde => {
-                self.bump();
-                let e = self.parse_prefix_expr(None);
-                let (span, e) = self.interpolated_or_expr_span(e)?;
-                let span_of_tilde = lo;
-                self.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator")
-                    .span_suggestion_short(
-                        span_of_tilde,
-                        "use `!` to perform bitwise not",
-                        "!".to_owned(),
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
-                (lo.to(span), self.mk_unary(UnOp::Not, e))
-            }
-            token::BinOp(token::Minus) => {
-                self.bump();
-                let e = self.parse_prefix_expr(None);
-                let (span, e) = self.interpolated_or_expr_span(e)?;
-                (lo.to(span), self.mk_unary(UnOp::Neg, e))
-            }
-            token::BinOp(token::Star) => {
-                self.bump();
-                let e = self.parse_prefix_expr(None);
-                let (span, e) = self.interpolated_or_expr_span(e)?;
-                (lo.to(span), self.mk_unary(UnOp::Deref, e))
-            }
-            token::BinOp(token::And) | token::AndAnd => self.parse_address_of(lo)?,
-            token::Ident(..) if self.token.is_keyword(kw::Box) => {
-                self.bump();
-                let e = self.parse_prefix_expr(None);
-                let (span, e) = self.interpolated_or_expr_span(e)?;
-                let span = lo.to(span);
-                self.sess.gated_spans.gate(sym::box_syntax, span);
-                (span, ExprKind::Box(e))
-            }
-            token::Ident(..) if self.token.is_ident_named(sym::not) => {
-                // `not` is just an ordinary identifier in Rust-the-language,
-                // but as `rustc`-the-compiler, we can issue clever diagnostics
-                // for confused users who really want to say `!`
-                let token_cannot_continue_expr = |t: &Token| match t.kind {
-                    // These tokens can start an expression after `!`, but
-                    // can't continue an expression after an ident
-                    token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),
-                    token::Literal(..) | token::Pound => true,
-                    _ => t.is_whole_expr(),
-                };
-                let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr);
-                if cannot_continue_expr {
-                    self.bump();
-                    // Emit the error ...
-                    self.struct_span_err(
-                        self.token.span,
-                        &format!("unexpected {} after identifier", self.this_token_descr()),
-                    )
-                    .span_suggestion_short(
-                        // Span the `not` plus trailing whitespace to avoid
-                        // trailing whitespace after the `!` in our suggestion
-                        self.sess.source_map().span_until_non_whitespace(lo.to(self.token.span)),
-                        "use `!` to perform logical negation",
-                        "!".to_owned(),
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
-                    // —and recover! (just as if we were in the block
-                    // for the `token::Not` arm)
-                    let e = self.parse_prefix_expr(None);
-                    let (span, e) = self.interpolated_or_expr_span(e)?;
-                    (lo.to(span), self.mk_unary(UnOp::Not, e))
-                } else {
-                    return self.parse_dot_or_call_expr(Some(attrs));
-                }
-            }
-            _ => {
-                return self.parse_dot_or_call_expr(Some(attrs));
-            }
+            token::Not => self.parse_unary_expr(lo, UnOp::Not), // `!expr`
+            token::Tilde => self.recover_tilde_expr(lo),        // `~expr`
+            token::BinOp(token::Minus) => self.parse_unary_expr(lo, UnOp::Neg), // `-expr`
+            token::BinOp(token::Star) => self.parse_unary_expr(lo, UnOp::Deref), // `*expr`
+            token::BinOp(token::And) | token::AndAnd => self.parse_borrow_expr(lo),
+            token::Ident(..) if self.token.is_keyword(kw::Box) => self.parse_box_expr(lo),
+            token::Ident(..) if self.is_mistaken_not_ident_negation() => self.recover_not_expr(lo),
+            _ => return self.parse_dot_or_call_expr(Some(attrs)),
+        }?;
+        Ok(self.mk_expr(lo.to(hi), ex, attrs))
+    }
+
+    fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P<Expr>)> {
+        self.bump();
+        let expr = self.parse_prefix_expr(None);
+        let (span, expr) = self.interpolated_or_expr_span(expr)?;
+        Ok((lo.to(span), expr))
+    }
+
+    fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> {
+        let (span, expr) = self.parse_prefix_expr_common(lo)?;
+        Ok((span, self.mk_unary(op, expr)))
+    }
+
+    // Recover on `!` suggesting for bitwise negation instead.
+    fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
+        self.struct_span_err(lo, "`~` cannot be used as a unary operator")
+            .span_suggestion_short(
+                lo,
+                "use `!` to perform bitwise not",
+                "!".to_owned(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
+
+        self.parse_unary_expr(lo, UnOp::Not)
+    }
+
+    /// Parse `box expr`.
+    fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
+        let (span, expr) = self.parse_prefix_expr_common(lo)?;
+        self.sess.gated_spans.gate(sym::box_syntax, span);
+        Ok((span, ExprKind::Box(expr)))
+    }
+
+    fn is_mistaken_not_ident_negation(&self) -> bool {
+        let token_cannot_continue_expr = |t: &Token| match t.kind {
+            // These tokens can start an expression after `!`, but
+            // can't continue an expression after an ident
+            token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),
+            token::Literal(..) | token::Pound => true,
+            _ => t.is_whole_expr(),
         };
-        return Ok(self.mk_expr(lo.to(hi), ex, attrs));
+        self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr)
+    }
+
+    /// Recover on `not expr` in favor of `!expr`.
+    fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
+        // Emit the error...
+        let not_token = self.look_ahead(1, |t| t.clone());
+        self.struct_span_err(
+            not_token.span,
+            &format!("unexpected {} after identifier", super::token_descr(&not_token)),
+        )
+        .span_suggestion_short(
+            // Span the `not` plus trailing whitespace to avoid
+            // trailing whitespace after the `!` in our suggestion
+            self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)),
+            "use `!` to perform logical negation",
+            "!".to_owned(),
+            Applicability::MachineApplicable,
+        )
+        .emit();
+
+        // ...and recover!
+        self.parse_unary_expr(lo, UnOp::Not)
     }
 
     /// Returns the span of expr, if it was not interpolated or the span of the interpolated token.
@@ -598,14 +587,7 @@ impl<'a> Parser<'a> {
                             op_noun,
                         );
                         let span_after_type = parser_snapshot_after_type.token.span;
-                        let expr = mk_expr(
-                            self,
-                            P(Ty {
-                                span: path.span,
-                                kind: TyKind::Path(None, path),
-                                id: DUMMY_NODE_ID,
-                            }),
-                        );
+                        let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path)));
 
                         let expr_str = self
                             .span_to_snippet(expr.span)
@@ -638,33 +620,44 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn parse_assoc_op_ascribe(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
+        let maybe_path = self.could_ascription_be_path(&lhs.kind);
+        self.last_type_ascription = Some((self.prev_span, maybe_path));
+        let lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
+        self.sess.gated_spans.gate(sym::type_ascription, lhs.span);
+        Ok(lhs)
+    }
+
     /// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.
-    fn parse_address_of(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
+    fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
         self.expect_and()?;
-        let (k, m) = if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) {
+        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)))
+    }
+
+    /// Parse `mut?` or `raw [ const | mut ]`.
+    fn parse_borrow_modifiers(&mut self, lo: Span) -> (ast::BorrowKind, ast::Mutability) {
+        if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) {
+            // `raw [ const | mut ]`.
             let found_raw = self.eat_keyword(kw::Raw);
             assert!(found_raw);
             let mutability = self.parse_const_or_mut().unwrap();
             self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_span));
             (ast::BorrowKind::Raw, mutability)
         } else {
+            // `mut?`
             (ast::BorrowKind::Ref, self.parse_mutability())
-        };
-        let e = self.parse_prefix_expr(None);
-        let (span, e) = self.interpolated_or_expr_span(e)?;
-        Ok((lo.to(span), ExprKind::AddrOf(k, m, e)))
+        }
     }
 
     /// Parses `a.b` or `a(13)` or `a[4]` or just `a`.
-    fn parse_dot_or_call_expr(
-        &mut self,
-        already_parsed_attrs: Option<AttrVec>,
-    ) -> PResult<'a, P<Expr>> {
-        let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
-
-        let b = self.parse_bottom_expr();
-        let (span, b) = self.interpolated_or_expr_span(b)?;
-        self.parse_dot_or_call_expr_with(b, span, attrs)
+    fn parse_dot_or_call_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
+        let attrs = self.parse_or_use_outer_attributes(attrs)?;
+        let base = self.parse_bottom_expr();
+        let (span, base) = self.interpolated_or_expr_span(base)?;
+        self.parse_dot_or_call_expr_with(base, span, attrs)
     }
 
     pub(super) fn parse_dot_or_call_expr_with(
@@ -694,95 +687,120 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
-        let mut e = e0;
-        let mut hi;
+    fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
         loop {
-            // expr?
-            while self.eat(&token::Question) {
-                let hi = self.prev_span;
-                e = self.mk_expr(lo.to(hi), ExprKind::Try(e), AttrVec::new());
+            if self.eat(&token::Question) {
+                // `expr?`
+                e = self.mk_expr(lo.to(self.prev_span), ExprKind::Try(e), AttrVec::new());
+                continue;
             }
-
-            // expr.f
             if self.eat(&token::Dot) {
-                match self.token.kind {
-                    token::Ident(..) => {
-                        e = self.parse_dot_suffix(e, lo)?;
-                    }
-                    token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
-                        let span = self.token.span;
-                        self.bump();
-                        let field = ExprKind::Field(e, Ident::new(symbol, span));
-                        e = self.mk_expr(lo.to(span), field, AttrVec::new());
-
-                        self.expect_no_suffix(span, "a tuple index", suffix);
-                    }
-                    token::Literal(token::Lit { kind: token::Float, symbol, .. }) => {
-                        self.bump();
-                        let fstr = symbol.as_str();
-                        let msg = format!("unexpected token: `{}`", symbol);
-                        let mut err = self.diagnostic().struct_span_err(self.prev_span, &msg);
-                        err.span_label(self.prev_span, "unexpected token");
-                        if fstr.chars().all(|x| "0123456789.".contains(x)) {
-                            let float = match fstr.parse::<f64>().ok() {
-                                Some(f) => f,
-                                None => continue,
-                            };
-                            let sugg = pprust::to_string(|s| {
-                                s.popen();
-                                s.print_expr(&e);
-                                s.s.word(".");
-                                s.print_usize(float.trunc() as usize);
-                                s.pclose();
-                                s.s.word(".");
-                                s.s.word(fstr.splitn(2, ".").last().unwrap().to_string())
-                            });
-                            err.span_suggestion(
-                                lo.to(self.prev_span),
-                                "try parenthesizing the first index",
-                                sugg,
-                                Applicability::MachineApplicable,
-                            );
-                        }
-                        return Err(err);
-                    }
-                    _ => {
-                        // FIXME Could factor this out into non_fatal_unexpected or something.
-                        let actual = self.this_token_to_string();
-                        self.span_err(self.token.span, &format!("unexpected token: `{}`", actual));
-                    }
-                }
+                // expr.f
+                e = self.parse_dot_suffix_expr(lo, e)?;
                 continue;
             }
             if self.expr_is_complete(&e) {
-                break;
+                return Ok(e);
             }
-            match self.token.kind {
-                // expr(...)
-                token::OpenDelim(token::Paren) => {
-                    let seq = self.parse_paren_expr_seq().map(|es| {
-                        let nd = self.mk_call(e, es);
-                        let hi = self.prev_span;
-                        self.mk_expr(lo.to(hi), nd, AttrVec::new())
-                    });
-                    e = self.recover_seq_parse_error(token::Paren, lo, seq);
-                }
-
-                // expr[...]
-                // Could be either an index expression or a slicing expression.
-                token::OpenDelim(token::Bracket) => {
-                    self.bump();
-                    let ix = self.parse_expr()?;
-                    hi = self.token.span;
-                    self.expect(&token::CloseDelim(token::Bracket))?;
-                    let index = self.mk_index(e, ix);
-                    e = self.mk_expr(lo.to(hi), index, AttrVec::new())
-                }
+            e = match self.token.kind {
+                token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e),
+                token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?,
                 _ => return Ok(e),
             }
         }
-        return Ok(e);
+    }
+
+    fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
+        match self.token.kind {
+            token::Ident(..) => self.parse_dot_suffix(base, lo),
+            token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
+                Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix))
+            }
+            token::Literal(token::Lit { kind: token::Float, symbol, .. }) => {
+                self.recover_field_access_by_float_lit(lo, base, symbol)
+            }
+            _ => {
+                self.error_unexpected_after_dot();
+                Ok(base)
+            }
+        }
+    }
+
+    fn error_unexpected_after_dot(&self) {
+        // FIXME Could factor this out into non_fatal_unexpected or something.
+        let actual = pprust::token_to_string(&self.token);
+        self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit();
+    }
+
+    fn recover_field_access_by_float_lit(
+        &mut self,
+        lo: Span,
+        base: P<Expr>,
+        sym: Symbol,
+    ) -> PResult<'a, P<Expr>> {
+        self.bump();
+
+        let fstr = sym.as_str();
+        let msg = format!("unexpected token: `{}`", sym);
+
+        let mut err = self.struct_span_err(self.prev_span, &msg);
+        err.span_label(self.prev_span, "unexpected token");
+
+        if fstr.chars().all(|x| "0123456789.".contains(x)) {
+            let float = match fstr.parse::<f64>() {
+                Ok(f) => f,
+                Err(_) => {
+                    err.emit();
+                    return Ok(base);
+                }
+            };
+            let sugg = pprust::to_string(|s| {
+                s.popen();
+                s.print_expr(&base);
+                s.s.word(".");
+                s.print_usize(float.trunc() as usize);
+                s.pclose();
+                s.s.word(".");
+                s.s.word(fstr.splitn(2, ".").last().unwrap().to_string())
+            });
+            err.span_suggestion(
+                lo.to(self.prev_span),
+                "try parenthesizing the first index",
+                sugg,
+                Applicability::MachineApplicable,
+            );
+        }
+        Err(err)
+    }
+
+    fn parse_tuple_field_access_expr(
+        &mut self,
+        lo: Span,
+        base: P<Expr>,
+        field: Symbol,
+        suffix: Option<Symbol>,
+    ) -> P<Expr> {
+        let span = self.token.span;
+        self.bump();
+        let field = ExprKind::Field(base, Ident::new(field, span));
+        self.expect_no_suffix(span, "a tuple index", suffix);
+        self.mk_expr(lo.to(span), field, AttrVec::new())
+    }
+
+    /// Parse a function call expression, `expr(...)`.
+    fn parse_fn_call_expr(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
+        let seq = self.parse_paren_expr_seq().map(|args| {
+            self.mk_expr(lo.to(self.prev_span), self.mk_call(fun, args), AttrVec::new())
+        });
+        self.recover_seq_parse_error(token::Paren, lo, seq)
+    }
+
+    /// Parse an indexing expression `expr[...]`.
+    fn parse_index_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
+        self.bump(); // `[`
+        let index = self.parse_expr()?;
+        self.expect(&token::CloseDelim(token::Bracket))?;
+        Ok(self.mk_expr(lo.to(self.prev_span), self.mk_index(base, index), AttrVec::new()))
     }
 
     /// Assuming we have just parsed `.`, continue parsing into an expression.
@@ -794,25 +812,22 @@ impl<'a> Parser<'a> {
         let segment = self.parse_path_segment(PathStyle::Expr)?;
         self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren));
 
-        Ok(match self.token.kind {
-            token::OpenDelim(token::Paren) => {
-                // Method call `expr.f()`
-                let mut args = self.parse_paren_expr_seq()?;
-                args.insert(0, self_arg);
+        if self.check(&token::OpenDelim(token::Paren)) {
+            // Method call `expr.f()`
+            let mut args = self.parse_paren_expr_seq()?;
+            args.insert(0, self_arg);
 
-                let span = lo.to(self.prev_span);
-                self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new())
+            let span = lo.to(self.prev_span);
+            Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new()))
+        } else {
+            // Field access `expr.f`
+            if let Some(args) = segment.args {
+                self.span_err(args.span(), "field expressions may not have generic arguments");
             }
-            _ => {
-                // Field access `expr.f`
-                if let Some(args) = segment.args {
-                    self.span_err(args.span(), "field expressions may not have generic arguments");
-                }
 
-                let span = lo.to(self.prev_span);
-                self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new())
-            }
-        })
+            let span = lo.to(self.prev_span);
+            Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new()))
+        }
     }
 
     /// At the bottom (top?) of the precedence hierarchy,
@@ -1117,7 +1132,7 @@ impl<'a> Parser<'a> {
 
     pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
         self.parse_opt_lit().ok_or_else(|| {
-            let msg = format!("unexpected token: {}", self.this_token_descr());
+            let msg = format!("unexpected token: {}", super::token_descr(&self.token));
             self.span_fatal(self.token.span, &msg)
         })
     }
@@ -1143,14 +1158,7 @@ impl<'a> Parser<'a> {
             });
             if let Some(token) = &recovered {
                 self.bump();
-                self.struct_span_err(token.span, "float literals must have an integer part")
-                    .span_suggestion(
-                        token.span,
-                        "must have an integer part",
-                        pprust::token_to_string(token),
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                self.error_float_lits_must_have_int_part(&token);
             }
         }
 
@@ -1179,6 +1187,17 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn error_float_lits_must_have_int_part(&self, token: &Token) {
+        self.struct_span_err(token.span, "float literals must have an integer part")
+            .span_suggestion(
+                token.span,
+                "must have an integer part",
+                pprust::token_to_string(token),
+                Applicability::MachineApplicable,
+            )
+            .emit();
+    }
+
     fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) {
         // Checks if `s` looks like i32 or u1234 etc.
         fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
@@ -1282,17 +1301,13 @@ impl<'a> Parser<'a> {
     pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
         maybe_whole_expr!(self);
 
-        let minus_lo = self.token.span;
-        let minus_present = self.eat(&token::BinOp(token::Minus));
         let lo = self.token.span;
-        let literal = self.parse_lit()?;
-        let hi = self.prev_span;
-        let expr = self.mk_expr(lo.to(hi), ExprKind::Lit(literal), AttrVec::new());
+        let minus_present = self.eat(&token::BinOp(token::Minus));
+        let lit = self.parse_lit()?;
+        let expr = self.mk_expr(lit.span, ExprKind::Lit(lit), AttrVec::new());
 
         if minus_present {
-            let minus_hi = self.prev_span;
-            let unary = self.mk_unary(UnOp::Neg, expr);
-            Ok(self.mk_expr(minus_lo.to(minus_hi), unary, AttrVec::new()))
+            Ok(self.mk_expr(lo.to(self.prev_span), self.mk_unary(UnOp::Neg, expr), AttrVec::new()))
         } else {
             Ok(expr)
         }
@@ -1362,26 +1377,24 @@ impl<'a> Parser<'a> {
 
     /// Parses the `|arg, arg|` header of a closure.
     fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
-        let inputs_captures = {
-            if self.eat(&token::OrOr) {
-                Vec::new()
-            } else {
-                self.expect(&token::BinOp(token::Or))?;
-                let args = self
-                    .parse_seq_to_before_tokens(
-                        &[&token::BinOp(token::Or), &token::OrOr],
-                        SeqSep::trailing_allowed(token::Comma),
-                        TokenExpectType::NoExpect,
-                        |p| p.parse_fn_block_param(),
-                    )?
-                    .0;
-                self.expect_or()?;
-                args
-            }
+        let inputs = if self.eat(&token::OrOr) {
+            Vec::new()
+        } else {
+            self.expect(&token::BinOp(token::Or))?;
+            let args = self
+                .parse_seq_to_before_tokens(
+                    &[&token::BinOp(token::Or), &token::OrOr],
+                    SeqSep::trailing_allowed(token::Comma),
+                    TokenExpectType::NoExpect,
+                    |p| p.parse_fn_block_param(),
+                )?
+                .0;
+            self.expect_or()?;
+            args
         };
         let output = self.parse_ret_ty(true, true)?;
 
-        Ok(P(FnDecl { inputs: inputs_captures, output }))
+        Ok(P(FnDecl { inputs, output }))
     }
 
     /// Parses a parameter in a closure header (e.g., `|arg, arg|`).
@@ -1389,17 +1402,16 @@ impl<'a> Parser<'a> {
         let lo = self.token.span;
         let attrs = self.parse_outer_attributes()?;
         let pat = self.parse_pat(PARAM_EXPECTED)?;
-        let t = if self.eat(&token::Colon) {
+        let ty = if self.eat(&token::Colon) {
             self.parse_ty()?
         } else {
-            P(Ty { id: DUMMY_NODE_ID, kind: TyKind::Infer, span: self.prev_span })
+            self.mk_ty(self.prev_span, TyKind::Infer)
         };
-        let span = lo.to(self.token.span);
         Ok(Param {
             attrs: attrs.into(),
-            ty: t,
+            ty,
             pat,
-            span,
+            span: lo.to(self.token.span),
             id: DUMMY_NODE_ID,
             is_placeholder: false,
         })
@@ -1414,28 +1426,29 @@ impl<'a> Parser<'a> {
         // verify that the last statement is either an implicit return (no `;`) or an explicit
         // return. This won't catch blocks with an explicit `return`, but that would be caught by
         // the dead code lint.
-        if self.eat_keyword(kw::Else) || !cond.returns() {
-            let sp = self.sess.source_map().next_point(lo);
-            let mut err =
-                self.diagnostic().struct_span_err(sp, "missing condition for `if` expression");
-            err.span_label(sp, "expected if condition here");
-            return Err(err);
-        }
-        let not_block = self.token != token::OpenDelim(token::Brace);
-        let thn = self.parse_block().map_err(|mut err| {
-            if not_block {
-                err.span_label(lo, "this `if` statement has a condition, but no block");
-            }
-            err
-        })?;
-        let mut els: Option<P<Expr>> = None;
-        let mut hi = thn.span;
-        if self.eat_keyword(kw::Else) {
-            let elexpr = self.parse_else_expr()?;
-            hi = elexpr.span;
-            els = Some(elexpr);
-        }
-        Ok(self.mk_expr(lo.to(hi), ExprKind::If(cond, thn, els), attrs))
+        let thn = if self.eat_keyword(kw::Else) || !cond.returns() {
+            self.error_missing_if_cond(lo, cond.span)
+        } else {
+            let not_block = self.token != token::OpenDelim(token::Brace);
+            self.parse_block().map_err(|mut err| {
+                if not_block {
+                    err.span_label(lo, "this `if` expression has a condition, but no block");
+                }
+                err
+            })?
+        };
+        let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
+        Ok(self.mk_expr(lo.to(self.prev_span), ExprKind::If(cond, thn, els), attrs))
+    }
+
+    fn error_missing_if_cond(&self, lo: Span, span: Span) -> P<ast::Block> {
+        let sp = self.sess.source_map().next_point(lo);
+        self.struct_span_err(sp, "missing condition for `if` expression")
+            .span_label(sp, "expected if condition here")
+            .emit();
+        let expr = self.mk_expr_err(span);
+        let stmt = self.mk_stmt(span, ast::StmtKind::Expr(expr));
+        self.mk_block(vec![stmt], BlockCheckMode::Default, span)
     }
 
     /// Parses the condition of a `if` or `while` expression.
@@ -1467,22 +1480,20 @@ 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) {
-            return self.parse_if_expr(AttrVec::new());
+            self.parse_if_expr(AttrVec::new())
         } else {
             let blk = self.parse_block()?;
-            return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()));
+            Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
         }
     }
 
-    /// Parses a `for ... in` expression (`for` token already eaten).
+    /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
     fn parse_for_expr(
         &mut self,
         opt_label: Option<Label>,
-        span_lo: Span,
+        lo: Span,
         mut attrs: AttrVec,
     ) -> PResult<'a, P<Expr>> {
-        // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
-
         // Record whether we are about to parse `for (`.
         // This is used below for recovery in case of `for ( $stuff ) $block`
         // in which case we will suggest `for $stuff $block`.
@@ -1493,19 +1504,9 @@ impl<'a> Parser<'a> {
 
         let pat = self.parse_top_pat(GateOr::Yes)?;
         if !self.eat_keyword(kw::In) {
-            let in_span = self.prev_span.between(self.token.span);
-            self.struct_span_err(in_span, "missing `in` in `for` loop")
-                .span_suggestion_short(
-                    in_span,
-                    "try adding `in` here",
-                    " in ".into(),
-                    // has been misleading, at least in the past (closed Issue #48492)
-                    Applicability::MaybeIncorrect,
-                )
-                .emit();
+            self.error_missing_in_for_loop();
         }
-        let in_span = self.prev_span;
-        self.check_for_for_in_in_typo(in_span);
+        self.check_for_for_in_in_typo(self.prev_span);
         let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
 
         let pat = self.recover_parens_around_for_head(pat, &expr, begin_paren);
@@ -1513,52 +1514,61 @@ impl<'a> Parser<'a> {
         let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
 
-        let hi = self.prev_span;
-        Ok(self.mk_expr(span_lo.to(hi), ExprKind::ForLoop(pat, expr, loop_block, opt_label), attrs))
+        let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
+        Ok(self.mk_expr(lo.to(self.prev_span), kind, attrs))
+    }
+
+    fn error_missing_in_for_loop(&self) {
+        let in_span = self.prev_span.between(self.token.span);
+        self.struct_span_err(in_span, "missing `in` in `for` loop")
+            .span_suggestion_short(
+                in_span,
+                "try adding `in` here",
+                " in ".into(),
+                // Has been misleading, at least in the past (closed Issue #48492).
+                Applicability::MaybeIncorrect,
+            )
+            .emit();
     }
 
     /// Parses a `while` or `while let` expression (`while` token already eaten).
     fn parse_while_expr(
         &mut self,
         opt_label: Option<Label>,
-        span_lo: Span,
+        lo: Span,
         mut attrs: AttrVec,
     ) -> PResult<'a, P<Expr>> {
         let cond = self.parse_cond_expr()?;
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
-        let span = span_lo.to(body.span);
-        Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs))
+        Ok(self.mk_expr(lo.to(self.prev_span), ExprKind::While(cond, body, opt_label), attrs))
     }
 
     /// Parses `loop { ... }` (`loop` token already eaten).
     fn parse_loop_expr(
         &mut self,
         opt_label: Option<Label>,
-        span_lo: Span,
+        lo: Span,
         mut attrs: AttrVec,
     ) -> PResult<'a, P<Expr>> {
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
-        let span = span_lo.to(body.span);
-        Ok(self.mk_expr(span, ExprKind::Loop(body, opt_label), attrs))
+        Ok(self.mk_expr(lo.to(self.prev_span), ExprKind::Loop(body, opt_label), attrs))
     }
 
     fn eat_label(&mut self) -> Option<Label> {
-        if let Some(ident) = self.token.lifetime() {
+        self.token.lifetime().map(|ident| {
             let span = self.token.span;
             self.bump();
-            Some(Label { ident: Ident::new(ident.name, span) })
-        } else {
-            None
-        }
+            Label { ident: Ident::new(ident.name, span) }
+        })
     }
 
     /// Parses a `match ... { ... }` expression (`match` token already eaten).
     fn parse_match_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let match_span = self.prev_span;
         let lo = self.prev_span;
-        let discriminant = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
         if let Err(mut e) = self.expect(&token::OpenDelim(token::Brace)) {
             if self.token == token::Semi {
                 e.span_suggestion_short(
@@ -1584,13 +1594,13 @@ impl<'a> Parser<'a> {
                     if self.token == token::CloseDelim(token::Brace) {
                         self.bump();
                     }
-                    return Ok(self.mk_expr(span, ExprKind::Match(discriminant, arms), attrs));
+                    return Ok(self.mk_expr(span, ExprKind::Match(scrutinee, arms), attrs));
                 }
             }
         }
         let hi = self.token.span;
         self.bump();
-        return Ok(self.mk_expr(lo.to(hi), ExprKind::Match(discriminant, arms), attrs));
+        return Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs));
     }
 
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
@@ -1699,16 +1709,13 @@ impl<'a> Parser<'a> {
 
     /// Parses an `async move? {...}` expression.
     fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
-        let span_lo = self.token.span;
+        let lo = self.token.span;
         self.expect_keyword(kw::Async)?;
         let capture_clause = self.parse_capture_clause();
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
-        Ok(self.mk_expr(
-            span_lo.to(body.span),
-            ExprKind::Async(capture_clause, DUMMY_NODE_ID, body),
-            attrs,
-        ))
+        let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body);
+        Ok(self.mk_expr(lo.to(self.prev_span), kind, attrs))
     }
 
     fn is_async_block(&self) -> bool {
@@ -1723,6 +1730,21 @@ impl<'a> Parser<'a> {
             ))
     }
 
+    fn is_certainly_not_a_block(&self) -> bool {
+        self.look_ahead(1, |t| t.is_ident())
+            && (
+                // `{ ident, ` cannot start a block.
+                self.look_ahead(2, |t| t == &token::Comma)
+                    || self.look_ahead(2, |t| t == &token::Colon)
+                        && (
+                            // `{ ident: token, ` cannot start a block.
+                            self.look_ahead(4, |t| t == &token::Comma) ||
+                // `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
+                self.look_ahead(3, |t| !t.can_begin_type())
+                        )
+            )
+    }
+
     fn maybe_parse_struct_expr(
         &mut self,
         lo: Span,
@@ -1730,41 +1752,27 @@ impl<'a> Parser<'a> {
         attrs: &AttrVec,
     ) -> Option<PResult<'a, P<Expr>>> {
         let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
-        let certainly_not_a_block = || {
-            self.look_ahead(1, |t| t.is_ident())
-                && (
-                    // `{ ident, ` cannot start a block.
-                    self.look_ahead(2, |t| t == &token::Comma)
-                        || self.look_ahead(2, |t| t == &token::Colon)
-                            && (
-                                // `{ ident: token, ` cannot start a block.
-                                self.look_ahead(4, |t| t == &token::Comma) ||
-                // `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
-                self.look_ahead(3, |t| !t.can_begin_type())
-                            )
-                )
-        };
-
-        if struct_allowed || certainly_not_a_block() {
+        if struct_allowed || self.is_certainly_not_a_block() {
             // This is a struct literal, but we don't can't accept them here.
             let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone());
             if let (Ok(expr), false) = (&expr, struct_allowed) {
-                self.struct_span_err(expr.span, "struct literals are not allowed here")
-                    .multipart_suggestion(
-                        "surround the struct literal with parentheses",
-                        vec![
-                            (lo.shrink_to_lo(), "(".to_string()),
-                            (expr.span.shrink_to_hi(), ")".to_string()),
-                        ],
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                self.error_struct_lit_not_allowed_here(lo, expr.span);
             }
             return Some(expr);
         }
         None
     }
 
+    fn error_struct_lit_not_allowed_here(&self, lo: Span, sp: Span) {
+        self.struct_span_err(sp, "struct literals are not allowed here")
+            .multipart_suggestion(
+                "surround the struct literal with parentheses",
+                vec![(lo.shrink_to_lo(), "(".to_string()), (sp.shrink_to_hi(), ")".to_string())],
+                Applicability::MachineApplicable,
+            )
+            .emit();
+    }
+
     pub(super) fn parse_struct_expr(
         &mut self,
         lo: Span,
@@ -1782,50 +1790,19 @@ impl<'a> Parser<'a> {
             if self.eat(&token::DotDot) {
                 let exp_span = self.prev_span;
                 match self.parse_expr() {
-                    Ok(e) => {
-                        base = Some(e);
-                    }
+                    Ok(e) => base = Some(e),
                     Err(mut e) => {
                         e.emit();
                         self.recover_stmt();
                     }
                 }
-                if self.token == token::Comma {
-                    self.struct_span_err(
-                        exp_span.to(self.prev_span),
-                        "cannot use a comma after the base struct",
-                    )
-                    .span_suggestion_short(
-                        self.token.span,
-                        "remove this comma",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    )
-                    .note("the base struct must always be the last field")
-                    .emit();
-                    self.recover_stmt();
-                }
+                self.recover_struct_comma_after_dotdot(exp_span);
                 break;
             }
 
-            let mut recovery_field = None;
-            if let token::Ident(name, _) = self.token.kind {
-                if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) {
-                    // Use in case of error after field-looking code: `S { foo: () with a }`.
-                    recovery_field = Some(ast::Field {
-                        ident: Ident::new(name, self.token.span),
-                        span: self.token.span,
-                        expr: self.mk_expr(self.token.span, ExprKind::Err, AttrVec::new()),
-                        is_shorthand: false,
-                        attrs: AttrVec::new(),
-                        id: DUMMY_NODE_ID,
-                        is_placeholder: false,
-                    });
-                }
-            }
-            let mut parsed_field = None;
-            match self.parse_field() {
-                Ok(f) => parsed_field = Some(f),
+            let recovery_field = self.find_struct_error_after_field_looking_code();
+            let parsed_field = match self.parse_field() {
+                Ok(f) => Some(f),
                 Err(mut e) => {
                     e.span_label(struct_sp, "while parsing this struct");
                     e.emit();
@@ -1839,8 +1816,9 @@ impl<'a> Parser<'a> {
                             break;
                         }
                     }
+                    None
                 }
-            }
+            };
 
             match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) {
                 Ok(_) => {
@@ -1863,53 +1841,91 @@ impl<'a> Parser<'a> {
 
         let span = lo.to(self.token.span);
         self.expect(&token::CloseDelim(token::Brace))?;
-        return Ok(self.mk_expr(span, ExprKind::Struct(pth, fields, base), attrs));
+        Ok(self.mk_expr(span, ExprKind::Struct(pth, fields, base), attrs))
+    }
+
+    /// Use in case of error after field-looking code: `S { foo: () with a }`.
+    fn find_struct_error_after_field_looking_code(&self) -> Option<Field> {
+        if let token::Ident(name, _) = self.token.kind {
+            if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) {
+                let span = self.token.span;
+                return Some(ast::Field {
+                    ident: Ident::new(name, span),
+                    span,
+                    expr: self.mk_expr_err(span),
+                    is_shorthand: false,
+                    attrs: AttrVec::new(),
+                    id: DUMMY_NODE_ID,
+                    is_placeholder: false,
+                });
+            }
+        }
+        None
+    }
+
+    fn recover_struct_comma_after_dotdot(&mut self, span: Span) {
+        if self.token != token::Comma {
+            return;
+        }
+        self.struct_span_err(span.to(self.prev_span), "cannot use a comma after the base struct")
+            .span_suggestion_short(
+                self.token.span,
+                "remove this comma",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .note("the base struct must always be the last field")
+            .emit();
+        self.recover_stmt();
     }
 
     /// Parses `ident (COLON expr)?`.
     fn parse_field(&mut self) -> PResult<'a, Field> {
-        let attrs = self.parse_outer_attributes()?;
+        let attrs = self.parse_outer_attributes()?.into();
         let lo = self.token.span;
 
         // Check if a colon exists one ahead. This means we're parsing a fieldname.
-        let (fieldname, expr, is_shorthand) =
-            if self.look_ahead(1, |t| t == &token::Colon || t == &token::Eq) {
-                let fieldname = self.parse_field_name()?;
-
-                // Check for an equals token. This means the source incorrectly attempts to
-                // initialize a field with an eq rather than a colon.
-                if self.token == token::Eq {
-                    self.diagnostic()
-                        .struct_span_err(self.token.span, "expected `:`, found `=`")
-                        .span_suggestion(
-                            fieldname.span.shrink_to_hi().to(self.token.span),
-                            "replace equals symbol with a colon",
-                            ":".to_string(),
-                            Applicability::MachineApplicable,
-                        )
-                        .emit();
-                }
-                self.bump(); // `:`
-                (fieldname, self.parse_expr()?, false)
-            } else {
-                let fieldname = self.parse_ident_common(false)?;
-
-                // Mimic `x: x` for the `x` field shorthand.
-                let path = ast::Path::from_ident(fieldname);
-                let expr = self.mk_expr(fieldname.span, ExprKind::Path(None, path), AttrVec::new());
-                (fieldname, expr, true)
-            };
+        let is_shorthand = !self.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
+        let (ident, expr) = if is_shorthand {
+            // Mimic `x: x` for the `x` field shorthand.
+            let ident = self.parse_ident_common(false)?;
+            let path = ast::Path::from_ident(ident);
+            (ident, self.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new()))
+        } else {
+            let ident = self.parse_field_name()?;
+            self.error_on_eq_field_init(ident);
+            self.bump(); // `:`
+            (ident, self.parse_expr()?)
+        };
         Ok(ast::Field {
-            ident: fieldname,
+            ident,
             span: lo.to(expr.span),
             expr,
             is_shorthand,
-            attrs: attrs.into(),
+            attrs,
             id: DUMMY_NODE_ID,
             is_placeholder: false,
         })
     }
 
+    /// Check for `=`. This means the source incorrectly attempts to
+    /// initialize a field with an eq rather than a colon.
+    fn error_on_eq_field_init(&self, field_name: Ident) {
+        if self.token != token::Eq {
+            return;
+        }
+
+        self.diagnostic()
+            .struct_span_err(self.token.span, "expected `:`, found `=`")
+            .span_suggestion(
+                field_name.span.shrink_to_hi().to(self.token.span),
+                "replace equals symbol with a colon",
+                ":".to_string(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
+    }
+
     fn err_dotdotdot_syntax(&self, span: Span) {
         self.struct_span_err(span, "unexpected token: `...`")
             .span_suggestion(
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 8f8e5fbb78765..343c6667d47d5 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -1348,7 +1348,7 @@ impl<'a> Parser<'a> {
             self.expect_semi()?;
             body
         } else {
-            let token_str = self.this_token_descr();
+            let token_str = super::token_descr(&self.token);
             let mut err = self.fatal(&format!(
                 "expected `where`, `{{`, `(`, or `;` after struct name, found {}",
                 token_str
@@ -1374,7 +1374,7 @@ impl<'a> Parser<'a> {
             let (fields, recovered) = self.parse_record_struct_body()?;
             VariantData::Struct(fields, recovered)
         } else {
-            let token_str = self.this_token_descr();
+            let token_str = super::token_descr(&self.token);
             let mut err = self
                 .fatal(&format!("expected `where` or `{{` after union name, found {}", token_str));
             err.span_label(self.token.span, "expected `where` or `{` after union name");
@@ -1411,7 +1411,7 @@ impl<'a> Parser<'a> {
             }
             self.eat(&token::CloseDelim(token::Brace));
         } else {
-            let token_str = self.this_token_descr();
+            let token_str = super::token_descr(&self.token);
             let mut err = self.fatal(&format!(
                 "expected `where`, or `{{` after struct name, found {}",
                 token_str
@@ -1498,7 +1498,7 @@ impl<'a> Parser<'a> {
                 let sp = self.sess.source_map().next_point(self.prev_span);
                 let mut err = self.struct_span_err(
                     sp,
-                    &format!("expected `,`, or `}}`, found {}", self.this_token_descr()),
+                    &format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
                 );
                 if self.token.is_ident() {
                     // This is likely another field; emit the diagnostic and keep going
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 97084482025bb..103bbe5dd763e 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -354,6 +354,24 @@ pub enum FollowedByType {
     No,
 }
 
+fn token_descr_opt(token: &Token) -> Option<&'static str> {
+    Some(match token.kind {
+        _ if token.is_special_ident() => "reserved identifier",
+        _ if token.is_used_keyword() => "keyword",
+        _ if token.is_unused_keyword() => "reserved keyword",
+        token::DocComment(..) => "doc comment",
+        _ => return None,
+    })
+}
+
+pub(super) fn token_descr(token: &Token) -> String {
+    let token_str = pprust::token_to_string(token);
+    match token_descr_opt(token) {
+        Some(prefix) => format!("{} `{}`", prefix, token_str),
+        _ => format!("`{}`", token_str),
+    }
+}
+
 impl<'a> Parser<'a> {
     pub fn new(
         sess: &'a ParseSess,
@@ -422,29 +440,6 @@ impl<'a> Parser<'a> {
         next
     }
 
-    /// Converts the current token to a string using `self`'s reader.
-    pub fn this_token_to_string(&self) -> String {
-        pprust::token_to_string(&self.token)
-    }
-
-    fn token_descr(&self) -> Option<&'static str> {
-        Some(match &self.token.kind {
-            _ if self.token.is_special_ident() => "reserved identifier",
-            _ if self.token.is_used_keyword() => "keyword",
-            _ if self.token.is_unused_keyword() => "reserved keyword",
-            token::DocComment(..) => "doc comment",
-            _ => return None,
-        })
-    }
-
-    pub(super) fn this_token_descr(&self) -> String {
-        if let Some(prefix) = self.token_descr() {
-            format!("{} `{}`", prefix, self.this_token_to_string())
-        } else {
-            format!("`{}`", self.this_token_to_string())
-        }
-    }
-
     crate fn unexpected<T>(&mut self) -> PResult<'a, T> {
         match self.expect_one_of(&[], &[]) {
             Err(e) => Err(e),
diff --git a/src/librustc_parse/parser/module.rs b/src/librustc_parse/parser/module.rs
index eaeb3af4ca2a2..3f54e0b6de031 100644
--- a/src/librustc_parse/parser/module.rs
+++ b/src/librustc_parse/parser/module.rs
@@ -79,7 +79,7 @@ impl<'a> Parser<'a> {
         }
 
         if !self.eat(term) {
-            let token_str = self.this_token_descr();
+            let token_str = super::token_descr(&self.token);
             if !self.maybe_consume_incorrect_semicolon(&items) {
                 let mut err = self.fatal(&format!("expected item, found {}", token_str));
                 err.span_label(self.token.span, "expected item");
diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs
index 1540e6d7c053f..d2288b27a0c40 100644
--- a/src/librustc_parse/parser/pat.rs
+++ b/src/librustc_parse/parser/pat.rs
@@ -671,7 +671,7 @@ impl<'a> Parser<'a> {
         err.cancel();
 
         let expected = expected.unwrap_or("pattern");
-        let msg = format!("expected {}, found {}", expected, self.this_token_descr());
+        let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token));
 
         let mut err = self.fatal(&msg);
         err.span_label(self.token.span, format!("expected {}", expected));
@@ -876,7 +876,7 @@ impl<'a> Parser<'a> {
                     etc_span = Some(etc_sp);
                     break;
                 }
-                let token_str = self.this_token_descr();
+                let token_str = super::token_descr(&self.token);
                 let mut err = self.fatal(&format!("expected `}}`, found {}", token_str));
 
                 err.span_label(self.token.span, "expected `}`");
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index ed5649310e3fd..8270da6c0234f 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -323,7 +323,7 @@ impl<'a> Parser<'a> {
 
     fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
         let sp = self.token.span;
-        let tok = self.this_token_descr();
+        let tok = super::token_descr(&self.token);
         let mut e = self.span_fatal(sp, &format!("expected `{{`, found {}", tok));
         let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
 
@@ -411,7 +411,7 @@ impl<'a> Parser<'a> {
                 continue;
             };
         }
-        Ok(P(ast::Block { stmts, id: DUMMY_NODE_ID, rules: s, span: lo.to(self.prev_span) }))
+        Ok(self.mk_block(stmts, s, lo.to(self.prev_span)))
     }
 
     /// Parses a statement, including the trailing semicolon.
@@ -463,7 +463,7 @@ impl<'a> Parser<'a> {
     fn warn_missing_semicolon(&self) {
         self.diagnostic()
             .struct_span_warn(self.token.span, {
-                &format!("expected `;`, found {}", self.this_token_descr())
+                &format!("expected `;`, found {}", super::token_descr(&self.token))
             })
             .note({
                 "this was erroneously allowed and will become a hard error in a future release"
@@ -471,7 +471,11 @@ impl<'a> Parser<'a> {
             .emit();
     }
 
-    fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
+    pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> {
+        P(Block { stmts, id: DUMMY_NODE_ID, rules, span })
+    }
+
+    pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
         Stmt { id: DUMMY_NODE_ID, kind, span }
     }
 }
diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs
index fe3db358accde..049c077c3cea3 100644
--- a/src/librustc_parse/parser/ty.rs
+++ b/src/librustc_parse/parser/ty.rs
@@ -135,7 +135,7 @@ impl<'a> Parser<'a> {
                 TyKind::Err
             }
         } else {
-            let msg = format!("expected type, found {}", self.this_token_descr());
+            let msg = format!("expected type, found {}", super::token_descr(&self.token));
             let mut err = self.struct_span_err(self.token.span, &msg);
             err.span_label(self.token.span, "expected type");
             self.maybe_annotate_with_ascription(&mut err, true);
diff --git a/src/libsyntax_expand/expand.rs b/src/libsyntax_expand/expand.rs
index b9b449d177915..4df51ff41f3f6 100644
--- a/src/libsyntax_expand/expand.rs
+++ b/src/libsyntax_expand/expand.rs
@@ -904,10 +904,8 @@ pub fn ensure_complete_parse<'a>(
     span: Span,
 ) {
     if this.token != token::Eof {
-        let msg = format!(
-            "macro expansion ignores token `{}` and any following",
-            this.this_token_to_string()
-        );
+        let token = pprust::token_to_string(&this.token);
+        let msg = format!("macro expansion ignores token `{}` and any following", token);
         // Avoid emitting backtrace info twice.
         let def_site_span = this.token.span.with_ctxt(SyntaxContext::root());
         let mut err = this.struct_span_err(def_site_span, &msg);
diff --git a/src/libsyntax_ext/source_util.rs b/src/libsyntax_ext/source_util.rs
index 4f46252f8be61..fccc36e2ea809 100644
--- a/src/libsyntax_ext/source_util.rs
+++ b/src/libsyntax_ext/source_util.rs
@@ -133,15 +133,17 @@ pub fn expand_include<'cx>(
             while self.p.token != token::Eof {
                 match panictry!(self.p.parse_item()) {
                     Some(item) => ret.push(item),
-                    None => self
-                        .p
-                        .sess
-                        .span_diagnostic
-                        .span_fatal(
-                            self.p.token.span,
-                            &format!("expected item, found `{}`", self.p.this_token_to_string()),
-                        )
-                        .raise(),
+                    None => {
+                        let token = pprust::token_to_string(&self.p.token);
+                        self.p
+                            .sess
+                            .span_diagnostic
+                            .span_fatal(
+                                self.p.token.span,
+                                &format!("expected item, found `{}`", token),
+                            )
+                            .raise();
+                    }
                 }
             }
             Some(ret)
diff --git a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
index f5edbe2a3af53..8025886a9eb46 100644
--- a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
+++ b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
@@ -26,7 +26,7 @@ error: expected `{`, found `;`
   --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:20:31
    |
 LL |     if not  // lack of braces is [sic]
-   |     -- this `if` statement has a condition, but no block
+   |     -- this `if` expression has a condition, but no block
 LL |         println!("Then when?");
    |                               ^
    |                               |
diff --git a/src/test/ui/if/if-let.stderr b/src/test/ui/if/if-let.stderr
index 83ab234cf04ea..570a64e999cd0 100644
--- a/src/test/ui/if/if-let.stderr
+++ b/src/test/ui/if/if-let.stderr
@@ -2,7 +2,7 @@ warning: irrefutable if-let pattern
   --> $DIR/if-let.rs:6:13
    |
 LL |               if let $p = $e $b
-   |               ^^
+   |               ^^^^^^^^^^^^^^^^^
 ...
 LL | /     foo!(a, 1, {
 LL | |         println!("irrefutable pattern");
@@ -15,7 +15,7 @@ warning: irrefutable if-let pattern
   --> $DIR/if-let.rs:6:13
    |
 LL |               if let $p = $e $b
-   |               ^^
+   |               ^^^^^^^^^^^^^^^^^
 ...
 LL | /     bar!(a, 1, {
 LL | |         println!("irrefutable pattern");
diff --git a/src/test/ui/if/if-without-block.rs b/src/test/ui/if/if-without-block.rs
index 3dde0ed7c7188..8a4c59f32613d 100644
--- a/src/test/ui/if/if-without-block.rs
+++ b/src/test/ui/if/if-without-block.rs
@@ -1,7 +1,7 @@
 fn main() {
     let n = 1;
     if 5 == {
-    //~^ NOTE this `if` statement has a condition, but no block
+    //~^ NOTE this `if` expression has a condition, but no block
         println!("five");
     }
 }
diff --git a/src/test/ui/if/if-without-block.stderr b/src/test/ui/if/if-without-block.stderr
index 1e45045adece8..34df8e3d77946 100644
--- a/src/test/ui/if/if-without-block.stderr
+++ b/src/test/ui/if/if-without-block.stderr
@@ -2,7 +2,7 @@ error: expected `{`, found `}`
   --> $DIR/if-without-block.rs:7:1
    |
 LL |     if 5 == {
-   |     -- this `if` statement has a condition, but no block
+   |     -- this `if` expression has a condition, but no block
 ...
 LL | }
    | ^ expected `{`
diff --git a/src/test/ui/issues/issue-13483.rs b/src/test/ui/issues/issue-13483.rs
index cb53523b3b04d..a2fd9264b1530 100644
--- a/src/test/ui/issues/issue-13483.rs
+++ b/src/test/ui/issues/issue-13483.rs
@@ -1,6 +1,7 @@
 fn main() {
     if true {
     } else if { //~ ERROR missing condition
+    //~^ ERROR mismatched types
     } else {
     }
 }
@@ -8,6 +9,7 @@ fn main() {
 fn foo() {
     if true {
     } else if { //~ ERROR missing condition
+    //~^ ERROR mismatched types
     }
     bar();
 }
diff --git a/src/test/ui/issues/issue-13483.stderr b/src/test/ui/issues/issue-13483.stderr
index df9f1dd0115db..5fd05b18ce06e 100644
--- a/src/test/ui/issues/issue-13483.stderr
+++ b/src/test/ui/issues/issue-13483.stderr
@@ -5,10 +5,29 @@ LL |     } else if {
    |              ^ expected if condition here
 
 error: missing condition for `if` expression
-  --> $DIR/issue-13483.rs:10:14
+  --> $DIR/issue-13483.rs:11:14
    |
 LL |     } else if {
    |              ^ expected if condition here
 
-error: aborting due to 2 previous errors
+error[E0308]: mismatched types
+  --> $DIR/issue-13483.rs:3:15
+   |
+LL |       } else if {
+   |  _______________^
+LL | |
+LL | |     } else {
+   | |_____^ expected `bool`, found `()`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-13483.rs:11:15
+   |
+LL |       } else if {
+   |  _______________^
+LL | |
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+
+error: aborting due to 4 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/issues/issue-39848.stderr b/src/test/ui/issues/issue-39848.stderr
index 47aa8e17a304b..11b145d6e0dc9 100644
--- a/src/test/ui/issues/issue-39848.stderr
+++ b/src/test/ui/issues/issue-39848.stderr
@@ -4,7 +4,7 @@ error: expected `{`, found `foo`
 LL |         if $tgt.has_$field() {}
    |         --                -- help: try placing this code inside a block: `{ () }`
    |         |
-   |         this `if` statement has a condition, but no block
+   |         this `if` expression has a condition, but no block
 ...
 LL |     get_opt!(bar, foo);
    |                   ^^^ expected `{`
diff --git a/src/test/ui/issues/issue-51602.stderr b/src/test/ui/issues/issue-51602.stderr
index 1ad69c0191b53..d800890bca38d 100644
--- a/src/test/ui/issues/issue-51602.stderr
+++ b/src/test/ui/issues/issue-51602.stderr
@@ -4,7 +4,7 @@ error: expected `{`, found keyword `in`
 LL |     if i in 1..10 {
    |     --   ^^ expected `{`
    |     |
-   |     this `if` statement has a condition, but no block
+   |     this `if` expression has a condition, but no block
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-61858.stderr b/src/test/ui/issues/issue-61858.stderr
index ea2ec3d013f59..8b95d9c6ae48b 100644
--- a/src/test/ui/issues/issue-61858.stderr
+++ b/src/test/ui/issues/issue-61858.stderr
@@ -4,7 +4,7 @@ error: expected `{`, found `)`
 LL |     (if foobar)
    |      --       ^ expected `{`
    |      |
-   |      this `if` statement has a condition, but no block
+   |      this `if` expression has a condition, but no block
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-62554.stderr b/src/test/ui/issues/issue-62554.stderr
index 87aaa03661321..d59546e23839a 100644
--- a/src/test/ui/issues/issue-62554.stderr
+++ b/src/test/ui/issues/issue-62554.stderr
@@ -17,7 +17,7 @@ error: expected `{`, found `macro_rules`
 LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
    |                 --    ^^^^^^^^^^^ expected `{`
    |                 |
-   |                 this `if` statement has a condition, but no block
+   |                 this `if` expression has a condition, but no block
    |
 help: try placing this code inside a block
    |
diff --git a/src/test/ui/label/label_break_value_illegal_uses.stderr b/src/test/ui/label/label_break_value_illegal_uses.stderr
index 0036f0f1db0fb..46b53c65b4817 100644
--- a/src/test/ui/label/label_break_value_illegal_uses.stderr
+++ b/src/test/ui/label/label_break_value_illegal_uses.stderr
@@ -12,7 +12,7 @@ LL |     if true 'b: {}
    |     |       |
    |     |       expected `{`
    |     |       help: try placing this code inside a block: `{ 'b: {} }`
-   |     this `if` statement has a condition, but no block
+   |     this `if` expression has a condition, but no block
 
 error: expected `{`, found `'b`
   --> $DIR/label_break_value_illegal_uses.rs:14:21
diff --git a/src/test/ui/missing/missing-block-hint.stderr b/src/test/ui/missing/missing-block-hint.stderr
index ee86a3241e822..0f635817bf462 100644
--- a/src/test/ui/missing/missing-block-hint.stderr
+++ b/src/test/ui/missing/missing-block-hint.stderr
@@ -4,13 +4,13 @@ error: expected `{`, found `=>`
 LL |         if (foo) => {}
    |         --       ^^ expected `{`
    |         |
-   |         this `if` statement has a condition, but no block
+   |         this `if` expression has a condition, but no block
 
 error: expected `{`, found `bar`
   --> $DIR/missing-block-hint.rs:7:13
    |
 LL |         if (foo)
-   |         -- this `if` statement has a condition, but no block
+   |         -- this `if` expression has a condition, but no block
 LL |             bar;
    |             ^^^-
    |             |
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs
deleted file mode 100644
index e5ac59ae46301..0000000000000
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
-//~^ ERROR unexpected token: `#`
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr
deleted file mode 100644
index ca1043250ba78..0000000000000
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: unexpected token: `#`
-  --> $DIR/attr-stmt-expr-attr-bad-2.rs:1:34
-   |
-LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
-   |                                  ^
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs
deleted file mode 100644
index 7dc71af52f4d9..0000000000000
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
-//~^ ERROR unexpected token: `#`
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr
deleted file mode 100644
index ab9366d042a2c..0000000000000
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: unexpected token: `#`
-  --> $DIR/attr-stmt-expr-attr-bad-3.rs:1:34
-   |
-LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
-   |                                  ^
-
-error: aborting due to previous error
-
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 ef10010ed0e61..6e1d72cd2f6e2 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
@@ -101,6 +101,13 @@ fn main() {}
 //~^ ERROR `X..=` range patterns are not supported
 //~| ERROR expected one of `=>`, `if`, or `|`, found `#`
 
+#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
+//~^ ERROR unexpected token: `#`
+//~| ERROR expected one of `.`
+#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
+//~^ ERROR unexpected token: `#`
+//~| ERROR expected one of `.`
+
 // make sure we don't catch this bug again...
 #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
 //~^ ERROR expected statement after outer attribute
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 9a0d3176714fa..371d3f575a418 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
@@ -149,7 +149,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
    |                                --   ^       --- help: try placing this code inside a block: `{ {}; }`
    |                                |    |
    |                                |    expected `{`
-   |                                this `if` statement has a condition, but no block
+   |                                this `if` expression has a condition, but no block
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:43:38
@@ -202,7 +202,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
    |                                             --   ^       --- help: try placing this code inside a block: `{ {}; }`
    |                                             |    |
    |                                             |    expected `{`
-   |                                             this `if` statement has a condition, but no block
+   |                                             this `if` expression has a condition, but no block
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:56:51
@@ -225,7 +225,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
    |                                --           ^       --- help: try placing this code inside a block: `{ {}; }`
    |                                |            |
    |                                |            expected `{`
-   |                                this `if` statement has a condition, but no block
+   |                                this `if` expression has a condition, but no block
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:62:46
@@ -278,7 +278,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}
    |                                                     --           ^       --- help: try placing this code inside a block: `{ {}; }`
    |                                                     |            |
    |                                                     |            expected `{`
-   |                                                     this `if` statement has a condition, but no block
+   |                                                     this `if` expression has a condition, but no block
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:75:67
@@ -380,11 +380,35 @@ error: expected one of `=>`, `if`, or `|`, found `#`
 LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
    |                                      ^ expected one of `=>`, `if`, or `|`
 
+error: unexpected token: `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:104:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
+   |                                  ^
+
+error: expected one of `.`, `;`, `?`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:104:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
+   |                                  ^ expected one of `.`, `;`, `?`, or an operator
+
+error: unexpected token: `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:107:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
+   |                                  ^
+
+error: expected one of `.`, `;`, `?`, or an operator, found `#`
+  --> $DIR/attr-stmt-expr-attr-bad.rs:107:34
+   |
+LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
+   |                                  ^ expected one of `.`, `;`, `?`, or an operator
+
 error: expected statement after outer attribute
-  --> $DIR/attr-stmt-expr-attr-bad.rs:105:44
+  --> $DIR/attr-stmt-expr-attr-bad.rs:112:44
    |
 LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
    |                                            ^
 
-error: aborting due to 52 previous errors
+error: aborting due to 56 previous errors
 
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 6bcb77385d7d5..a720dd68bd037 100644
--- a/src/test/ui/parser/doc-comment-in-if-statement.stderr
+++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr
@@ -4,7 +4,7 @@ error: expected `{`, found doc comment `/*!*/`
 LL |     if true /*!*/ {}
    |     --      ^^^^^ expected `{`
    |     |
-   |     this `if` statement has a condition, but no block
+   |     this `if` expression has a condition, but no block
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/while-let.stderr b/src/test/ui/while-let.stderr
index 172874ad5fc5c..09f0d641de060 100644
--- a/src/test/ui/while-let.stderr
+++ b/src/test/ui/while-let.stderr
@@ -2,7 +2,7 @@ warning: irrefutable while-let pattern
   --> $DIR/while-let.rs:7:13
    |
 LL |               while let $p = $e $b
-   |               ^^^^^
+   |               ^^^^^^^^^^^^^^^^^^^^
 ...
 LL | /     foo!(_a, 1, {
 LL | |         println!("irrefutable pattern");
@@ -15,7 +15,7 @@ warning: irrefutable while-let pattern
   --> $DIR/while-let.rs:7:13
    |
 LL |               while let $p = $e $b
-   |               ^^^^^
+   |               ^^^^^^^^^^^^^^^^^^^^
 ...
 LL | /     bar!(_a, 1, {
 LL | |         println!("irrefutable pattern");