diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d6840b20c89ac..a58d4f2a7c6a4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -142,7 +142,7 @@ the issue in question.
 Please make sure your pull request is in compliance with Rust's style
 guidelines by running
 
-    $ python x.py test src/tools/tidy
+    $ python x.py test tidy
 
 Make this check before every pull request (and every new commit in a pull
 request); you can add [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index c501378bff549..87da7327fe619 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -55,6 +55,9 @@ The script accepts commands, flags, and arguments to determine what to do:
   # run all unit tests
   ./x.py test
 
+  # execute tool tests
+  ./x.py test tidy
+
   # execute the UI test suite
   ./x.py test src/test/ui
 
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 0b2ab6bbbc021..d8831c6d9e566 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -359,7 +359,7 @@ Arguments:
                 subcommand_help.push_str(
                     "\n
 Arguments:
-    This subcommand accepts a number of paths to directories to tests that
+    This subcommand accepts a number of paths to test directories that
     should be compiled and run. For example:
 
         ./x.py test src/test/ui
@@ -372,6 +372,10 @@ Arguments:
     just like `build src/libstd --stage N` it tests the compiler produced by the previous
     stage.
 
+    Execute tool tests with a tool name argument:
+
+        ./x.py test tidy
+
     If no arguments are passed then the complete artifacts for that stage are
     compiled and tested.
 
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index f661b83042868..d1956270f135f 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -2626,13 +2626,21 @@ impl<T: Clone> Clone for IntoIter<T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl<#[may_dangle] T> Drop for IntoIter<T> {
     fn drop(&mut self) {
+        struct DropGuard<'a, T>(&'a mut IntoIter<T>);
+
+        impl<T> Drop for DropGuard<'_, T> {
+            fn drop(&mut self) {
+                // RawVec handles deallocation
+                let _ = unsafe { RawVec::from_raw_parts(self.0.buf.as_ptr(), self.0.cap) };
+            }
+        }
+
+        let guard = DropGuard(self);
         // destroy the remaining elements
         unsafe {
-            ptr::drop_in_place(self.as_mut_slice());
+            ptr::drop_in_place(guard.0.as_mut_slice());
         }
-
-        // RawVec handles deallocation
-        let _ = unsafe { RawVec::from_raw_parts(self.buf.as_ptr(), self.cap) };
+        // now `guard` will be dropped and do the rest
     }
 }
 
diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs
index 76e58f0cc62bc..04af5b5f76828 100644
--- a/src/libcore/macros/mod.rs
+++ b/src/libcore/macros/mod.rs
@@ -1,3 +1,4 @@
+#[cfg(bootstrap)]
 #[doc(include = "panic.md")]
 #[macro_export]
 #[allow_internal_unstable(core_panic, track_caller)]
@@ -20,6 +21,26 @@ macro_rules! panic {
     );
 }
 
+#[cfg(not(bootstrap))]
+#[doc(include = "panic.md")]
+#[macro_export]
+#[allow_internal_unstable(core_panic, track_caller)]
+#[stable(feature = "core", since = "1.6.0")]
+macro_rules! panic {
+    () => (
+        $crate::panic!("explicit panic")
+    );
+    ($msg:expr) => (
+        $crate::panicking::panic($msg)
+    );
+    ($msg:expr,) => (
+        $crate::panic!($msg)
+    );
+    ($fmt:expr, $($arg:tt)+) => (
+        $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+))
+    );
+}
+
 /// Asserts that two expressions are equal to each other (using [`PartialEq`]).
 ///
 /// On panic, this macro will print the values of the expressions with their
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 61b764f2d6206..3587f3f0ebf56 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -32,6 +32,7 @@
 use crate::fmt;
 use crate::panic::{Location, PanicInfo};
 
+/// The underlying implementation of libcore's `panic!` macro when no formatting is used.
 #[cold]
 // never inline unless panic_immediate_abort to avoid code
 // bloat at the call sites as much as possible
@@ -49,9 +50,28 @@ pub fn panic(expr: &str) -> ! {
     // truncation and padding (even though none is used here). Using
     // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
     // output binary, saving up to a few kilobytes.
-    panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), Location::caller())
+    #[cfg(not(bootstrap))]
+    panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
+    #[cfg(bootstrap)]
+    panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), Location::caller());
 }
 
+#[cfg(not(bootstrap))]
+#[cold]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[track_caller]
+#[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        unsafe { super::intrinsics::abort() }
+    }
+
+    panic!("index out of bounds: the len is {} but the index is {}", len, index)
+}
+
+// For bootstrap, we need a variant with the old argument order, and a corresponding
+// `panic_fmt`.
+#[cfg(bootstrap)]
 #[cold]
 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
 #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access
@@ -66,10 +86,12 @@ fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! {
     )
 }
 
+/// The underlying implementation of libcore's `panic!` macro when formatting is used.
 #[cold]
 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
 #[cfg_attr(feature = "panic_immediate_abort", inline)]
-pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! {
+#[cfg_attr(not(bootstrap), track_caller)]
+pub fn panic_fmt(fmt: fmt::Arguments<'_>, #[cfg(bootstrap)] location: &Location<'_>) -> ! {
     if cfg!(feature = "panic_immediate_abort") {
         unsafe { super::intrinsics::abort() }
     }
@@ -81,6 +103,10 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! {
         fn panic_impl(pi: &PanicInfo<'_>) -> !;
     }
 
+    #[cfg(bootstrap)]
     let pi = PanicInfo::internal_constructor(Some(&fmt), location);
+    #[cfg(not(bootstrap))]
+    let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller());
+
     unsafe { panic_impl(&pi) }
 }
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index b46095927b709..0b33408edf02d 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -5,15 +5,18 @@ use crate::mir;
 use crate::mir::interpret::ConstValue;
 use crate::ty::layout::{Align, LayoutError, Size};
 use crate::ty::query::TyCtxtAt;
+use crate::ty::tls;
 use crate::ty::{self, layout, Ty};
 
 use backtrace::Backtrace;
+use rustc_data_structures::sync::Lock;
 use rustc_errors::{struct_span_err, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_macros::HashStable;
+use rustc_session::CtfeBacktrace;
 use rustc_span::{Pos, Span};
 use rustc_target::spec::abi::Abi;
-use std::{any::Any, env, fmt};
+use std::{any::Any, fmt};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
 pub enum ErrorHandled {
@@ -257,21 +260,25 @@ impl From<ErrorHandled> for InterpErrorInfo<'_> {
 
 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
     fn from(kind: InterpError<'tcx>) -> Self {
-        let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") {
-            // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
-            Ok(ref val) if val != "0" => {
-                let mut backtrace = Backtrace::new_unresolved();
+        let capture_backtrace = tls::with_context_opt(|ctxt| {
+            if let Some(ctxt) = ctxt {
+                *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
+            } else {
+                CtfeBacktrace::Disabled
+            }
+        });
 
-                if val == "immediate" {
-                    // Print it now.
-                    print_backtrace(&mut backtrace);
-                    None
-                } else {
-                    Some(Box::new(backtrace))
-                }
+        let backtrace = match capture_backtrace {
+            CtfeBacktrace::Disabled => None,
+            CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
+            CtfeBacktrace::Immediate => {
+                // Print it now.
+                let mut backtrace = Backtrace::new_unresolved();
+                print_backtrace(&mut backtrace);
+                None
             }
-            _ => None,
         };
+
         InterpErrorInfo { kind, backtrace }
     }
 }
diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs
index b67b7d346f756..3fc6444168e24 100644
--- a/src/librustc_ast/token.rs
+++ b/src/librustc_ast/token.rs
@@ -535,6 +535,16 @@ impl Token {
         false
     }
 
+    // Is the token an interpolated block (`$b:block`)?
+    pub fn is_whole_block(&self) -> bool {
+        if let Interpolated(ref nt) = self.kind {
+            if let NtBlock(..) = **nt {
+                return true;
+            }
+        }
+        false
+    }
+
     /// Returns `true` if the token is either the `mut` or `const` keyword.
     pub fn is_mutability(&self) -> bool {
         self.is_keyword(kw::Mut) || self.is_keyword(kw::Const)
diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs
index 821377db0ee15..4e7bc9fa0e2ae 100644
--- a/src/librustc_codegen_llvm/allocator.rs
+++ b/src/librustc_codegen_llvm/allocator.rs
@@ -1,5 +1,3 @@
-use std::ffi::CString;
-
 use crate::attributes;
 use libc::c_uint;
 use rustc::bug;
@@ -50,8 +48,8 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc
             args.len() as c_uint,
             False,
         );
-        let name = CString::new(format!("__rust_{}", method.name)).unwrap();
-        let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr(), ty);
+        let name = format!("__rust_{}", method.name);
+        let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
 
         if tcx.sess.target.target.options.default_hidden_visibility {
             llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
@@ -60,8 +58,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc
             attributes::emit_uwtable(llfn, true);
         }
 
-        let callee = CString::new(kind.fn_name(method.name)).unwrap();
-        let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr(), ty);
+        let callee = kind.fn_name(method.name);
+        let callee =
+            llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
         llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
 
         let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
@@ -73,14 +72,8 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc
             .enumerate()
             .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
             .collect::<Vec<_>>();
-        let ret = llvm::LLVMRustBuildCall(
-            llbuilder,
-            callee,
-            args.as_ptr(),
-            args.len() as c_uint,
-            None,
-            "\0".as_ptr().cast(),
-        );
+        let ret =
+            llvm::LLVMRustBuildCall(llbuilder, callee, args.as_ptr(), args.len() as c_uint, None);
         llvm::LLVMSetTailCall(ret, True);
         if output.is_some() {
             llvm::LLVMBuildRet(llbuilder, ret);
diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs
index 7975a70ab269c..6edc3d5ecd477 100644
--- a/src/librustc_codegen_llvm/asm.rs
+++ b/src/librustc_codegen_llvm/asm.rs
@@ -12,7 +12,6 @@ use rustc_span::Span;
 
 use libc::{c_char, c_uint};
 use log::debug;
-use std::ffi::{CStr, CString};
 
 impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
     fn codegen_inline_asm(
@@ -80,12 +79,11 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
             _ => self.type_struct(&output_types, false),
         };
 
-        let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap();
-        let constraint_cstr = CString::new(all_constraints).unwrap();
+        let asm = ia.asm.as_str();
         let r = inline_asm_call(
             self,
             &asm,
-            &constraint_cstr,
+            &all_constraints,
             &inputs,
             output_type,
             ia.volatile,
@@ -125,17 +123,17 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
 
 impl AsmMethods for CodegenCx<'ll, 'tcx> {
     fn codegen_global_asm(&self, ga: &hir::GlobalAsm) {
-        let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap();
+        let asm = ga.asm.as_str();
         unsafe {
-            llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr());
+            llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr().cast(), asm.len());
         }
     }
 }
 
 fn inline_asm_call(
     bx: &mut Builder<'a, 'll, 'tcx>,
-    asm: &CStr,
-    cons: &CStr,
+    asm: &str,
+    cons: &str,
     inputs: &[&'ll Value],
     output: &'ll llvm::Type,
     volatile: bool,
@@ -157,13 +155,15 @@ fn inline_asm_call(
     let fty = bx.cx.type_func(&argtys[..], output);
     unsafe {
         // Ask LLVM to verify that the constraints are well-formed.
-        let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr());
+        let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len());
         debug!("constraint verification result: {:?}", constraints_ok);
         if constraints_ok {
             let v = llvm::LLVMRustInlineAsm(
                 fty,
-                asm.as_ptr(),
-                cons.as_ptr(),
+                asm.as_ptr().cast(),
+                asm.len(),
+                cons.as_ptr().cast(),
+                cons.len(),
                 volatile,
                 alignstack,
                 llvm::AsmDialect::from_generic(dia),
diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs
index 32604d5601d30..1c5987f26f129 100644
--- a/src/librustc_codegen_llvm/builder.rs
+++ b/src/librustc_codegen_llvm/builder.rs
@@ -1016,7 +1016,6 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                 args.as_ptr() as *const &llvm::Value,
                 args.len() as c_uint,
                 bundle,
-                UNNAMED,
             )
         }
     }
diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs
index 691f32dd85a05..fab6321186b2c 100644
--- a/src/librustc_codegen_llvm/declare.rs
+++ b/src/librustc_codegen_llvm/declare.rs
@@ -21,7 +21,6 @@ use crate::value::Value;
 use log::debug;
 use rustc::ty::Ty;
 use rustc_codegen_ssa::traits::*;
-use rustc_data_structures::small_c_str::SmallCStr;
 
 /// Declare a function.
 ///
@@ -34,8 +33,9 @@ fn declare_raw_fn(
     ty: &'ll Type,
 ) -> &'ll Value {
     debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty);
-    let namebuf = SmallCStr::new(name);
-    let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty) };
+    let llfn = unsafe {
+        llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_ptr().cast(), name.len(), ty)
+    };
 
     llvm::SetFunctionCallConv(llfn, callconv);
     // Function addresses in Rust are never significant, allowing functions to
@@ -83,8 +83,7 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 
     fn get_declared_value(&self, name: &str) -> Option<&'ll Value> {
         debug!("get_declared_value(name={:?})", name);
-        let namebuf = SmallCStr::new(name);
-        unsafe { llvm::LLVMRustGetNamedValue(self.llmod, namebuf.as_ptr()) }
+        unsafe { llvm::LLVMRustGetNamedValue(self.llmod, name.as_ptr().cast(), name.len()) }
     }
 
     fn get_defined_value(&self, name: &str) -> Option<&'ll Value> {
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 31a0f52809088..388b6c7483958 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -732,7 +732,7 @@ extern "C" {
 
     /// See Module::setModuleInlineAsm.
     pub fn LLVMSetModuleInlineAsm(M: &Module, Asm: *const c_char);
-    pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char);
+    pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t);
 
     /// See llvm::LLVMTypeKind::getTypeID.
     pub fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind;
@@ -879,13 +879,18 @@ extern "C" {
     pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode);
     pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
     pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
-    pub fn LLVMRustGetNamedValue(M: &Module, Name: *const c_char) -> Option<&Value>;
+    pub fn LLVMRustGetNamedValue(
+        M: &Module,
+        Name: *const c_char,
+        NameLen: size_t,
+    ) -> Option<&Value>;
     pub fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
 
     // Operations on functions
     pub fn LLVMRustGetOrInsertFunction(
         M: &'a Module,
         Name: *const c_char,
+        NameLen: size_t,
         FunctionTy: &'a Type,
     ) -> &'a Value;
     pub fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint);
@@ -1332,7 +1337,6 @@ extern "C" {
         Args: *const &'a Value,
         NumArgs: c_uint,
         Bundle: Option<&OperandBundleDef<'a>>,
-        Name: *const c_char,
     ) -> &'a Value;
     pub fn LLVMRustBuildMemCpy(
         B: &Builder<'a>,
@@ -1581,12 +1585,18 @@ extern "C" {
     pub fn LLVMRustInlineAsm(
         Ty: &Type,
         AsmString: *const c_char,
+        AsmStringLen: size_t,
         Constraints: *const c_char,
+        ConstraintsLen: size_t,
         SideEffects: Bool,
         AlignStack: Bool,
         Dialect: AsmDialect,
     ) -> &Value;
-    pub fn LLVMRustInlineAsmVerify(Ty: &Type, Constraints: *const c_char) -> bool;
+    pub fn LLVMRustInlineAsmVerify(
+        Ty: &Type,
+        Constraints: *const c_char,
+        ConstraintsLen: size_t,
+    ) -> bool;
 
     pub fn LLVMRustDebugMetadataVersion() -> u32;
     pub fn LLVMRustVersionMajor() -> u32;
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index c8d352cd2dd98..9ce23ff5f3fc8 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -415,11 +415,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             AssertKind::BoundsCheck { ref len, ref index } => {
                 let len = self.codegen_operand(&mut bx, len).immediate();
                 let index = self.codegen_operand(&mut bx, index).immediate();
-                (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len])
+                // It's `fn panic_bounds_check(index: usize, len: usize)`,
+                // and `#[track_caller]` adds an implicit third argument.
+                (lang_items::PanicBoundsCheckFnLangItem, vec![index, len, location])
             }
             _ => {
                 let msg_str = Symbol::intern(msg.description());
                 let msg = bx.const_str(msg_str);
+                // It's `pub fn panic(expr: &str)`, with the wide reference being passed
+                // as two arguments, and `#[track_caller]` adds an implicit third argument.
                 (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location])
             }
         };
diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs
index 11d1209923fb2..0e14e2196caf0 100644
--- a/src/librustc_error_codes/error_codes.rs
+++ b/src/librustc_error_codes/error_codes.rs
@@ -411,6 +411,7 @@ E0735: include_str!("./error_codes/E0735.md"),
 E0736: include_str!("./error_codes/E0736.md"),
 E0737: include_str!("./error_codes/E0737.md"),
 E0738: include_str!("./error_codes/E0738.md"),
+E0739: include_str!("./error_codes/E0739.md"),
 E0740: include_str!("./error_codes/E0740.md"),
 E0741: include_str!("./error_codes/E0741.md"),
 E0742: include_str!("./error_codes/E0742.md"),
@@ -610,5 +611,4 @@ E0748: include_str!("./error_codes/E0748.md"),
     E0722, // Malformed `#[optimize]` attribute
     E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
     E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
-    E0739, // invalid track_caller application/syntax
 }
diff --git a/src/librustc_error_codes/error_codes/E0739.md b/src/librustc_error_codes/error_codes/E0739.md
new file mode 100644
index 0000000000000..707751066edbc
--- /dev/null
+++ b/src/librustc_error_codes/error_codes/E0739.md
@@ -0,0 +1,13 @@
+`#[track_caller]` can not be applied on struct.
+
+Erroneous code example:
+
+```compile_fail,E0739
+#![feature(track_caller)]
+#[track_caller]
+struct Bar {
+    a: u8,
+}
+```
+
+[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md
diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs
index ed8029834680c..bb661d3d2a30a 100644
--- a/src/librustc_mir/const_eval/machine.rs
+++ b/src/librustc_mir/const_eval/machine.rs
@@ -280,7 +280,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
 
     fn assert_panic(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _span: Span,
         msg: &AssertMessage<'tcx>,
         _unwind: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx> {
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 6615cc608fb54..087517ff4e31d 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -165,7 +165,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Called to evaluate `Assert` MIR terminators that trigger a panic.
     fn assert_panic(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        span: Span,
         msg: &mir::AssertMessage<'tcx>,
         unwind: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx>;
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index ea8378574a3e0..b5c34daf8a318 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -95,7 +95,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 if expected == cond_val {
                     self.go_to_block(target);
                 } else {
-                    M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
+                    M::assert_panic(self, msg, cleanup)?;
                 }
             }
 
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index a07c8575b300c..f9682a77173f5 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -197,7 +197,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
 
     fn assert_panic(
         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        _span: Span,
         _msg: &rustc::mir::AssertMessage<'tcx>,
         _unwind: Option<rustc::mir::BasicBlock>,
     ) -> InterpResult<'tcx> {
diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs
index 10d524776a11b..fe75062ee503c 100644
--- a/src/librustc_parse/lib.rs
+++ b/src/librustc_parse/lib.rs
@@ -2,6 +2,7 @@
 
 #![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
+#![feature(bindings_after_at)]
 
 use rustc_ast::ast;
 use rustc_ast::token::{self, Nonterminal};
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index bdd78e671a8b3..b56dd30739dae 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 16ea2773b2009..7f6f90431fc94 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 a9c4de04c0a2e..3932bbd7564c3 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 f52a91ff5989d..5f2b3b03488b4 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 3864ec3aaa163..489549a57505f 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 {
@@ -300,21 +292,28 @@ impl<'a> Parser<'a> {
     /// Parses a block. Inner attributes are allowed.
     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 16adf5c05a4ee..3dd415bf37289 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))
diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs
index 173b120e1f6d7..8cda95783a8a9 100644
--- a/src/librustc_session/session.rs
+++ b/src/librustc_session/session.rs
@@ -49,6 +49,18 @@ pub struct OptimizationFuel {
     out_of_fuel: bool,
 }
 
+/// The behavior of the CTFE engine when an error occurs with regards to backtraces.
+#[derive(Clone, Copy)]
+pub enum CtfeBacktrace {
+    /// Do nothing special, return the error as usual without a backtrace.
+    Disabled,
+    /// Capture a backtrace at the point the error is created and return it in the error
+    /// (to be printed later if/when the error ever actually gets shown to the user).
+    Capture,
+    /// Capture a backtrace at the point the error is created and immediately print it out.
+    Immediate,
+}
+
 /// Represents the data associated with a compilation
 /// session for a single crate.
 pub struct Session {
@@ -139,6 +151,11 @@ pub struct Session {
     /// Path for libraries that will take preference over libraries shipped by Rust.
     /// Used by windows-gnu targets to priortize system mingw-w64 libraries.
     pub system_library_path: OneThread<RefCell<Option<Option<PathBuf>>>>,
+
+    /// Tracks the current behavior of the CTFE engine when an error occurs.
+    /// Options range from returning the error without a backtrace to returning an error
+    /// and immediately printing the backtrace to stderr.
+    pub ctfe_backtrace: Lock<CtfeBacktrace>,
 }
 
 pub struct PerfStats {
@@ -1040,6 +1057,12 @@ fn build_session_(
         sopts.debugging_opts.time_passes,
     );
 
+    let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") {
+        Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate,
+        Ok(ref val) if val != "0" => CtfeBacktrace::Capture,
+        _ => CtfeBacktrace::Disabled,
+    });
+
     let sess = Session {
         target: target_cfg,
         host,
@@ -1078,6 +1101,7 @@ fn build_session_(
         trait_methods_not_found: Lock::new(Default::default()),
         confused_type_with_std_module: Lock::new(Default::default()),
         system_library_path: OneThread::new(RefCell::new(Default::default())),
+        ctfe_backtrace,
     };
 
     validate_commandline_args_with_session_available(&sess);
diff --git a/src/librustc_span/source_map.rs b/src/librustc_span/source_map.rs
index 65095c6f1317c..353f7b3f52bc3 100644
--- a/src/librustc_span/source_map.rs
+++ b/src/librustc_span/source_map.rs
@@ -517,6 +517,13 @@ impl SourceMap {
         Ok((lo, hi))
     }
 
+    pub fn is_line_before_span_empty(&self, sp: Span) -> bool {
+        match self.span_to_prev_source(sp) {
+            Ok(s) => s.split('\n').last().map(|l| l.trim_start().is_empty()).unwrap_or(false),
+            Err(_) => false,
+        }
+    }
+
     pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
         debug!("span_to_lines(sp={:?})", sp);
         let (lo, hi) = self.is_valid_span(sp)?;
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 33b3e800374e3..58c8a7d82bfb5 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -1,4 +1,5 @@
 use std::collections::BTreeMap;
+use std::convert::TryFrom;
 use std::ffi::OsStr;
 use std::fmt;
 use std::path::PathBuf;
@@ -24,6 +25,33 @@ use crate::opts;
 use crate::passes::{self, Condition, DefaultPassOption};
 use crate::theme;
 
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum OutputFormat {
+    Json,
+    Html,
+}
+
+impl OutputFormat {
+    pub fn is_json(&self) -> bool {
+        match self {
+            OutputFormat::Json => true,
+            _ => false,
+        }
+    }
+}
+
+impl TryFrom<&str> for OutputFormat {
+    type Error = String;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        match value {
+            "json" => Ok(OutputFormat::Json),
+            "html" => Ok(OutputFormat::Html),
+            _ => Err(format!("unknown output format `{}`", value)),
+        }
+    }
+}
+
 /// Configuration options for rustdoc.
 #[derive(Clone)]
 pub struct Options {
@@ -115,6 +143,8 @@ pub struct Options {
     pub crate_version: Option<String>,
     /// Collected options specific to outputting final pages.
     pub render_options: RenderOptions,
+    /// Output format rendering (used only for "show-coverage" option for the moment)
+    pub output_format: Option<OutputFormat>,
 }
 
 impl fmt::Debug for Options {
@@ -425,14 +455,6 @@ impl Options {
             }
         }
 
-        match matches.opt_str("w").as_ref().map(|s| &**s) {
-            Some("html") | None => {}
-            Some(s) => {
-                diag.struct_err(&format!("unknown output format: {}", s)).emit();
-                return Err(1);
-            }
-        }
-
         let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s));
         if let Some(ref index_page) = index_page {
             if !index_page.is_file() {
@@ -469,6 +491,29 @@ impl Options {
             }
         };
 
+        let output_format = match matches.opt_str("output-format") {
+            Some(s) => match OutputFormat::try_from(s.as_str()) {
+                Ok(o) => {
+                    if o.is_json() && !show_coverage {
+                        diag.struct_err("json output format isn't supported for doc generation")
+                            .emit();
+                        return Err(1);
+                    } else if !o.is_json() && show_coverage {
+                        diag.struct_err(
+                            "html output format isn't supported for the --show-coverage option",
+                        )
+                        .emit();
+                        return Err(1);
+                    }
+                    Some(o)
+                }
+                Err(e) => {
+                    diag.struct_err(&e).emit();
+                    return Err(1);
+                }
+            },
+            None => None,
+        };
         let crate_name = matches.opt_str("crate-name");
         let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
         let playground_url = matches.opt_str("playground-url");
@@ -553,6 +598,7 @@ impl Options {
                 generate_search_filter,
                 generate_redirect_pages,
             },
+            output_format,
         })
     }
 
@@ -568,6 +614,9 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han
 
     for flag in deprecated_flags.iter() {
         if matches.opt_present(flag) {
+            if *flag == "output-format" && matches.opt_present("show-coverage") {
+                continue;
+            }
             let mut err =
                 diag.struct_warn(&format!("the '{}' flag is considered deprecated", flag));
             err.warn(
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 8bc34e949f175..b9ae3d53afc04 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -228,6 +228,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
         mut manual_passes,
         display_warnings,
         render_options,
+        output_format,
         ..
     } = options;
 
@@ -385,6 +386,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
 
                 let mut renderinfo = RenderInfo::default();
                 renderinfo.access_levels = access_levels;
+                renderinfo.output_format = output_format;
 
                 let mut ctxt = DocContext {
                     tcx,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index d3015b403fc4c..2d1e3c29055cf 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -60,7 +60,7 @@ use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 
 use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy};
-use crate::config::RenderOptions;
+use crate::config::{OutputFormat, RenderOptions};
 use crate::docfs::{DocFS, ErrorStorage, PathError};
 use crate::doctree;
 use crate::html::escape::Escape;
@@ -270,6 +270,7 @@ pub struct RenderInfo {
     pub deref_trait_did: Option<DefId>,
     pub deref_mut_trait_did: Option<DefId>,
     pub owned_box_did: Option<DefId>,
+    pub output_format: Option<OutputFormat>,
 }
 
 // Helper structs for rendering items/sidebars and carrying along contextual
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index a0a35e4ce4b85..4198369eca8f5 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -139,6 +139,7 @@ impl Cache {
             deref_trait_did,
             deref_mut_trait_did,
             owned_box_did,
+            ..
         } = renderinfo;
 
         let external_paths =
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index d4a7f3313a49b..f48224512ba4f 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -1,4 +1,5 @@
 use crate::clean;
+use crate::config::OutputFormat;
 use crate::core::DocContext;
 use crate::fold::{self, DocFolder};
 use crate::passes::Pass;
@@ -6,6 +7,8 @@ use crate::passes::Pass;
 use rustc_ast::attr;
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
+use serde::Serialize;
+use serde_json;
 
 use std::collections::BTreeMap;
 use std::ops;
@@ -16,16 +19,16 @@ pub const CALCULATE_DOC_COVERAGE: Pass = Pass {
     description: "counts the number of items with and without documentation",
 };
 
-fn calculate_doc_coverage(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
-    let mut calc = CoverageCalculator::default();
+fn calculate_doc_coverage(krate: clean::Crate, ctx: &DocContext<'_>) -> clean::Crate {
+    let mut calc = CoverageCalculator::new();
     let krate = calc.fold_crate(krate);
 
-    calc.print_results();
+    calc.print_results(ctx.renderinfo.borrow().output_format);
 
     krate
 }
 
-#[derive(Default, Copy, Clone)]
+#[derive(Default, Copy, Clone, Serialize)]
 struct ItemCount {
     total: u64,
     with_docs: u64,
@@ -64,13 +67,41 @@ impl ops::AddAssign for ItemCount {
     }
 }
 
-#[derive(Default)]
 struct CoverageCalculator {
     items: BTreeMap<FileName, ItemCount>,
 }
 
+fn limit_filename_len(filename: String) -> String {
+    let nb_chars = filename.chars().count();
+    if nb_chars > 35 {
+        "...".to_string()
+            + &filename[filename.char_indices().nth(nb_chars - 32).map(|x| x.0).unwrap_or(0)..]
+    } else {
+        filename
+    }
+}
+
 impl CoverageCalculator {
-    fn print_results(&self) {
+    fn new() -> CoverageCalculator {
+        CoverageCalculator { items: Default::default() }
+    }
+
+    fn to_json(&self) -> String {
+        serde_json::to_string(
+            &self
+                .items
+                .iter()
+                .map(|(k, v)| (k.to_string(), v))
+                .collect::<BTreeMap<String, &ItemCount>>(),
+        )
+        .expect("failed to convert JSON data to string")
+    }
+
+    fn print_results(&self, output_format: Option<OutputFormat>) {
+        if output_format.map(|o| o.is_json()).unwrap_or_else(|| false) {
+            println!("{}", self.to_json());
+            return;
+        }
         let mut total = ItemCount::default();
 
         fn print_table_line() {
@@ -93,15 +124,7 @@ impl CoverageCalculator {
 
         for (file, &count) in &self.items {
             if let Some(percentage) = count.percentage() {
-                let mut name = file.to_string();
-                // if a filename is too long, shorten it so we don't blow out the table
-                // FIXME(misdreavus): this needs to count graphemes, and probably also track
-                // double-wide characters...
-                if name.len() > 35 {
-                    name = "...".to_string() + &name[name.len() - 32..];
-                }
-
-                print_table_record(&name, count, percentage);
+                print_table_record(&limit_filename_len(file.to_string()), count, percentage);
 
                 total += count;
             }
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index aeddd4cfb9fe9..25cfee3373dc4 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -112,20 +112,22 @@ extern "C" void LLVMRustPrintPassTimings() {
   TimerGroup::printAll(OS);
 }
 
-extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M,
-                                              const char *Name) {
-  return wrap(unwrap(M)->getNamedValue(Name));
+extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
+                                              size_t NameLen) {
+  return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen)));
 }
 
 extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
                                                     const char *Name,
+                                                    size_t NameLen,
                                                     LLVMTypeRef FunctionTy) {
-  return wrap(
-      unwrap(M)->getOrInsertFunction(Name, unwrap<FunctionType>(FunctionTy))
+  return wrap(unwrap(M)
+                  ->getOrInsertFunction(StringRef(Name, NameLen),
+                                        unwrap<FunctionType>(FunctionTy))
 #if LLVM_VERSION_GE(9, 0)
-      .getCallee()
+                  .getCallee()
 #endif
-      );
+  );
 }
 
 extern "C" LLVMValueRef
@@ -395,22 +397,26 @@ static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) {
   }
 }
 
-extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString,
-                                          char *Constraints,
-                                          LLVMBool HasSideEffects,
-                                          LLVMBool IsAlignStack,
-                                          LLVMRustAsmDialect Dialect) {
-  return wrap(InlineAsm::get(unwrap<FunctionType>(Ty), AsmString, Constraints,
+extern "C" LLVMValueRef
+LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
+                  char *Constraints, size_t ConstraintsLen,
+                  LLVMBool HasSideEffects, LLVMBool IsAlignStack,
+                  LLVMRustAsmDialect Dialect) {
+  return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
+                             StringRef(AsmString, AsmStringLen),
+                             StringRef(Constraints, ConstraintsLen),
                              HasSideEffects, IsAlignStack, fromRust(Dialect)));
 }
 
-extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty,
-                                          char *Constraints) {
-  return InlineAsm::Verify(unwrap<FunctionType>(Ty), Constraints);
+extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
+                                        size_t ConstraintsLen) {
+  return InlineAsm::Verify(unwrap<FunctionType>(Ty),
+                           StringRef(Constraints, ConstraintsLen));
 }
 
-extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
-  unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
+extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm,
+                                              size_t AsmLen) {
+  unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen));
 }
 
 typedef DIBuilder *LLVMRustDIBuilderRef;
@@ -1282,12 +1288,11 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
 
 extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn,
                                           LLVMValueRef *Args, unsigned NumArgs,
-                                          OperandBundleDef *Bundle,
-                                          const char *Name) {
+                                          OperandBundleDef *Bundle) {
   unsigned Len = Bundle ? 1 : 0;
   ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
   return wrap(unwrap(B)->CreateCall(
-      unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles, Name));
+      unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles));
 }
 
 extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
diff --git a/src/test/rustdoc-ui/coverage/html.rs b/src/test/rustdoc-ui/coverage/html.rs
new file mode 100644
index 0000000000000..181cb4c5061a7
--- /dev/null
+++ b/src/test/rustdoc-ui/coverage/html.rs
@@ -0,0 +1,4 @@
+// compile-flags:-Z unstable-options --output-format html --show-coverage
+
+/// Foo
+pub struct Xo;
diff --git a/src/test/rustdoc-ui/coverage/html.stderr b/src/test/rustdoc-ui/coverage/html.stderr
new file mode 100644
index 0000000000000..adca375d4bce5
--- /dev/null
+++ b/src/test/rustdoc-ui/coverage/html.stderr
@@ -0,0 +1,2 @@
+error: html output format isn't supported for the --show-coverage option
+
diff --git a/src/test/rustdoc-ui/coverage/json.rs b/src/test/rustdoc-ui/coverage/json.rs
new file mode 100644
index 0000000000000..b1220b32e9194
--- /dev/null
+++ b/src/test/rustdoc-ui/coverage/json.rs
@@ -0,0 +1,27 @@
+// build-pass
+// compile-flags:-Z unstable-options --output-format json --show-coverage
+
+pub mod foo {
+    /// Hello!
+    pub struct Foo;
+    /// Bar
+    pub enum Bar { A }
+}
+
+/// X
+pub struct X;
+
+/// Bar
+pub mod bar {
+    /// bar
+    pub struct Bar;
+    /// X
+    pub enum X { Y }
+}
+
+/// yolo
+pub enum Yolo { X }
+
+pub struct Xo<T: Clone> {
+    x: T,
+}
diff --git a/src/test/rustdoc-ui/coverage/json.stdout b/src/test/rustdoc-ui/coverage/json.stdout
new file mode 100644
index 0000000000000..63b22a7d94b00
--- /dev/null
+++ b/src/test/rustdoc-ui/coverage/json.stdout
@@ -0,0 +1 @@
+{"$DIR/json.rs":{"total":13,"with_docs":7}}
diff --git a/src/test/ui/did_you_mean/issue-40006.stderr b/src/test/ui/did_you_mean/issue-40006.stderr
index 613d7eee59480..76e87a3749c33 100644
--- a/src/test/ui/did_you_mean/issue-40006.stderr
+++ b/src/test/ui/did_you_mean/issue-40006.stderr
@@ -32,11 +32,11 @@ LL |     X() {}
 LL | }
    | - the item list ends here
 
-error: expected `[`, found `#`
+error: expected one of `!` or `[`, found `#`
   --> $DIR/issue-40006.rs:19:17
    |
 LL |     fn xxx() { ### }
-   |                 ^ expected `[`
+   |                 ^ expected one of `!` or `[`
 
 error: expected one of `!` or `::`, found `=`
   --> $DIR/issue-40006.rs:22:7
diff --git a/src/test/ui/generic-associated-types/empty_generics.rs b/src/test/ui/generic-associated-types/empty_generics.rs
index 522e23ca43d78..6eb25a92f3413 100644
--- a/src/test/ui/generic-associated-types/empty_generics.rs
+++ b/src/test/ui/generic-associated-types/empty_generics.rs
@@ -3,7 +3,7 @@
 
 trait Foo {
     type Bar<,>;
-    //~^ ERROR expected one of `>`, `const`, identifier, or lifetime, found `,`
+    //~^ ERROR expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
 }
 
 fn main() {}
diff --git a/src/test/ui/generic-associated-types/empty_generics.stderr b/src/test/ui/generic-associated-types/empty_generics.stderr
index bd5708d814037..1599d683ad6dd 100644
--- a/src/test/ui/generic-associated-types/empty_generics.stderr
+++ b/src/test/ui/generic-associated-types/empty_generics.stderr
@@ -1,10 +1,10 @@
-error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
   --> $DIR/empty_generics.rs:5:14
    |
 LL | trait Foo {
    |           - while parsing this item list starting here
 LL |     type Bar<,>;
-   |              ^ expected one of `>`, `const`, identifier, or lifetime
+   |              ^ expected one of `#`, `>`, `const`, identifier, or lifetime
 LL |
 LL | }
    | - the item list ends here
diff --git a/src/test/ui/if-attrs/else-attrs.rs b/src/test/ui/if-attrs/else-attrs.rs
index 4394b2100c1b5..85da7cf6bb8c3 100644
--- a/src/test/ui/if-attrs/else-attrs.rs
+++ b/src/test/ui/if-attrs/else-attrs.rs
@@ -8,7 +8,7 @@ fn if_else_parse_error() {
 #[cfg(FALSE)]
 fn else_attr_ifparse_error() {
     if true {
-    } else #[attr] if false { //~ ERROR expected
+    } else #[attr] if false { //~ ERROR outer attributes are not allowed
     } else {
     }
 }
diff --git a/src/test/ui/if-attrs/else-attrs.stderr b/src/test/ui/if-attrs/else-attrs.stderr
index af25b6abc0a3a..2733377054d7d 100644
--- a/src/test/ui/if-attrs/else-attrs.stderr
+++ b/src/test/ui/if-attrs/else-attrs.stderr
@@ -4,18 +4,17 @@ error: expected expression, found keyword `else`
 LL |     } #[attr] else if false {
    |               ^^^^ expected expression
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/else-attrs.rs:11:12
    |
-LL |     } else #[attr] if false {
-   |            ^ expected `{`
-   |
-help: try placing this code inside a block
-   |
-LL |     } else #[attr] { if false {
-LL |     } else {
-LL |     } }
-   |
+LL |       } else #[attr] if false {
+   |  _______----_^^^^^^^_-
+   | |       |    |
+   | |       |    help: remove the attributes
+   | |       the branch belongs to this `else`
+LL | |     } else {
+LL | |     }
+   | |_____- the attributes are attached to this branch
 
 error: expected expression, found keyword `else`
   --> $DIR/else-attrs.rs:20:15
diff --git a/src/test/ui/issues/issue-20616-8.rs b/src/test/ui/issues/issue-20616-8.rs
index c9e8b61e50b52..3ceb58d1252d3 100644
--- a/src/test/ui/issues/issue-20616-8.rs
+++ b/src/test/ui/issues/issue-20616-8.rs
@@ -29,7 +29,7 @@ type Type_5_<'a> = Type_1_<'a, ()>;
 
 
 type Type_8<'a,,> = &'a ();
-//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
 
 
 //type Type_9<T,,> = Box<T>; // error: expected identifier, found `,`
diff --git a/src/test/ui/issues/issue-20616-8.stderr b/src/test/ui/issues/issue-20616-8.stderr
index 479469634c523..e9f37e50fffec 100644
--- a/src/test/ui/issues/issue-20616-8.stderr
+++ b/src/test/ui/issues/issue-20616-8.stderr
@@ -1,8 +1,8 @@
-error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
   --> $DIR/issue-20616-8.rs:31:16
    |
 LL | type Type_8<'a,,> = &'a ();
-   |                ^ expected one of `>`, `const`, identifier, or lifetime
+   |                ^ expected one of `#`, `>`, `const`, identifier, or lifetime
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-20616-9.rs b/src/test/ui/issues/issue-20616-9.rs
index 1c509f26fd63a..7f84284481e2f 100644
--- a/src/test/ui/issues/issue-20616-9.rs
+++ b/src/test/ui/issues/issue-20616-9.rs
@@ -32,4 +32,4 @@ type Type_5_<'a> = Type_1_<'a, ()>;
 
 
 type Type_9<T,,> = Box<T>;
-//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
diff --git a/src/test/ui/issues/issue-20616-9.stderr b/src/test/ui/issues/issue-20616-9.stderr
index b7e3322b7aa92..dc309d1bce158 100644
--- a/src/test/ui/issues/issue-20616-9.stderr
+++ b/src/test/ui/issues/issue-20616-9.stderr
@@ -1,8 +1,8 @@
-error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
   --> $DIR/issue-20616-9.rs:34:15
    |
 LL | type Type_9<T,,> = Box<T>;
-   |               ^ expected one of `>`, `const`, identifier, or lifetime
+   |               ^ expected one of `#`, `>`, `const`, identifier, or lifetime
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-66473.rs b/src/test/ui/issues/issue-66473.rs
index cc298a28b97e5..9db4521bb4239 100644
Binary files a/src/test/ui/issues/issue-66473.rs and b/src/test/ui/issues/issue-66473.rs differ
diff --git a/src/test/ui/issues/issue-66473.stderr b/src/test/ui/issues/issue-66473.stderr
index dbeef44bad0ec..b370b125cfefd 100644
Binary files a/src/test/ui/issues/issue-66473.stderr and b/src/test/ui/issues/issue-66473.stderr differ
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 fd8850dd8dab2..a2c75882be0ca 100644
--- a/src/test/ui/label/label_break_value_illegal_uses.stderr
+++ b/src/test/ui/label/label_break_value_illegal_uses.stderr
@@ -2,7 +2,10 @@ error: expected `{`, found `'b`
   --> $DIR/label_break_value_illegal_uses.rs:6:12
    |
 LL |     unsafe 'b: {}
-   |            ^^ expected `{`
+   |            ^^----
+   |            |
+   |            expected `{`
+   |            help: try placing this code inside a block: `{ 'b: {} }`
 
 error: expected `{`, found `'b`
   --> $DIR/label_break_value_illegal_uses.rs:10:13
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 f3980a596481c..09f494bdc2fed 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs
@@ -39,35 +39,35 @@ fn main() {}
 #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
 //~^ ERROR expected one of
 #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
 //~^ ERROR expected one of
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
-//~^ ERROR expected `{`, found `#`
+//~^ ERROR outer attributes are not allowed on `if`
 #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
 //~^ ERROR an inner attribute is not permitted in this context
 
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
index 4dcba27cb68db..6dfe7aad6ea64 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
@@ -136,14 +136,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
    |
    = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:41:37
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
-   |                                --   ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                --   ^^^^^^^ -- the attributes are attached to this branch
    |                                |    |
-   |                                |    expected `{`
-   |                                this `if` expression has a condition, but no block
+   |                                |    help: remove the attributes
+   |                                the branch belongs to this `if`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:43:38
@@ -159,13 +159,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
    |                                        ^ expected one of `.`, `;`, `?`, `else`, or an operator
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:47:45
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
-   |                                             ^       --- help: try placing this code inside a block: `{ {}; }`
-   |                                             |
-   |                                             expected `{`
+   |                                        ---- ^^^^^^^ -- the attributes are attached to this branch
+   |                                        |    |
+   |                                        |    help: remove the attributes
+   |                                        the branch belongs to this `else`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:49:46
@@ -175,22 +176,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
    |
    = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
-   |                                             ^       -------- help: try placing this code inside a block: `{ if 0 {}; }`
-   |                                             |
-   |                                             expected `{`
+   |                                        ---- ^^^^^^^ ------- the attributes are attached to this branch
+   |                                        |    |
+   |                                        |    help: remove the attributes
+   |                                        the branch belongs to this `else`
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:53:50
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
-   |                                             --   ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                             --   ^^^^^^^ -- the attributes are attached to this branch
    |                                             |    |
-   |                                             |    expected `{`
-   |                                             this `if` expression has a condition, but no block
+   |                                             |    help: remove the attributes
+   |                                             the branch belongs to this `if`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:55:51
@@ -200,14 +202,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
    |
    = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:57:45
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
-   |                                --           ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                --           ^^^^^^^ -- the attributes are attached to this branch
    |                                |            |
-   |                                |            expected `{`
-   |                                this `if` expression has a condition, but no block
+   |                                |            help: remove the attributes
+   |                                the branch belongs to this `if`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:59:46
@@ -223,13 +225,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
    |                                                ^ expected one of `.`, `;`, `?`, `else`, or an operator
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:63:53
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
-   |                                                     ^       --- help: try placing this code inside a block: `{ {}; }`
-   |                                                     |
-   |                                                     expected `{`
+   |                                                ---- ^^^^^^^ -- the attributes are attached to this branch
+   |                                                |    |
+   |                                                |    help: remove the attributes
+   |                                                the branch belongs to this `else`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:65:54
@@ -239,22 +242,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
    |
    = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:67:53
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
-   |                                                     ^       ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }`
-   |                                                     |
-   |                                                     expected `{`
+   |                                                ---- ^^^^^^^ --------------- the attributes are attached to this branch
+   |                                                |    |
+   |                                                |    help: remove the attributes
+   |                                                the branch belongs to this `else`
 
-error: expected `{`, found `#`
+error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:69:66
    |
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
-   |                                                     --           ^       --- help: try placing this code inside a block: `{ {}; }`
+   |                                                     --           ^^^^^^^ -- the attributes are attached to this branch
    |                                                     |            |
-   |                                                     |            expected `{`
-   |                                                     this `if` expression has a condition, but no block
+   |                                                     |            help: remove the attributes
+   |                                                     the branch belongs to this `if`
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:71:67
diff --git a/src/test/ui/parser/bad-interpolated-block.rs b/src/test/ui/parser/bad-interpolated-block.rs
new file mode 100644
index 0000000000000..38d53a14bc0bc
--- /dev/null
+++ b/src/test/ui/parser/bad-interpolated-block.rs
@@ -0,0 +1,15 @@
+#![feature(label_break_value)]
+
+fn main() {}
+
+macro_rules! m {
+    ($b:block) => {
+        'lab: $b; //~ ERROR cannot use a `block` macro fragment here
+        unsafe $b; //~ ERROR cannot use a `block` macro fragment here
+        |x: u8| -> () $b; //~ ERROR cannot use a `block` macro fragment here
+    }
+}
+
+fn foo() {
+    m!({});
+}
diff --git a/src/test/ui/parser/bad-interpolated-block.stderr b/src/test/ui/parser/bad-interpolated-block.stderr
new file mode 100644
index 0000000000000..2cbb6a13e74b9
--- /dev/null
+++ b/src/test/ui/parser/bad-interpolated-block.stderr
@@ -0,0 +1,39 @@
+error: cannot use a `block` macro fragment here
+  --> $DIR/bad-interpolated-block.rs:7:15
+   |
+LL |         'lab: $b;
+   |         ------^^
+   |         |
+   |         the `block` fragment is within this context
+...
+LL |     m!({});
+   |     ------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: cannot use a `block` macro fragment here
+  --> $DIR/bad-interpolated-block.rs:8:16
+   |
+LL |         unsafe $b;
+   |         -------^^
+   |         |
+   |         the `block` fragment is within this context
+...
+LL |     m!({});
+   |     ------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: cannot use a `block` macro fragment here
+  --> $DIR/bad-interpolated-block.rs:9:23
+   |
+LL |         |x: u8| -> () $b;
+   |                       ^^ the `block` fragment is within this context
+...
+LL |     m!({});
+   |     ------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/parser/block-no-opening-brace.rs b/src/test/ui/parser/block-no-opening-brace.rs
new file mode 100644
index 0000000000000..e4bb39f6836b4
--- /dev/null
+++ b/src/test/ui/parser/block-no-opening-brace.rs
@@ -0,0 +1,31 @@
+// edition:2018
+
+#![feature(try_blocks)]
+
+fn main() {}
+
+fn f1() {
+    loop
+        let x = 0; //~ ERROR expected `{`, found keyword `let`
+        drop(0);
+    }
+
+fn f2() {
+    while true
+        let x = 0; //~ ERROR expected `{`, found keyword `let`
+    }
+
+fn f3() {
+    for x in 0..1
+        let x = 0; //~ ERROR expected `{`, found keyword `let`
+    }
+
+fn f4() {
+    try //~ ERROR expected expression, found reserved keyword `try`
+        let x = 0;
+    }
+
+fn f5() {
+    async //~ ERROR async closures are unstable
+        let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let`
+    }
diff --git a/src/test/ui/parser/block-no-opening-brace.stderr b/src/test/ui/parser/block-no-opening-brace.stderr
new file mode 100644
index 0000000000000..a88e4ac44cfda
--- /dev/null
+++ b/src/test/ui/parser/block-no-opening-brace.stderr
@@ -0,0 +1,53 @@
+error: expected `{`, found keyword `let`
+  --> $DIR/block-no-opening-brace.rs:9:9
+   |
+LL |         let x = 0;
+   |         ^^^-------
+   |         |
+   |         expected `{`
+   |         help: try placing this code inside a block: `{ let x = 0; }`
+
+error: expected `{`, found keyword `let`
+  --> $DIR/block-no-opening-brace.rs:15:9
+   |
+LL |         let x = 0;
+   |         ^^^-------
+   |         |
+   |         expected `{`
+   |         help: try placing this code inside a block: `{ let x = 0; }`
+
+error: expected `{`, found keyword `let`
+  --> $DIR/block-no-opening-brace.rs:20:9
+   |
+LL |         let x = 0;
+   |         ^^^-------
+   |         |
+   |         expected `{`
+   |         help: try placing this code inside a block: `{ let x = 0; }`
+
+error: expected expression, found reserved keyword `try`
+  --> $DIR/block-no-opening-brace.rs:24:5
+   |
+LL |     try
+   |     ^^^ expected expression
+
+error: expected one of `move`, `|`, or `||`, found keyword `let`
+  --> $DIR/block-no-opening-brace.rs:30:9
+   |
+LL |     async
+   |          - expected one of `move`, `|`, or `||`
+LL |         let x = 0;
+   |         ^^^ unexpected token
+
+error[E0658]: async closures are unstable
+  --> $DIR/block-no-opening-brace.rs:29:5
+   |
+LL |     async
+   |     ^^^^^
+   |
+   = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
+   = help: add `#![feature(async_closure)]` to the crate attributes to enable
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/parser/bounds-lifetime.rs b/src/test/ui/parser/bounds-lifetime.rs
index 9225cfce94eb8..c9251ac532185 100644
--- a/src/test/ui/parser/bounds-lifetime.rs
+++ b/src/test/ui/parser/bounds-lifetime.rs
@@ -6,6 +6,6 @@ type A = for<'a: 'b + 'c> fn(); // OK (rejected later by ast_validation)
 type A = for<'a: 'b,> fn(); // OK(rejected later by ast_validation)
 type A = for<'a: 'b +> fn(); // OK (rejected later by ast_validation)
 type A = for<'a, T> fn(); // OK (rejected later by ast_validation)
-type A = for<,> fn(); //~ ERROR expected one of `>`, `const`, identifier, or lifetime, found `,`
+type A = for<,> fn(); //~ ERROR expected one of `#`, `>`, `const`, identifier, or lifetime
 
 fn main() {}
diff --git a/src/test/ui/parser/bounds-lifetime.stderr b/src/test/ui/parser/bounds-lifetime.stderr
index 12b9b61ebd174..e47a21d892b2f 100644
--- a/src/test/ui/parser/bounds-lifetime.stderr
+++ b/src/test/ui/parser/bounds-lifetime.stderr
@@ -1,8 +1,8 @@
-error: expected one of `>`, `const`, identifier, or lifetime, found `,`
+error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
   --> $DIR/bounds-lifetime.rs:9:14
    |
 LL | type A = for<,> fn();
-   |              ^ expected one of `>`, `const`, identifier, or lifetime
+   |              ^ expected one of `#`, `>`, `const`, identifier, or lifetime
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/closure-return-syntax.rs b/src/test/ui/parser/closure-return-syntax.rs
index 54eb791d2bce4..c6a08abeff4ba 100644
--- a/src/test/ui/parser/closure-return-syntax.rs
+++ b/src/test/ui/parser/closure-return-syntax.rs
@@ -3,5 +3,5 @@
 
 fn main() {
     let x = || -> i32 22;
-    //~^ ERROR expected one of `!`, `(`, `+`, `::`, `<`, or `{`, found `22`
+    //~^ ERROR expected `{`, found `22`
 }
diff --git a/src/test/ui/parser/closure-return-syntax.stderr b/src/test/ui/parser/closure-return-syntax.stderr
index bfb7f98c5f527..1ccdd97730555 100644
--- a/src/test/ui/parser/closure-return-syntax.stderr
+++ b/src/test/ui/parser/closure-return-syntax.stderr
@@ -1,8 +1,11 @@
-error: expected one of `!`, `(`, `+`, `::`, `<`, or `{`, found `22`
+error: expected `{`, found `22`
   --> $DIR/closure-return-syntax.rs:5:23
    |
 LL |     let x = || -> i32 22;
-   |                       ^^ expected one of `!`, `(`, `+`, `::`, `<`, or `{`
+   |                       ^^
+   |                       |
+   |                       expected `{`
+   |                       help: try placing this code inside a block: `{ 22 }`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/column-offset-1-based.rs b/src/test/ui/parser/column-offset-1-based.rs
index e158e5247db18..0c24478c25c8b 100644
--- a/src/test/ui/parser/column-offset-1-based.rs
+++ b/src/test/ui/parser/column-offset-1-based.rs
@@ -1 +1 @@
-# //~ ERROR expected `[`, found `<eof>`
+# //~ ERROR expected one of `!` or `[`, found `<eof>`
diff --git a/src/test/ui/parser/column-offset-1-based.stderr b/src/test/ui/parser/column-offset-1-based.stderr
index 5cbf3d3e95965..766d72a0a5a93 100644
--- a/src/test/ui/parser/column-offset-1-based.stderr
+++ b/src/test/ui/parser/column-offset-1-based.stderr
@@ -1,8 +1,8 @@
-error: expected `[`, found `<eof>`
+error: expected one of `!` or `[`, found `<eof>`
   --> $DIR/column-offset-1-based.rs:1:1
    |
 LL | #
-   | ^ expected `[`
+   | ^ expected one of `!` or `[`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-comment-in-if-statement.rs b/src/test/ui/parser/doc-comment-in-if-statement.rs
index c85fe25a7d0d1..343eac1b81ff0 100644
--- a/src/test/ui/parser/doc-comment-in-if-statement.rs
+++ b/src/test/ui/parser/doc-comment-in-if-statement.rs
@@ -1,4 +1,5 @@
 fn main() {
     if true /*!*/ {}
-    //~^ ERROR expected `{`, found doc comment `/*!*/`
+    //~^ ERROR outer attributes are not allowed on
+    //~| ERROR expected outer doc comment
 }
diff --git a/src/test/ui/parser/doc-comment-in-if-statement.stderr b/src/test/ui/parser/doc-comment-in-if-statement.stderr
index a720dd68bd037..af21b78733f90 100644
--- a/src/test/ui/parser/doc-comment-in-if-statement.stderr
+++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr
@@ -1,10 +1,19 @@
-error: expected `{`, found doc comment `/*!*/`
+error: expected outer doc comment
   --> $DIR/doc-comment-in-if-statement.rs:2:13
    |
 LL |     if true /*!*/ {}
-   |     --      ^^^^^ expected `{`
-   |     |
-   |     this `if` expression has a condition, but no block
+   |             ^^^^^
+   |
+   = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+
+error: outer attributes are not allowed on `if` and `else` branches
+  --> $DIR/doc-comment-in-if-statement.rs:2:13
+   |
+LL |     if true /*!*/ {}
+   |     --      ^^^^^ -- the attributes are attached to this branch
+   |     |       |
+   |     |       help: remove the attributes
+   |     the branch belongs to this `if`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/parser/fn-body-eq-expr-semi.rs b/src/test/ui/parser/fn-body-eq-expr-semi.rs
new file mode 100644
index 0000000000000..09444079365bf
--- /dev/null
+++ b/src/test/ui/parser/fn-body-eq-expr-semi.rs
@@ -0,0 +1,23 @@
+fn main() {}
+
+fn syntax() {
+    fn foo() = 42; //~ ERROR function body cannot be `= expression;`
+    fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;`
+}
+
+extern {
+    fn foo() = 42; //~ ERROR function body cannot be `= expression;`
+    //~^ ERROR incorrect function inside `extern` block
+    fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;`
+    //~^ ERROR incorrect function inside `extern` block
+}
+
+trait Foo {
+    fn foo() = 42; //~ ERROR function body cannot be `= expression;`
+    fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;`
+}
+
+impl Foo for () {
+    fn foo() = 42; //~ ERROR function body cannot be `= expression;`
+    fn bar() -> u8 = 42; //~ ERROR function body cannot be `= expression;`
+}
diff --git a/src/test/ui/parser/fn-body-eq-expr-semi.stderr b/src/test/ui/parser/fn-body-eq-expr-semi.stderr
new file mode 100644
index 0000000000000..739133e0b408b
--- /dev/null
+++ b/src/test/ui/parser/fn-body-eq-expr-semi.stderr
@@ -0,0 +1,117 @@
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:4:14
+   |
+LL |     fn foo() = 42;
+   |              ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn foo() { 42 }
+   |              ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:5:20
+   |
+LL |     fn bar() -> u8 = 42;
+   |                    ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn bar() -> u8 { 42 }
+   |                    ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:9:14
+   |
+LL |     fn foo() = 42;
+   |              ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn foo() { 42 }
+   |              ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:11:20
+   |
+LL |     fn bar() -> u8 = 42;
+   |                    ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn bar() -> u8 { 42 }
+   |                    ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:16:14
+   |
+LL |     fn foo() = 42;
+   |              ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn foo() { 42 }
+   |              ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:17:20
+   |
+LL |     fn bar() -> u8 = 42;
+   |                    ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn bar() -> u8 { 42 }
+   |                    ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:21:14
+   |
+LL |     fn foo() = 42;
+   |              ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn foo() { 42 }
+   |              ^    ^
+
+error: function body cannot be `= expression;`
+  --> $DIR/fn-body-eq-expr-semi.rs:22:20
+   |
+LL |     fn bar() -> u8 = 42;
+   |                    ^^^^^
+   |
+help: surround the expression with `{` and `}` instead of `=` and `;`
+   |
+LL |     fn bar() -> u8 { 42 }
+   |                    ^    ^
+
+error: incorrect function inside `extern` block
+  --> $DIR/fn-body-eq-expr-semi.rs:9:8
+   |
+LL | extern {
+   | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body
+LL |     fn foo() = 42;
+   |        ^^^   ----- help: remove the invalid body: `;`
+   |        |
+   |        cannot have a body
+   |
+   = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
+   = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
+
+error: incorrect function inside `extern` block
+  --> $DIR/fn-body-eq-expr-semi.rs:11:8
+   |
+LL | extern {
+   | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body
+...
+LL |     fn bar() -> u8 = 42;
+   |        ^^^         ----- help: remove the invalid body: `;`
+   |        |
+   |        cannot have a body
+   |
+   = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
+   = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
+
+error: aborting due to 10 previous errors
+
diff --git a/src/test/ui/parser/issue-1655.rs b/src/test/ui/parser/issue-1655.rs
index 3d0bf3c1c7b9f..e9fc6f15346f2 100644
--- a/src/test/ui/parser/issue-1655.rs
+++ b/src/test/ui/parser/issue-1655.rs
@@ -1,6 +1,5 @@
-// error-pattern:expected `[`, found `vec`
 mod blade_runner {
-    #vec[doc(
+    #vec[doc( //~ ERROR expected one of `!` or `[`, found `vec`
         brief = "Blade Runner is probably the best movie ever",
         desc = "I like that in the world of Blade Runner it is always
                 raining, and that it's always night time. And Aliens
diff --git a/src/test/ui/parser/issue-1655.stderr b/src/test/ui/parser/issue-1655.stderr
index 3f656b63cdb84..0c390a0ec563c 100644
--- a/src/test/ui/parser/issue-1655.stderr
+++ b/src/test/ui/parser/issue-1655.stderr
@@ -1,8 +1,8 @@
-error: expected `[`, found `vec`
-  --> $DIR/issue-1655.rs:3:6
+error: expected one of `!` or `[`, found `vec`
+  --> $DIR/issue-1655.rs:2:6
    |
 LL |     #vec[doc(
-   |      ^^^ expected `[`
+   |      ^^^ expected one of `!` or `[`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr
index 2beb73d83d261..15cd3df860b0d 100644
--- a/src/test/ui/parser/issue-63116.stderr
+++ b/src/test/ui/parser/issue-63116.stderr
@@ -12,7 +12,7 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;`
 LL | impl W <s(f;Y(;]
    |            ^ expected one of 7 possible tokens
 
-error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, or lifetime, found `;`
+error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `<`, `>`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;`
   --> $DIR/issue-63116.rs:3:15
    |
 LL | impl W <s(f;Y(;]
diff --git a/src/test/ui/parser/issue-63135.rs b/src/test/ui/parser/issue-63135.rs
index 7d46b8904f033..a5a8de85466bb 100644
--- a/src/test/ui/parser/issue-63135.rs
+++ b/src/test/ui/parser/issue-63135.rs
@@ -1,3 +1,3 @@
-// error-pattern: aborting due to 7 previous errors
+// error-pattern: aborting due to 5 previous errors
 
 fn i(n{...,f #
diff --git a/src/test/ui/parser/issue-63135.stderr b/src/test/ui/parser/issue-63135.stderr
index 04afae93be0e5..396aec8335dbf 100644
--- a/src/test/ui/parser/issue-63135.stderr
+++ b/src/test/ui/parser/issue-63135.stderr
@@ -31,23 +31,11 @@ LL | fn i(n{...,f #
    |        |  expected `}`
    |        `..` must be at the end and cannot have a trailing comma
 
-error: expected `[`, found `}`
+error: expected one of `!` or `[`, found `}`
   --> $DIR/issue-63135.rs:3:16
    |
 LL | fn i(n{...,f #
-   |                ^ expected `[`
+   |                ^ expected one of `!` or `[`
 
-error: expected one of `:` or `|`, found `)`
-  --> $DIR/issue-63135.rs:3:16
-   |
-LL | fn i(n{...,f #
-   |                ^ expected one of `:` or `|`
-
-error: expected `;` or `{`, found `<eof>`
-  --> $DIR/issue-63135.rs:3:16
-   |
-LL | fn i(n{...,f #
-   |                ^ expected `;` or `{`
-
-error: aborting due to 7 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/parser/issue-68730.rs b/src/test/ui/parser/issue-68730.rs
index b570e9417751b..20e18b4bcbb32 100644
Binary files a/src/test/ui/parser/issue-68730.rs and b/src/test/ui/parser/issue-68730.rs differ
diff --git a/src/test/ui/parser/issue-68730.stderr b/src/test/ui/parser/issue-68730.stderr
index 090b41d839f82..9f8833e17fe25 100644
Binary files a/src/test/ui/parser/issue-68730.stderr and b/src/test/ui/parser/issue-68730.stderr differ
diff --git a/src/test/ui/parser/issue-68890-2.rs b/src/test/ui/parser/issue-68890-2.rs
new file mode 100644
index 0000000000000..ae02246046880
--- /dev/null
+++ b/src/test/ui/parser/issue-68890-2.rs
@@ -0,0 +1,6 @@
+fn main() {}
+
+type X<'a> = (?'a) +;
+//~^ ERROR `?` may only modify trait bounds, not lifetime bounds
+//~| ERROR at least one trait is required for an object type
+//~| WARN trait objects without an explicit `dyn` are deprecated
diff --git a/src/test/ui/parser/issue-68890-2.stderr b/src/test/ui/parser/issue-68890-2.stderr
new file mode 100644
index 0000000000000..d475c79cb27b4
--- /dev/null
+++ b/src/test/ui/parser/issue-68890-2.stderr
@@ -0,0 +1,22 @@
+error: `?` may only modify trait bounds, not lifetime bounds
+  --> $DIR/issue-68890-2.rs:3:15
+   |
+LL | type X<'a> = (?'a) +;
+   |               ^
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/issue-68890-2.rs:3:14
+   |
+LL | type X<'a> = (?'a) +;
+   |              ^^^^^^^ help: use `dyn`: `dyn (?'a) +`
+   |
+   = note: `#[warn(bare_trait_objects)]` on by default
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/issue-68890-2.rs:3:14
+   |
+LL | type X<'a> = (?'a) +;
+   |              ^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/parser/issue-68890.rs b/src/test/ui/parser/issue-68890.rs
index a7c5a5e13008a..bab4ed7f800c5 100644
--- a/src/test/ui/parser/issue-68890.rs
+++ b/src/test/ui/parser/issue-68890.rs
@@ -1,4 +1,4 @@
 enum e{A((?'a a+?+l))}
 //~^ ERROR `?` may only modify trait bounds, not lifetime bounds
 //~| ERROR expected one of `)`, `+`, or `,`
-//~| ERROR expected trait bound, not lifetime bound
+//~| ERROR expected item, found `)`
diff --git a/src/test/ui/parser/issue-68890.stderr b/src/test/ui/parser/issue-68890.stderr
index 9bb8761b67b5f..2a3bf6b41f02e 100644
--- a/src/test/ui/parser/issue-68890.stderr
+++ b/src/test/ui/parser/issue-68890.stderr
@@ -10,11 +10,11 @@ error: expected one of `)`, `+`, or `,`, found `a`
 LL | enum e{A((?'a a+?+l))}
    |               ^ expected one of `)`, `+`, or `,`
 
-error: expected trait bound, not lifetime bound
-  --> $DIR/issue-68890.rs:1:11
+error: expected item, found `)`
+  --> $DIR/issue-68890.rs:1:21
    |
 LL | enum e{A((?'a a+?+l))}
-   |           ^^^
+   |                     ^ expected item
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/parser/labeled-no-colon-expr.rs b/src/test/ui/parser/labeled-no-colon-expr.rs
new file mode 100644
index 0000000000000..db9ef52c1aeec
--- /dev/null
+++ b/src/test/ui/parser/labeled-no-colon-expr.rs
@@ -0,0 +1,17 @@
+#![feature(label_break_value)]
+
+fn main() {
+    'l0 while false {} //~ ERROR labeled expression must be followed by `:`
+    'l1 for _ in 0..1 {} //~ ERROR labeled expression must be followed by `:`
+    'l2 loop {} //~ ERROR labeled expression must be followed by `:`
+    'l3 {} //~ ERROR labeled expression must be followed by `:`
+    'l4 0; //~ ERROR labeled expression must be followed by `:`
+    //~^ ERROR expected `while`, `for`, `loop` or `{`
+
+    macro_rules! m {
+        ($b:block) => {
+            'l5 $b; //~ ERROR cannot use a `block` macro fragment here
+        }
+    }
+    m!({}); //~ ERROR labeled expression must be followed by `:`
+}
diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr
new file mode 100644
index 0000000000000..4f5e8f78aa0cd
--- /dev/null
+++ b/src/test/ui/parser/labeled-no-colon-expr.stderr
@@ -0,0 +1,89 @@
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:4:5
+   |
+LL |     'l0 while false {}
+   |     ----^^^^^^^^^^^^^^
+   |     |  |
+   |     |  help: add `:` after the label
+   |     the label
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:5:5
+   |
+LL |     'l1 for _ in 0..1 {}
+   |     ----^^^^^^^^^^^^^^^^
+   |     |  |
+   |     |  help: add `:` after the label
+   |     the label
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:6:5
+   |
+LL |     'l2 loop {}
+   |     ----^^^^^^^
+   |     |  |
+   |     |  help: add `:` after the label
+   |     the label
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:7:5
+   |
+LL |     'l3 {}
+   |     ----^^
+   |     |  |
+   |     |  help: add `:` after the label
+   |     the label
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/labeled-no-colon-expr.rs:8:9
+   |
+LL |     'l4 0;
+   |         ^ expected `while`, `for`, `loop` or `{` after a label
+
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:8:9
+   |
+LL |     'l4 0;
+   |     ----^
+   |     |  |
+   |     |  help: add `:` after the label
+   |     the label
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: cannot use a `block` macro fragment here
+  --> $DIR/labeled-no-colon-expr.rs:13:17
+   |
+LL |             'l5 $b;
+   |             ----^^
+   |             |
+   |             the `block` fragment is within this context
+...
+LL |     m!({});
+   |     ------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: labeled expression must be followed by `:`
+  --> $DIR/labeled-no-colon-expr.rs:16:8
+   |
+LL |             'l5 $b;
+   |             ---- help: add `:` after the label
+   |             |
+   |             the label
+...
+LL |     m!({});
+   |        ^^
+   |
+   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them
+
+error: aborting due to 8 previous errors
+
diff --git a/src/test/ui/parser/macro/trait-object-macro-matcher.rs b/src/test/ui/parser/macro/trait-object-macro-matcher.rs
index 80d867d3b568e..170ac22780b63 100644
--- a/src/test/ui/parser/macro/trait-object-macro-matcher.rs
+++ b/src/test/ui/parser/macro/trait-object-macro-matcher.rs
@@ -2,9 +2,14 @@
 // `ty` matcher in particular doesn't accept a single lifetime
 
 macro_rules! m {
-    ($t: ty) => ( let _: $t; )
+    ($t: ty) => {
+        let _: $t;
+    };
 }
 
 fn main() {
-    m!('static); //~ ERROR expected type, found `'static`
+    m!('static);
+    //~^ ERROR lifetime in trait object type must be followed by `+`
+    //~| ERROR at least one trait is required for an object type
+    //~| WARN trait objects without an explicit `dyn` are deprecated
 }
diff --git a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr
index f02f60e4bfb1d..230733371ddd8 100644
--- a/src/test/ui/parser/macro/trait-object-macro-matcher.stderr
+++ b/src/test/ui/parser/macro/trait-object-macro-matcher.stderr
@@ -1,8 +1,22 @@
-error: expected type, found `'static`
-  --> $DIR/trait-object-macro-matcher.rs:9:8
+error: lifetime in trait object type must be followed by `+`
+  --> $DIR/trait-object-macro-matcher.rs:11:8
    |
 LL |     m!('static);
-   |        ^^^^^^^ expected type
+   |        ^^^^^^^
 
-error: aborting due to previous error
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/trait-object-macro-matcher.rs:11:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^ help: use `dyn`: `dyn 'static`
+   |
+   = note: `#[warn(bare_trait_objects)]` on by default
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/trait-object-macro-matcher.rs:11:8
+   |
+LL |     m!('static);
+   |        ^^^^^^^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/parser/regions-out-of-scope-slice.rs b/src/test/ui/parser/regions-out-of-scope-slice.rs
index 21369d0be61b1..d223619e1de9e 100644
--- a/src/test/ui/parser/regions-out-of-scope-slice.rs
+++ b/src/test/ui/parser/regions-out-of-scope-slice.rs
@@ -4,7 +4,7 @@ fn foo(cond: bool) {
     let mut x;
 
     if cond {
-        x = &'blk [1,2,3]; //~ ERROR expected `:`, found `[`
+        x = &'blk [1,2,3]; //~ ERROR borrow expressions cannot be annotated with lifetimes
     }
 }
 
diff --git a/src/test/ui/parser/regions-out-of-scope-slice.stderr b/src/test/ui/parser/regions-out-of-scope-slice.stderr
index 8d9bf0b7a0445..bbc657ffd614c 100644
--- a/src/test/ui/parser/regions-out-of-scope-slice.stderr
+++ b/src/test/ui/parser/regions-out-of-scope-slice.stderr
@@ -1,8 +1,11 @@
-error: expected `:`, found `[`
-  --> $DIR/regions-out-of-scope-slice.rs:7:19
+error: borrow expressions cannot be annotated with lifetimes
+  --> $DIR/regions-out-of-scope-slice.rs:7:13
    |
 LL |         x = &'blk [1,2,3];
-   |                   ^ expected `:`
+   |             ^----^^^^^^^^
+   |              |
+   |              annotated with lifetime here
+   |              help: remove the lifetime annotation
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/trait-object-lifetime-parens.rs b/src/test/ui/parser/trait-object-lifetime-parens.rs
index c8b0eb684f33d..5a5c19f32e806 100644
--- a/src/test/ui/parser/trait-object-lifetime-parens.rs
+++ b/src/test/ui/parser/trait-object-lifetime-parens.rs
@@ -6,9 +6,7 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s
 
 fn check<'a>() {
     let _: Box<Trait + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
-    let _: Box<('a) + Trait>;
-    //~^ ERROR expected type, found `'a`
-    //~| ERROR expected `:`, found `)`
+    let _: Box<('a) + Trait>; //~ ERROR lifetime in trait object type must be followed by `+`
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/trait-object-lifetime-parens.stderr b/src/test/ui/parser/trait-object-lifetime-parens.stderr
index 319a308c0137c..1289c248275dc 100644
--- a/src/test/ui/parser/trait-object-lifetime-parens.stderr
+++ b/src/test/ui/parser/trait-object-lifetime-parens.stderr
@@ -10,19 +10,11 @@ error: parenthesized lifetime bounds are not supported
 LL |     let _: Box<Trait + ('a)>;
    |                        ^^^^ help: remove the parentheses
 
-error: expected `:`, found `)`
-  --> $DIR/trait-object-lifetime-parens.rs:9:19
-   |
-LL |     let _: Box<('a) + Trait>;
-   |                   ^ expected `:`
-
-error: expected type, found `'a`
+error: lifetime in trait object type must be followed by `+`
   --> $DIR/trait-object-lifetime-parens.rs:9:17
    |
 LL |     let _: Box<('a) + Trait>;
-   |         -       ^^ expected type
-   |         |
-   |         while parsing the type for `_`
+   |                 ^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/parser/trait-object-trait-parens.rs b/src/test/ui/parser/trait-object-trait-parens.rs
index a113de14b6fbc..9fbc938c4dce8 100644
--- a/src/test/ui/parser/trait-object-trait-parens.rs
+++ b/src/test/ui/parser/trait-object-trait-parens.rs
@@ -1,15 +1,20 @@
 trait Trait<'a> {}
 
+trait Obj {}
+
 fn f<T: (Copy) + (?Sized) + (for<'a> Trait<'a>)>() {}
 
 fn main() {
-    let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
+    let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>;
+    //~^ ERROR `?Trait` is not permitted in trait object types
+    //~| ERROR only auto traits can be used as additional traits
+    //~| WARN trait objects without an explicit `dyn` are deprecated
+    let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>;
     //~^ ERROR `?Trait` is not permitted in trait object types
+    //~| ERROR only auto traits can be used as additional traits
     //~| WARN trait objects without an explicit `dyn` are deprecated
-    let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>;
-    //~^ WARN trait objects without an explicit `dyn` are deprecated
-    let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
-    //~^ ERROR use of undeclared lifetime name `'a`
-    //~| ERROR `?Trait` is not permitted in trait object types
+    let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>;
+    //~^ ERROR `?Trait` is not permitted in trait object types
+    //~| ERROR only auto traits can be used as additional traits
     //~| WARN trait objects without an explicit `dyn` are deprecated
 }
diff --git a/src/test/ui/parser/trait-object-trait-parens.stderr b/src/test/ui/parser/trait-object-trait-parens.stderr
index 4b9f49423cbf4..7022a66ca1a17 100644
--- a/src/test/ui/parser/trait-object-trait-parens.stderr
+++ b/src/test/ui/parser/trait-object-trait-parens.stderr
@@ -1,44 +1,74 @@
 error: `?Trait` is not permitted in trait object types
-  --> $DIR/trait-object-trait-parens.rs:6:25
+  --> $DIR/trait-object-trait-parens.rs:8:24
    |
-LL |     let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
-   |                         ^^^^^^^^
+LL |     let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>;
+   |                        ^^^^^^^^
 
 error: `?Trait` is not permitted in trait object types
-  --> $DIR/trait-object-trait-parens.rs:11:47
+  --> $DIR/trait-object-trait-parens.rs:12:17
    |
-LL |     let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
-   |                                               ^^^^^^^^
+LL |     let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>;
+   |                 ^^^^^^
+
+error: `?Trait` is not permitted in trait object types
+  --> $DIR/trait-object-trait-parens.rs:16:46
+   |
+LL |     let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>;
+   |                                              ^^^^^^^^
 
 warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/trait-object-trait-parens.rs:6:16
+  --> $DIR/trait-object-trait-parens.rs:8:16
    |
-LL |     let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (Copy) + (?Sized) + (for<'a> Trait<'a>)`
+LL |     let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>;
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (Obj) + (?Sized) + (for<'a> Trait<'a>)`
    |
    = note: `#[warn(bare_trait_objects)]` on by default
 
 warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/trait-object-trait-parens.rs:9:16
+  --> $DIR/trait-object-trait-parens.rs:12:16
    |
-LL |     let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>;
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (?Sized) + (for<'a> Trait<'a>) + (Copy)`
+LL |     let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>;
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (?Sized) + (for<'a> Trait<'a>) + (Obj)`
 
 warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/trait-object-trait-parens.rs:11:16
+  --> $DIR/trait-object-trait-parens.rs:16:16
+   |
+LL |     let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>;
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (for<'a> Trait<'a>) + (Obj) + (?Sized)`
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-trait-parens.rs:8:35
+   |
+LL |     let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>;
+   |                -----              ^^^^^^^^^^^^^^^^^^^
+   |                |                  |
+   |                |                  additional non-auto trait
+   |                |                  trait alias used in trait object type (additional use)
+   |                first non-auto trait
+   |                trait alias used in trait object type (first use)
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-trait-parens.rs:12:49
    |
-LL |     let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn (for<'a> Trait<'a>) + (Copy) + (?Sized)`
+LL |     let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>;
+   |                           -------------------   ^^^^^
+   |                           |                     |
+   |                           |                     additional non-auto trait
+   |                           |                     trait alias used in trait object type (additional use)
+   |                           first non-auto trait
+   |                           trait alias used in trait object type (first use)
 
-error[E0261]: use of undeclared lifetime name `'a`
-  --> $DIR/trait-object-trait-parens.rs:11:31
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-object-trait-parens.rs:16:38
    |
-LL | fn main() {
-   |        - help: consider introducing lifetime `'a` here: `<'a>`
-...
-LL |     let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
-   |                               ^^ undeclared lifetime
+LL |     let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>;
+   |                 -----------------    ^^^^^
+   |                 |                    |
+   |                 |                    additional non-auto trait
+   |                 |                    trait alias used in trait object type (additional use)
+   |                 first non-auto trait
+   |                 trait alias used in trait object type (first use)
 
-error: aborting due to 3 previous errors
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0261`.
+For more information about this error, try `rustc --explain E0225`.
diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr
index 7becb9c5b60af..c2fb8fa1eb6ca 100644
--- a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr
+++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr
@@ -8,3 +8,4 @@ LL | struct S;
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0739`.
diff --git a/src/test/ui/unsafe/unsafe-block-without-braces.stderr b/src/test/ui/unsafe/unsafe-block-without-braces.stderr
index 13e0c3681fa00..895f33638f95a 100644
--- a/src/test/ui/unsafe/unsafe-block-without-braces.stderr
+++ b/src/test/ui/unsafe/unsafe-block-without-braces.stderr
@@ -1,10 +1,11 @@
 error: expected `{`, found `std`
   --> $DIR/unsafe-block-without-braces.rs:3:9
    |
-LL |     unsafe //{
-   |           - expected `{`
 LL |         std::mem::transmute::<f32, u32>(1.0);
-   |         ^^^ unexpected token
+   |         ^^^----------------------------------
+   |         |
+   |         expected `{`
+   |         help: try placing this code inside a block: `{ std::mem::transmute::<f32, u32>(1.0); }`
 
 error: aborting due to previous error
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 424bac88c8531..680c32c92af4b 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -3204,7 +3204,9 @@ impl<'test> TestCx<'test> {
         let json = cflags.contains("--error-format json")
             || cflags.contains("--error-format pretty-json")
             || cflags.contains("--error-format=json")
-            || cflags.contains("--error-format=pretty-json");
+            || cflags.contains("--error-format=pretty-json")
+            || cflags.contains("--output-format json")
+            || cflags.contains("--output-format=json");
 
         let mut normalized = output.to_string();
 
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index ec8b14c288a2f..909529d730784 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -1,8 +1,8 @@
 //! Tidy checks source code in this repository.
 //!
 //! This program runs all of the various tidy checks for style, cleanliness,
-//! etc. This is run by default on `make check` and as part of the auto
-//! builders.
+//! etc. This is run by default on `./x.py test` and as part of the auto
+//! builders. The tidy checks can be executed with `./x.py test tidy`.
 
 #![deny(warnings)]