diff --git a/src/librustc_builtin_macros/cmdline_attrs.rs b/src/librustc_builtin_macros/cmdline_attrs.rs
index 7ddbf08306b72..093815dbbcd4f 100644
--- a/src/librustc_builtin_macros/cmdline_attrs.rs
+++ b/src/librustc_builtin_macros/cmdline_attrs.rs
@@ -3,7 +3,6 @@
 use rustc_ast::ast::{self, AttrItem, AttrStyle};
 use rustc_ast::attr::mk_attr;
 use rustc_ast::token;
-use rustc_expand::panictry;
 use rustc_session::parse::ParseSess;
 use rustc_span::FileName;
 
@@ -16,7 +15,13 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
         );
 
         let start_span = parser.token.span;
-        let AttrItem { path, args } = panictry!(parser.parse_attr_item());
+        let AttrItem { path, args } = match parser.parse_attr_item() {
+            Ok(ai) => ai,
+            Err(mut err) => {
+                err.emit();
+                continue;
+            }
+        };
         let end_span = parser.token.span;
         if parser.token != token::Eof {
             parse_sess.span_diagnostic.span_err(start_span.to(end_span), "invalid crate attribute");
diff --git a/src/librustc_builtin_macros/source_util.rs b/src/librustc_builtin_macros/source_util.rs
index 51a15f9df1bc7..67145c6bf433b 100644
--- a/src/librustc_builtin_macros/source_util.rs
+++ b/src/librustc_builtin_macros/source_util.rs
@@ -5,7 +5,6 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::{self, *};
 use rustc_expand::module::DirectoryOwnership;
-use rustc_expand::panictry;
 use rustc_parse::{self, new_parser_from_file, parser::Parser};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
 use rustc_span::symbol::Symbol;
@@ -126,7 +125,7 @@ pub fn expand_include<'cx>(
     }
     impl<'a> base::MacResult for ExpandResult<'a> {
         fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
-            let r = panictry!(self.p.parse_expr());
+            let r = base::parse_expr(&mut self.p)?;
             if self.p.token != token::Eof {
                 self.p.sess.buffer_lint(
                     &INCOMPLETE_INCLUDE,
@@ -141,18 +140,17 @@ pub fn expand_include<'cx>(
         fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
             let mut ret = SmallVec::new();
             while self.p.token != token::Eof {
-                match panictry!(self.p.parse_item()) {
-                    Some(item) => ret.push(item),
-                    None => {
+                match self.p.parse_item() {
+                    Err(mut err) => {
+                        err.emit();
+                        break;
+                    }
+                    Ok(Some(item)) => ret.push(item),
+                    Ok(None) => {
                         let token = pprust::token_to_string(&self.p.token);
-                        self.p
-                            .sess
-                            .span_diagnostic
-                            .span_fatal(
-                                self.p.token.span,
-                                &format!("expected item, found `{}`", token),
-                            )
-                            .raise();
+                        let msg = format!("expected item, found `{}`", token);
+                        self.p.struct_span_err(self.p.token.span, &msg).emit();
+                        break;
                     }
                 }
             }
diff --git a/src/librustc_builtin_macros/test.rs b/src/librustc_builtin_macros/test.rs
index 39009ca27f102..bdc4ae2fe274b 100644
--- a/src/librustc_builtin_macros/test.rs
+++ b/src/librustc_builtin_macros/test.rs
@@ -74,16 +74,16 @@ pub fn expand_test_or_bench(
         return vec![];
     }
 
-    let item = if let Annotatable::Item(i) = item {
-        i
-    } else {
-        cx.parse_sess
-            .span_diagnostic
-            .span_fatal(
-                item.span(),
+    let item = match item {
+        Annotatable::Item(i) => i,
+        other => {
+            cx.struct_span_err(
+                other.span(),
                 "`#[test]` attribute is only allowed on non associated functions",
             )
-            .raise();
+            .emit();
+            return vec![other];
+        }
     };
 
     if let ast::ItemKind::MacCall(_) = item.kind {
diff --git a/src/librustc_builtin_macros/test_harness.rs b/src/librustc_builtin_macros/test_harness.rs
index b87767f4a4127..160a5204eaf70 100644
--- a/src/librustc_builtin_macros/test_harness.rs
+++ b/src/librustc_builtin_macros/test_harness.rs
@@ -345,14 +345,14 @@ fn is_test_case(i: &ast::Item) -> bool {
 
 fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
     let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
-    test_attr.meta_item_list().map(|meta_list| {
-        if meta_list.len() != 1 {
-            sd.span_fatal(test_attr.span, "`#![test_runner(..)]` accepts exactly 1 argument")
-                .raise()
-        }
-        match meta_list[0].meta_item() {
-            Some(meta_item) if meta_item.is_word() => meta_item.path.clone(),
-            _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise(),
-        }
-    })
+    let meta_list = test_attr.meta_item_list()?;
+    let span = test_attr.span;
+    match &*meta_list {
+        [single] => match single.meta_item() {
+            Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
+            _ => sd.struct_span_err(span, "`test_runner` argument must be a path").emit(),
+        },
+        _ => sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit(),
+    }
+    None
 }
diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs
index 0fc477bbd0b4c..59c1a5468f100 100644
--- a/src/librustc_expand/base.rs
+++ b/src/librustc_expand/base.rs
@@ -10,7 +10,7 @@ use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{DiagnosticBuilder, ErrorReported};
 use rustc_parse::{self, parser, MACRO_ARGUMENTS};
 use rustc_session::parse::ParseSess;
 use rustc_span::edition::Edition;
@@ -296,16 +296,26 @@ where
 }
 
 pub trait ProcMacro {
-    fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt<'_>, span: Span, ts: TokenStream) -> TokenStream;
+    fn expand<'cx>(
+        &self,
+        ecx: &'cx mut ExtCtxt<'_>,
+        span: Span,
+        ts: TokenStream,
+    ) -> Result<TokenStream, ErrorReported>;
 }
 
 impl<F> ProcMacro for F
 where
     F: Fn(TokenStream) -> TokenStream,
 {
-    fn expand<'cx>(&self, _ecx: &'cx mut ExtCtxt<'_>, _span: Span, ts: TokenStream) -> TokenStream {
+    fn expand<'cx>(
+        &self,
+        _ecx: &'cx mut ExtCtxt<'_>,
+        _span: Span,
+        ts: TokenStream,
+    ) -> Result<TokenStream, ErrorReported> {
         // FIXME setup implicit context in TLS before calling self.
-        (*self)(ts)
+        Ok((*self)(ts))
     }
 }
 
@@ -316,7 +326,7 @@ pub trait AttrProcMacro {
         span: Span,
         annotation: TokenStream,
         annotated: TokenStream,
-    ) -> TokenStream;
+    ) -> Result<TokenStream, ErrorReported>;
 }
 
 impl<F> AttrProcMacro for F
@@ -329,9 +339,9 @@ where
         _span: Span,
         annotation: TokenStream,
         annotated: TokenStream,
-    ) -> TokenStream {
+    ) -> Result<TokenStream, ErrorReported> {
         // FIXME setup implicit context in TLS before calling self.
-        (*self)(annotation, annotated)
+        Ok((*self)(annotation, annotated))
     }
 }
 
@@ -1004,31 +1014,9 @@ impl<'a> ExtCtxt<'a> {
         self.current_expansion.id.expansion_cause()
     }
 
-    pub fn struct_span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
-        self.parse_sess.span_diagnostic.struct_span_warn(sp, msg)
-    }
     pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
         self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
     }
-    pub fn struct_span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
-        self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg)
-    }
-
-    /// Emit `msg` attached to `sp`, and stop compilation immediately.
-    ///
-    /// `span_err` should be strongly preferred where-ever possible:
-    /// this should *only* be used when:
-    ///
-    /// - continuing has a high risk of flow-on errors (e.g., errors in
-    ///   declaring a macro would cause all uses of that macro to
-    ///   complain about "undefined macro"), or
-    /// - there is literally nothing else that can be done (however,
-    ///   in most cases one can construct a dummy expression/item to
-    ///   substitute; we never hit resolve/type-checking so the dummy
-    ///   value doesn't have to match anything)
-    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.parse_sess.span_diagnostic.span_fatal(sp, msg).raise();
-    }
 
     /// Emit `msg` attached to `sp`, without immediately stopping
     /// compilation.
@@ -1038,9 +1026,6 @@ impl<'a> ExtCtxt<'a> {
     pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.parse_sess.span_diagnostic.span_err(sp, msg);
     }
-    pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
-        self.parse_sess.span_diagnostic.span_err_with_code(sp, msg, code);
-    }
     pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.parse_sess.span_diagnostic.span_warn(sp, msg);
     }
@@ -1168,6 +1153,18 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str)
     }
 }
 
+/// Parse an expression. On error, emit it, advancing to `Eof`, and return `None`.
+pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
+    match p.parse_expr() {
+        Ok(e) => return Some(e),
+        Err(mut err) => err.emit(),
+    }
+    while p.token != token::Eof {
+        p.bump();
+    }
+    None
+}
+
 /// Interpreting `tts` as a comma-separated sequence of expressions,
 /// expect exactly one string literal, or emit an error and return `None`.
 pub fn get_single_str_from_tts(
@@ -1181,7 +1178,7 @@ pub fn get_single_str_from_tts(
         cx.span_err(sp, &format!("{} takes 1 argument", name));
         return None;
     }
-    let ret = panictry!(p.parse_expr());
+    let ret = parse_expr(&mut p)?;
     let _ = p.eat(&token::Comma);
 
     if p.token != token::Eof {
@@ -1190,8 +1187,8 @@ pub fn get_single_str_from_tts(
     expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s.to_string())
 }
 
-/// Extracts comma-separated expressions from `tts`. If there is a
-/// parsing error, emit a non-fatal error and return `None`.
+/// Extracts comma-separated expressions from `tts`.
+/// On error, emit it, and return `None`.
 pub fn get_exprs_from_tts(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -1200,7 +1197,7 @@ pub fn get_exprs_from_tts(
     let mut p = cx.new_parser_from_tts(tts);
     let mut es = Vec::new();
     while p.token != token::Eof {
-        let expr = panictry!(p.parse_expr());
+        let expr = parse_expr(&mut p)?;
 
         // Perform eager expansion on the expression.
         // We want to be able to handle e.g., `concat!("foo", "bar")`.
diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs
index 4f568e5456c72..51208906c2f2d 100644
--- a/src/librustc_expand/expand.rs
+++ b/src/librustc_expand/expand.rs
@@ -204,7 +204,7 @@ ast_fragments! {
 }
 
 impl AstFragmentKind {
-    fn dummy(self, span: Span) -> AstFragment {
+    crate fn dummy(self, span: Span) -> AstFragment {
         self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment")
     }
 
@@ -682,7 +682,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             InvocationKind::Bang { mac, .. } => match ext {
                 SyntaxExtensionKind::Bang(expander) => {
                     self.gate_proc_macro_expansion_kind(span, fragment_kind);
-                    let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens());
+                    let tok_result = match expander.expand(self.cx, span, mac.args.inner_tokens()) {
+                        Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)),
+                        Ok(ts) => ts,
+                    };
                     self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
                 }
                 SyntaxExtensionKind::LegacyBang(expander) => {
@@ -709,8 +712,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     if let MacArgs::Eq(..) = attr_item.args {
                         self.cx.span_err(span, "key-value macro attributes are not supported");
                     }
-                    let tok_result =
-                        expander.expand(self.cx, span, attr_item.args.inner_tokens(), tokens);
+                    let inner_tokens = attr_item.args.inner_tokens();
+                    let tok_result = match expander.expand(self.cx, span, inner_tokens, tokens) {
+                        Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)),
+                        Ok(ts) => ts,
+                    };
                     self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span)
                 }
                 SyntaxExtensionKind::LegacyAttr(expander) => {
@@ -1139,6 +1145,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
             // macros are expanded before any lint passes so this warning has to be hardcoded
             if attr.has_name(sym::derive) {
                 self.cx
+                    .parse_sess()
+                    .span_diagnostic
                     .struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations")
                     .note("this may become a hard error in a future release")
                     .emit();
diff --git a/src/librustc_expand/lib.rs b/src/librustc_expand/lib.rs
index 0320a275e5d20..876a26de3fb7e 100644
--- a/src/librustc_expand/lib.rs
+++ b/src/librustc_expand/lib.rs
@@ -9,25 +9,6 @@
 
 extern crate proc_macro as pm;
 
-// A variant of 'try!' that panics on an Err. This is used as a crutch on the
-// way towards a non-panic!-prone parser. It should be used for fatal parsing
-// errors; eventually we plan to convert all code using panictry to just use
-// normal try.
-#[macro_export]
-macro_rules! panictry {
-    ($e:expr) => {{
-        use rustc_errors::FatalError;
-        use std::result::Result::{Err, Ok};
-        match $e {
-            Ok(e) => e,
-            Err(mut e) => {
-                e.emit();
-                FatalError.raise()
-            }
-        }
-    }};
-}
-
 mod placeholders;
 mod proc_macro_server;
 
diff --git a/src/librustc_expand/mbe/macro_parser.rs b/src/librustc_expand/mbe/macro_parser.rs
index 0d777d88cad3a..e868b7e36aac5 100644
--- a/src/librustc_expand/mbe/macro_parser.rs
+++ b/src/librustc_expand/mbe/macro_parser.rs
@@ -84,7 +84,7 @@ use rustc_parse::parser::{FollowedByType, Parser, PathStyle};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol};
 
-use rustc_errors::{FatalError, PResult};
+use rustc_errors::PResult;
 use rustc_span::Span;
 use smallvec::{smallvec, SmallVec};
 
@@ -271,6 +271,7 @@ crate enum ParseResult<T> {
     Failure(Token, &'static str),
     /// Fatal error (malformed macro?). Abort compilation.
     Error(rustc_span::Span, String),
+    ErrorReported,
 }
 
 /// A `ParseResult` where the `Success` variant contains a mapping of
@@ -652,6 +653,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na
             Success(_) => {}
             Failure(token, msg) => return Failure(token, msg),
             Error(sp, msg) => return Error(sp, msg),
+            ErrorReported => return ErrorReported,
         }
 
         // inner parse loop handled all cur_items, so it's empty
@@ -735,10 +737,11 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na
             let mut item = bb_items.pop().unwrap();
             if let TokenTree::MetaVarDecl(span, _, ident) = item.top_elts.get_tt(item.idx) {
                 let match_cur = item.match_cur;
-                item.push_match(
-                    match_cur,
-                    MatchedNonterminal(Lrc::new(parse_nt(parser.to_mut(), span, ident.name))),
-                );
+                let nt = match parse_nt(parser.to_mut(), span, ident.name) {
+                    Err(()) => return ErrorReported,
+                    Ok(nt) => nt,
+                };
+                item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt)));
                 item.idx += 1;
                 item.match_cur += 1;
             } else {
@@ -849,20 +852,16 @@ fn may_begin_with(token: &Token, name: Name) -> bool {
 /// # Returns
 ///
 /// The parsed non-terminal.
-fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Nonterminal {
+fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Result<Nonterminal, ()> {
     // FIXME(Centril): Consider moving this to `parser.rs` to make
     // the visibilities of the methods used below `pub(super)` at most.
-
     if name == sym::tt {
-        return token::NtTT(p.parse_token_tree());
-    }
-    match parse_nt_inner(p, sp, name) {
-        Ok(nt) => nt,
-        Err(mut err) => {
-            err.emit();
-            FatalError.raise();
-        }
+        return Ok(token::NtTT(p.parse_token_tree()));
     }
+    parse_nt_inner(p, sp, name).map_err(|mut err| {
+        err.span_label(sp, format!("while parsing argument for this `{}` macro fragment", name))
+            .emit()
+    })
 }
 
 fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, Nonterminal> {
diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs
index b6b69400bad43..859362b5e29d3 100644
--- a/src/librustc_expand/mbe/macro_rules.rs
+++ b/src/librustc_expand/mbe/macro_rules.rs
@@ -4,7 +4,7 @@ use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstF
 use crate::mbe;
 use crate::mbe::macro_check;
 use crate::mbe::macro_parser::parse_tt;
-use crate::mbe::macro_parser::{Error, Failure, Success};
+use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success};
 use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq};
 use crate::mbe::transcribe::transcribe;
 
@@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, TransparencyError};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, DiagnosticBuilder, FatalError};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_feature::Features;
 use rustc_parse::parser::Parser;
 use rustc_session::parse::ParseSess;
@@ -83,41 +83,56 @@ fn suggest_slice_pat(e: &mut DiagnosticBuilder<'_>, site_span: Span, parser: &Pa
     );
 }
 
+fn emit_frag_parse_err(
+    mut e: DiagnosticBuilder<'_>,
+    parser: &Parser<'_>,
+    site_span: Span,
+    macro_ident: ast::Ident,
+    arm_span: Span,
+    kind: AstFragmentKind,
+) {
+    if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") {
+        if !e.span.is_dummy() {
+            // early end of macro arm (#52866)
+            e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
+        }
+        let msg = &e.message[0];
+        e.message[0] = (
+            format!(
+                "macro expansion ends with an incomplete expression: {}",
+                msg.0.replace(", found `<eof>`", ""),
+            ),
+            msg.1,
+        );
+    }
+    if e.span.is_dummy() {
+        // Get around lack of span in error (#30128)
+        e.replace_span_with(site_span);
+        if !parser.sess.source_map().is_imported(arm_span) {
+            e.span_label(arm_span, "in this macro arm");
+        }
+    } else if parser.sess.source_map().is_imported(parser.token.span) {
+        e.span_label(site_span, "in this macro invocation");
+    }
+    match kind {
+        AstFragmentKind::Pat if macro_ident.name == sym::vec => {
+            suggest_slice_pat(&mut e, site_span, parser);
+        }
+        _ => annotate_err_with_kind(&mut e, kind, site_span),
+    };
+    e.emit();
+}
+
 impl<'a> ParserAnyMacro<'a> {
     crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
         let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self;
-        let fragment = panictry!(parse_ast_fragment(parser, kind).map_err(|mut e| {
-            if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") {
-                if !e.span.is_dummy() {
-                    // early end of macro arm (#52866)
-                    e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
-                }
-                let msg = &e.message[0];
-                e.message[0] = (
-                    format!(
-                        "macro expansion ends with an incomplete expression: {}",
-                        msg.0.replace(", found `<eof>`", ""),
-                    ),
-                    msg.1,
-                );
+        let fragment = match parse_ast_fragment(parser, kind) {
+            Ok(f) => f,
+            Err(err) => {
+                emit_frag_parse_err(err, parser, site_span, macro_ident, arm_span, kind);
+                return kind.dummy(site_span);
             }
-            if e.span.is_dummy() {
-                // Get around lack of span in error (#30128)
-                e.replace_span_with(site_span);
-                if !parser.sess.source_map().is_imported(arm_span) {
-                    e.span_label(arm_span, "in this macro arm");
-                }
-            } else if parser.sess.source_map().is_imported(parser.token.span) {
-                e.span_label(site_span, "in this macro invocation");
-            }
-            match kind {
-                AstFragmentKind::Pat if macro_ident.name == sym::vec => {
-                    suggest_slice_pat(&mut e, site_span, parser);
-                }
-                _ => annotate_err_with_kind(&mut e, kind, site_span),
-            };
-            e
-        }));
+        };
 
         // We allow semicolons at the end of expressions -- e.g., the semicolon in
         // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`,
@@ -165,6 +180,14 @@ impl TTMacroExpander for MacroRulesMacroExpander {
     }
 }
 
+fn macro_rules_dummy_expander<'cx>(
+    _: &'cx mut ExtCtxt<'_>,
+    span: Span,
+    _: TokenStream,
+) -> Box<dyn MacResult + 'cx> {
+    DummyResult::any(span)
+}
+
 fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) {
     let sp = sp.macro_backtrace().last().map(|trace| trace.call_site).unwrap_or(sp);
     cx_expansions.entry(sp).or_default().push(message);
@@ -240,7 +263,13 @@ fn generic_extension<'cx>(
 
                 let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
                 // rhs has holes ( `$id` and `$(...)` that need filled)
-                let mut tts = transcribe(cx, &named_matches, rhs, transparency);
+                let mut tts = match transcribe(cx, &named_matches, rhs, transparency) {
+                    Ok(tts) => tts,
+                    Err(mut err) => {
+                        err.emit();
+                        return DummyResult::any(arm_span);
+                    }
+                };
 
                 // Replace all the tokens for the corresponding positions in the macro, to maintain
                 // proper positions in error reporting, while maintaining the macro_backtrace.
@@ -278,7 +307,12 @@ fn generic_extension<'cx>(
                 Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
                 _ => best_failure = Some((token, msg)),
             },
-            Error(err_sp, ref msg) => cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]),
+            Error(err_sp, ref msg) => {
+                let span = err_sp.substitute_dummy(sp);
+                cx.struct_span_err(span, &msg).emit();
+                return DummyResult::any(span);
+            }
+            ErrorReported => return DummyResult::any(sp),
         }
 
         // The matcher was not `Success(..)`ful.
@@ -337,6 +371,18 @@ pub fn compile_declarative_macro(
     def: &ast::Item,
     edition: Edition,
 ) -> SyntaxExtension {
+    let mk_syn_ext = |expander| {
+        SyntaxExtension::new(
+            sess,
+            SyntaxExtensionKind::LegacyBang(expander),
+            def.span,
+            Vec::new(),
+            edition,
+            def.ident.name,
+            &def.attrs,
+        )
+    };
+
     let diag = &sess.span_diagnostic;
     let lhs_nm = ast::Ident::new(sym::lhs, def.span);
     let rhs_nm = ast::Ident::new(sym::rhs, def.span);
@@ -391,13 +437,15 @@ pub fn compile_declarative_macro(
         Failure(token, msg) => {
             let s = parse_failure_msg(&token);
             let sp = token.span.substitute_dummy(def.span);
-            let mut err = sess.span_diagnostic.struct_span_fatal(sp, &s);
-            err.span_label(sp, msg);
-            err.emit();
-            FatalError.raise();
+            sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
+            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
+        }
+        Error(sp, msg) => {
+            sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit();
+            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
         }
-        Error(sp, s) => {
-            sess.span_diagnostic.span_fatal(sp.substitute_dummy(def.span), &s).raise();
+        ErrorReported => {
+            return mk_syn_ext(Box::new(macro_rules_dummy_expander));
         }
     };
 
@@ -460,24 +508,14 @@ pub fn compile_declarative_macro(
         None => {}
     }
 
-    let expander: Box<_> = Box::new(MacroRulesMacroExpander {
+    mk_syn_ext(Box::new(MacroRulesMacroExpander {
         name: def.ident,
         span: def.span,
         transparency,
         lhses,
         rhses,
         valid,
-    });
-
-    SyntaxExtension::new(
-        sess,
-        SyntaxExtensionKind::LegacyBang(expander),
-        def.span,
-        Vec::new(),
-        edition,
-        def.ident.name,
-        &def.attrs,
-    )
+    }))
 }
 
 fn check_lhs_nt_follows(
diff --git a/src/librustc_expand/mbe/transcribe.rs b/src/librustc_expand/mbe/transcribe.rs
index 1b1093c9529f4..e2d3d5c4d644e 100644
--- a/src/librustc_expand/mbe/transcribe.rs
+++ b/src/librustc_expand/mbe/transcribe.rs
@@ -8,7 +8,7 @@ use rustc_ast::token::{self, NtTT, Token};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::pluralize;
+use rustc_errors::{pluralize, PResult};
 use rustc_span::hygiene::{ExpnId, Transparency};
 use rustc_span::symbol::MacroRulesNormalizedIdent;
 use rustc_span::Span;
@@ -80,15 +80,15 @@ impl Iterator for Frame {
 /// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`.
 ///
 /// Along the way, we do some additional error checking.
-pub(super) fn transcribe(
-    cx: &ExtCtxt<'_>,
+pub(super) fn transcribe<'a>(
+    cx: &ExtCtxt<'a>,
     interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
     src: Vec<mbe::TokenTree>,
     transparency: Transparency,
-) -> TokenStream {
+) -> PResult<'a, TokenStream> {
     // Nothing for us to transcribe...
     if src.is_empty() {
-        return TokenStream::default();
+        return Ok(TokenStream::default());
     }
 
     // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
@@ -152,7 +152,7 @@ pub(super) fn transcribe(
                 Frame::Delimited { forest, span, .. } => {
                     if result_stack.is_empty() {
                         // No results left to compute! We are back at the top-level.
-                        return TokenStream::new(result);
+                        return Ok(TokenStream::new(result));
                     }
 
                     // Step back into the parent Delimited.
@@ -173,11 +173,11 @@ pub(super) fn transcribe(
             seq @ mbe::TokenTree::Sequence(..) => {
                 match lockstep_iter_size(&seq, interp, &repeats) {
                     LockstepIterSize::Unconstrained => {
-                        cx.span_fatal(
+                        return Err(cx.struct_span_err(
                             seq.span(), /* blame macro writer */
                             "attempted to repeat an expression containing no syntax variables \
                              matched as repeating at this depth",
-                        );
+                        ));
                     }
 
                     LockstepIterSize::Contradiction(ref msg) => {
@@ -185,7 +185,7 @@ pub(super) fn transcribe(
                         // happens when two meta-variables are used in the same repetition in a
                         // sequence, but they come from different sequence matchers and repeat
                         // different amounts.
-                        cx.span_fatal(seq.span(), &msg[..]);
+                        return Err(cx.struct_span_err(seq.span(), &msg[..]));
                     }
 
                     LockstepIterSize::Constraint(len, _) => {
@@ -203,7 +203,10 @@ pub(super) fn transcribe(
                                 // FIXME: this really ought to be caught at macro definition
                                 // time... It happens when the Kleene operator in the matcher and
                                 // the body for the same meta-variable do not match.
-                                cx.span_fatal(sp.entire(), "this must repeat at least once");
+                                return Err(cx.struct_span_err(
+                                    sp.entire(),
+                                    "this must repeat at least once",
+                                ));
                             }
                         } else {
                             // 0 is the initial counter (we have done 0 repretitions so far). `len`
@@ -242,10 +245,10 @@ pub(super) fn transcribe(
                         }
                     } else {
                         // We were unable to descend far enough. This is an error.
-                        cx.span_fatal(
+                        return Err(cx.struct_span_err(
                             sp, /* blame the macro writer */
                             &format!("variable '{}' is still repeating at this depth", ident),
-                        );
+                        ));
                     }
                 } else {
                     // If we aren't able to match the meta-var, we push it back into the result but
diff --git a/src/librustc_expand/proc_macro.rs b/src/librustc_expand/proc_macro.rs
index cb9afa4cd4f02..df7bf9438c3d0 100644
--- a/src/librustc_expand/proc_macro.rs
+++ b/src/librustc_expand/proc_macro.rs
@@ -5,7 +5,7 @@ use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
 use rustc_ast::token;
 use rustc_ast::tokenstream::{self, TokenStream};
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, FatalError};
+use rustc_errors::{Applicability, ErrorReported};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
 
@@ -21,21 +21,16 @@ impl base::ProcMacro for BangProcMacro {
         ecx: &'cx mut ExtCtxt<'_>,
         span: Span,
         input: TokenStream,
-    ) -> TokenStream {
+    ) -> Result<TokenStream, ErrorReported> {
         let server = proc_macro_server::Rustc::new(ecx);
-        match self.client.run(&EXEC_STRATEGY, server, input) {
-            Ok(stream) => stream,
-            Err(e) => {
-                let msg = "proc macro panicked";
-                let mut err = ecx.struct_span_fatal(span, msg);
-                if let Some(s) = e.as_str() {
-                    err.help(&format!("message: {}", s));
-                }
-
-                err.emit();
-                FatalError.raise();
+        self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| {
+            let mut err = ecx.struct_span_err(span, "proc macro panicked");
+            if let Some(s) = e.as_str() {
+                err.help(&format!("message: {}", s));
             }
-        }
+            err.emit();
+            ErrorReported
+        })
     }
 }
 
@@ -50,21 +45,16 @@ impl base::AttrProcMacro for AttrProcMacro {
         span: Span,
         annotation: TokenStream,
         annotated: TokenStream,
-    ) -> TokenStream {
+    ) -> Result<TokenStream, ErrorReported> {
         let server = proc_macro_server::Rustc::new(ecx);
-        match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
-            Ok(stream) => stream,
-            Err(e) => {
-                let msg = "custom attribute panicked";
-                let mut err = ecx.struct_span_fatal(span, msg);
-                if let Some(s) = e.as_str() {
-                    err.help(&format!("message: {}", s));
-                }
-
-                err.emit();
-                FatalError.raise();
+        self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| {
+            let mut err = ecx.struct_span_err(span, "custom attribute panicked");
+            if let Some(s) = e.as_str() {
+                err.help(&format!("message: {}", s));
             }
-        }
+            err.emit();
+            ErrorReported
+        })
     }
 }
 
@@ -96,8 +86,7 @@ impl MultiItemModifier for ProcMacroDerive {
             | Annotatable::Expr(_) => {
                 ecx.span_err(
                     span,
-                    "proc-macro derives may only be \
-                                    applied to a struct, enum, or union",
+                    "proc-macro derives may only be applied to a struct, enum, or union",
                 );
                 return ExpandResult::Ready(Vec::new());
             }
@@ -107,8 +96,7 @@ impl MultiItemModifier for ProcMacroDerive {
             _ => {
                 ecx.span_err(
                     span,
-                    "proc-macro derives may only be \
-                                    applied to a struct, enum, or union",
+                    "proc-macro derives may only be applied to a struct, enum, or union",
                 );
                 return ExpandResult::Ready(Vec::new());
             }
@@ -121,20 +109,16 @@ impl MultiItemModifier for ProcMacroDerive {
         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
             Ok(stream) => stream,
             Err(e) => {
-                let msg = "proc-macro derive panicked";
-                let mut err = ecx.struct_span_fatal(span, msg);
+                let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
                 if let Some(s) = e.as_str() {
                     err.help(&format!("message: {}", s));
                 }
-
                 err.emit();
-                FatalError.raise();
+                return ExpandResult::Ready(vec![]);
             }
         };
 
         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
-        let msg = "proc-macro derive produced unparseable tokens";
-
         let mut parser =
             rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
         let mut items = vec![];
@@ -144,18 +128,15 @@ impl MultiItemModifier for ProcMacroDerive {
                 Ok(None) => break,
                 Ok(Some(item)) => items.push(Annotatable::Item(item)),
                 Err(mut err) => {
-                    // FIXME: handle this better
-                    err.cancel();
-                    ecx.struct_span_fatal(span, msg).emit();
-                    FatalError.raise();
+                    err.emit();
+                    break;
                 }
             }
         }
 
         // fail if there have been errors emitted
         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
-            ecx.struct_span_fatal(span, msg).emit();
-            FatalError.raise();
+            ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
         }
 
         ExpandResult::Ready(items)
diff --git a/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs b/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs
index dbc0465b08e77..d5ed9fb9a285e 100644
--- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs
+++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs
@@ -1,9 +1,17 @@
 // edition:2018
 // aux-build:edition-kw-macro-2015.rs
 
+#![feature(async_closure)]
+
+fn main() {}
+
 #[macro_use]
 extern crate edition_kw_macro_2015;
 
+mod module {
+    pub fn r#async() {}
+}
+
 pub fn check_async() {
     let mut async = 1; //~ ERROR expected identifier, found keyword `async`
     let mut r#async = 1; // OK
@@ -17,4 +25,6 @@ pub fn check_async() {
     if passes_ident!(r#async) == 1 {} // OK
     module::async(); //~ ERROR expected identifier, found keyword `async`
     module::r#async(); // OK
+
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
 }
diff --git a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
index e12d1a48463d7..28663563c6ccd 100644
--- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `async`
-  --> $DIR/edition-keywords-2018-2015-parsing.rs:8:13
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:16:13
    |
 LL |     let mut async = 1;
    |             ^^^^^ expected identifier, found keyword
@@ -10,7 +10,7 @@ LL |     let mut r#async = 1;
    |             ^^^^^^^
 
 error: expected identifier, found keyword `async`
-  --> $DIR/edition-keywords-2018-2015-parsing.rs:18:13
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:26:13
    |
 LL |     module::async();
    |             ^^^^^ expected identifier, found keyword
@@ -21,13 +21,13 @@ LL |     module::r#async();
    |             ^^^^^^^
 
 error: no rules expected the token `r#async`
-  --> $DIR/edition-keywords-2018-2015-parsing.rs:12:31
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:20:31
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
 
 error: no rules expected the token `async`
-  --> $DIR/edition-keywords-2018-2015-parsing.rs:13:35
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:21:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
@@ -38,10 +38,19 @@ error: macro expansion ends with an incomplete expression: expected one of `move
 LL |     ($i: ident) => ($i)
    |                       ^ expected one of `move`, `|`, or `||`
    | 
-  ::: $DIR/edition-keywords-2018-2015-parsing.rs:16:8
+  ::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8
    |
 LL |     if passes_ident!(async) == 1 {}
    |        -------------------- in this macro invocation
 
-error: aborting due to 5 previous errors
+error[E0308]: mismatched types
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 6 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs b/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs
index 5aca0839f0f15..044ab249f2c26 100644
--- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs
+++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs
@@ -1,9 +1,17 @@
 // edition:2018
 // aux-build:edition-kw-macro-2018.rs
 
+#![feature(async_closure)]
+
+fn main() {}
+
 #[macro_use]
 extern crate edition_kw_macro_2018;
 
+mod module {
+    pub fn r#async() {}
+}
+
 pub fn check_async() {
     let mut async = 1; //~ ERROR expected identifier, found keyword `async`
     let mut r#async = 1; // OK
@@ -17,4 +25,6 @@ pub fn check_async() {
     if passes_ident!(r#async) == 1 {} // OK
     module::async(); //~ ERROR expected identifier, found keyword `async`
     module::r#async(); // OK
+
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
 }
diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
index 110165fc077ca..cda7e65e437e8 100644
--- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `async`
-  --> $DIR/edition-keywords-2018-2018-parsing.rs:8:13
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:16:13
    |
 LL |     let mut async = 1;
    |             ^^^^^ expected identifier, found keyword
@@ -10,7 +10,7 @@ LL |     let mut r#async = 1;
    |             ^^^^^^^
 
 error: expected identifier, found keyword `async`
-  --> $DIR/edition-keywords-2018-2018-parsing.rs:18:13
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:26:13
    |
 LL |     module::async();
    |             ^^^^^ expected identifier, found keyword
@@ -21,13 +21,13 @@ LL |     module::r#async();
    |             ^^^^^^^
 
 error: no rules expected the token `r#async`
-  --> $DIR/edition-keywords-2018-2018-parsing.rs:12:31
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:20:31
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
 
 error: no rules expected the token `async`
-  --> $DIR/edition-keywords-2018-2018-parsing.rs:13:35
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
@@ -38,10 +38,19 @@ error: macro expansion ends with an incomplete expression: expected one of `move
 LL |     ($i: ident) => ($i)
    |                       ^ expected one of `move`, `|`, or `||`
    | 
-  ::: $DIR/edition-keywords-2018-2018-parsing.rs:16:8
+  ::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8
    |
 LL |     if passes_ident!(async) == 1 {}
    |        -------------------- in this macro invocation
 
-error: aborting due to 5 previous errors
+error[E0308]: mismatched types
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:29:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 6 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/macros/issue-61033-1.rs b/src/test/ui/macros/issue-61033-1.rs
index 8f85dec017f2a..18df3f6ee94c1 100644
--- a/src/test/ui/macros/issue-61033-1.rs
+++ b/src/test/ui/macros/issue-61033-1.rs
@@ -1,9 +1,10 @@
 // Regression test for issue #61033.
 
 macro_rules! test1 {
-    ($x:ident, $($tt:tt)*) => { $($tt)+ } //~ERROR this must repeat at least once
+    ($x:ident, $($tt:tt)*) => { $($tt)+ } //~ ERROR this must repeat at least once
 }
 
 fn main() {
     test1!(x,);
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
 }
diff --git a/src/test/ui/macros/issue-61033-1.stderr b/src/test/ui/macros/issue-61033-1.stderr
index f3c68f4928dbb..18205c3436b0b 100644
--- a/src/test/ui/macros/issue-61033-1.stderr
+++ b/src/test/ui/macros/issue-61033-1.stderr
@@ -4,5 +4,14 @@ error: this must repeat at least once
 LL |     ($x:ident, $($tt:tt)*) => { $($tt)+ }
    |                                  ^^^^^
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/issue-61033-1.rs:9:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/macros/issue-61033-2.rs b/src/test/ui/macros/issue-61033-2.rs
index 0799be10b96c7..1760ba1584d0b 100644
--- a/src/test/ui/macros/issue-61033-2.rs
+++ b/src/test/ui/macros/issue-61033-2.rs
@@ -5,7 +5,9 @@ macro_rules! test2 {
         $(* $id1:ident)*
         $(+ $id2:ident)*
     ) => {
-        $( //~ERROR meta-variable `id1` repeats 2 times
+        $(
+        //~^ ERROR meta-variable `id1` repeats 2 times
+        //~| ERROR meta-variable `id1` repeats 2 times
             $id1 + $id2 // $id1 and $id2 may repeat different numbers of times
         )*
     }
@@ -16,4 +18,8 @@ fn main() {
         * a * b
         + a + b + c
     }
+    test2! {
+        * a * b
+        + a + b + c + d
+    }
 }
diff --git a/src/test/ui/macros/issue-61033-2.stderr b/src/test/ui/macros/issue-61033-2.stderr
index bf502919cf794..cdfe7934a0cac 100644
--- a/src/test/ui/macros/issue-61033-2.stderr
+++ b/src/test/ui/macros/issue-61033-2.stderr
@@ -3,9 +3,22 @@ error: meta-variable `id1` repeats 2 times, but `id2` repeats 3 times
    |
 LL |           $(
    |  __________^
+LL | |
+LL | |
 LL | |             $id1 + $id2 // $id1 and $id2 may repeat different numbers of times
 LL | |         )*
    | |_________^
 
-error: aborting due to previous error
+error: meta-variable `id1` repeats 2 times, but `id2` repeats 4 times
+  --> $DIR/issue-61033-2.rs:8:10
+   |
+LL |           $(
+   |  __________^
+LL | |
+LL | |
+LL | |             $id1 + $id2 // $id1 and $id2 may repeat different numbers of times
+LL | |         )*
+   | |_________^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/macros/local-ambiguity-multiple-parsing-options.rs b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.rs
new file mode 100644
index 0000000000000..3967481098cff
--- /dev/null
+++ b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.rs
@@ -0,0 +1,8 @@
+fn main() {}
+
+macro_rules! ambiguity {
+    ($($i:ident)* $j:ident) => {};
+}
+
+ambiguity!(error); //~ ERROR local ambiguity
+ambiguity!(error); //~ ERROR local ambiguity
diff --git a/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr
new file mode 100644
index 0000000000000..0ae56c422213c
--- /dev/null
+++ b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr
@@ -0,0 +1,14 @@
+error: local ambiguity: multiple parsing options: built-in NTs ident ('i') or ident ('j').
+  --> $DIR/local-ambiguity-multiple-parsing-options.rs:7:12
+   |
+LL | ambiguity!(error);
+   |            ^^^^^
+
+error: local ambiguity: multiple parsing options: built-in NTs ident ('i') or ident ('j').
+  --> $DIR/local-ambiguity-multiple-parsing-options.rs:8:12
+   |
+LL | ambiguity!(error);
+   |            ^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/macros/macro-context.rs b/src/test/ui/macros/macro-context.rs
index 9130c3d921c52..13e179578ad01 100644
--- a/src/test/ui/macros/macro-context.rs
+++ b/src/test/ui/macros/macro-context.rs
@@ -4,6 +4,8 @@ macro_rules! m {
                             //~| ERROR macro expansion ignores token `typeof`
                             //~| ERROR macro expansion ignores token `;`
                             //~| ERROR macro expansion ignores token `;`
+                            //~| ERROR cannot find type `i` in this scope
+                            //~| ERROR cannot find value `i` in this scope
 }
 
 fn main() {
diff --git a/src/test/ui/macros/macro-context.stderr b/src/test/ui/macros/macro-context.stderr
index 2e712110689f8..17c7389812475 100644
--- a/src/test/ui/macros/macro-context.stderr
+++ b/src/test/ui/macros/macro-context.stderr
@@ -42,5 +42,29 @@ LL |     m!();
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 4 previous errors
+error[E0412]: cannot find type `i` in this scope
+  --> $DIR/macro-context.rs:3:13
+   |
+LL |     () => ( i ; typeof );
+   |             ^ help: a builtin type with a similar name exists: `i8`
+...
+LL |     let a: m!();
+   |            ---- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0425]: cannot find value `i` in this scope
+  --> $DIR/macro-context.rs:3:13
+   |
+LL |     () => ( i ; typeof );
+   |             ^ help: a local variable with a similar name exists: `a`
+...
+LL |     let i = 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 6 previous errors
 
+Some errors have detailed explanations: E0412, E0425.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/src/test/ui/macros/macro-match-nonterminal.rs b/src/test/ui/macros/macro-match-nonterminal.rs
index 6d4b32c9bc9a0..b23e5c71c03f0 100644
--- a/src/test/ui/macros/macro-match-nonterminal.rs
+++ b/src/test/ui/macros/macro-match-nonterminal.rs
@@ -1,4 +1,11 @@
-macro_rules! test { ($a, $b) => (()); } //~ ERROR missing fragment
+macro_rules! test {
+    ($a, $b) => {
+        //~^ ERROR missing fragment
+        //~| ERROR missing fragment
+        //~| WARN this was previously accepted
+        ()
+    };
+}
 
 fn main() {
     test!()
diff --git a/src/test/ui/macros/macro-match-nonterminal.stderr b/src/test/ui/macros/macro-match-nonterminal.stderr
index 1de8c5bd4b472..674ce3434aac6 100644
--- a/src/test/ui/macros/macro-match-nonterminal.stderr
+++ b/src/test/ui/macros/macro-match-nonterminal.stderr
@@ -1,8 +1,18 @@
 error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:1:24
+  --> $DIR/macro-match-nonterminal.rs:2:8
    |
-LL | macro_rules! test { ($a, $b) => (()); }
-   |                        ^
+LL |     ($a, $b) => {
+   |        ^
 
-error: aborting due to previous error
+error: missing fragment specifier
+  --> $DIR/macro-match-nonterminal.rs:2:10
+   |
+LL |     ($a, $b) => {
+   |          ^^
+   |
+   = note: `#[deny(missing_fragment_specifier)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/macros/trace_faulty_macros.rs b/src/test/ui/macros/trace_faulty_macros.rs
index a55f05414b206..5a8e2f50ce33d 100644
--- a/src/test/ui/macros/trace_faulty_macros.rs
+++ b/src/test/ui/macros/trace_faulty_macros.rs
@@ -1,6 +1,6 @@
 // compile-flags: -Z trace-macros
 
-#![recursion_limit="4"]
+#![recursion_limit = "4"]
 
 macro_rules! my_faulty_macro {
     () => {
@@ -24,9 +24,7 @@ macro_rules! my_recursive_macro {
 }
 
 macro_rules! my_macro {
-    () => {
-
-    };
+    () => {};
 }
 
 fn main() {
@@ -39,7 +37,7 @@ fn main() {
 }
 
 #[my_macro]
-fn use_bang_macro_as_attr(){}
+fn use_bang_macro_as_attr() {}
 
-#[derive(Debug)]
-fn use_derive_macro_as_attr(){}
+#[derive(Debug)] //~ ERROR `derive` may only be applied to structs
+fn use_derive_macro_as_attr() {}
diff --git a/src/test/ui/macros/trace_faulty_macros.stderr b/src/test/ui/macros/trace_faulty_macros.stderr
index 109b493b43717..aec9d1ab191af 100644
--- a/src/test/ui/macros/trace_faulty_macros.stderr
+++ b/src/test/ui/macros/trace_faulty_macros.stderr
@@ -13,7 +13,7 @@ LL |     my_faulty_macro!();
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: trace_macro
-  --> $DIR/trace_faulty_macros.rs:33:5
+  --> $DIR/trace_faulty_macros.rs:31:5
    |
 LL |     my_faulty_macro!();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -35,7 +35,7 @@ LL |     my_recursive_macro!();
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: trace_macro
-  --> $DIR/trace_faulty_macros.rs:34:5
+  --> $DIR/trace_faulty_macros.rs:32:5
    |
 LL |     my_recursive_macro!();
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -60,5 +60,22 @@ LL |     let a = pat_macro!();
    |
    = 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
+error: `derive` may only be applied to structs, enums and unions
+  --> $DIR/trace_faulty_macros.rs:42:1
+   |
+LL | #[derive(Debug)]
+   | ^^^^^^^^^^^^^^^^
+
+note: trace_macro
+  --> $DIR/trace_faulty_macros.rs:36:13
+   |
+LL |     let a = pat_macro!();
+   |             ^^^^^^^^^^^^
+   |
+   = note: expanding `pat_macro! {  }`
+   = note: to `pat_macro ! (A { a : a, b : 0, c : _, .. }) ;`
+   = note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }`
+   = note: to `A { a: a, b: 0, c: _, .. }`
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/parser/issue-62894.rs b/src/test/ui/parser/issue-62894.rs
index b9c0bf834ddb2..e38b7b6508937 100644
--- a/src/test/ui/parser/issue-62894.rs
+++ b/src/test/ui/parser/issue-62894.rs
@@ -1,3 +1,8 @@
+// FIXME: missing sysroot spans (#53081)
+// ignore-i586-unknown-linux-gnu
+// ignore-i586-unknown-linux-musl
+// ignore-i686-unknown-linux-musl
+
 // Regression test for #62894, shouldn't crash.
 // error-pattern: this file contains an unclosed delimiter
 // error-pattern: expected one of `(`, `[`, or `{`, found keyword `fn`
diff --git a/src/test/ui/parser/issue-62894.stderr b/src/test/ui/parser/issue-62894.stderr
index 6db380f7a7fe2..4a1d7e275bed8 100644
--- a/src/test/ui/parser/issue-62894.stderr
+++ b/src/test/ui/parser/issue-62894.stderr
@@ -1,5 +1,5 @@
 error: this file contains an unclosed delimiter
-  --> $DIR/issue-62894.rs:7:14
+  --> $DIR/issue-62894.rs:12:14
    |
 LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq!
    |        -           -                   - unclosed delimiter
@@ -11,7 +11,7 @@ LL | fn main() {}
    |              ^
 
 error: this file contains an unclosed delimiter
-  --> $DIR/issue-62894.rs:7:14
+  --> $DIR/issue-62894.rs:12:14
    |
 LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq!
    |        -           -                   - unclosed delimiter
@@ -23,7 +23,7 @@ LL | fn main() {}
    |              ^
 
 error: this file contains an unclosed delimiter
-  --> $DIR/issue-62894.rs:7:14
+  --> $DIR/issue-62894.rs:12:14
    |
 LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq!
    |        -           -                   - unclosed delimiter
@@ -35,13 +35,18 @@ LL | fn main() {}
    |              ^
 
 error: expected one of `(`, `[`, or `{`, found keyword `fn`
-  --> $DIR/issue-62894.rs:7:1
+  --> $DIR/issue-62894.rs:12:1
    |
 LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq!
    |                                                   - expected one of `(`, `[`, or `{`
 LL | 
 LL | fn main() {}
    | ^^ unexpected token
+   | 
+  ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL
+   |
+LL |     ($left:expr, $right:expr) => ({
+   |      ---------- while parsing argument for this `expr` macro fragment
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/parser/macro/issue-33569.rs b/src/test/ui/parser/macro/issue-33569.rs
index 9ed53519ceb31..80e2d7c6545ba 100644
--- a/src/test/ui/parser/macro/issue-33569.rs
+++ b/src/test/ui/parser/macro/issue-33569.rs
@@ -6,3 +6,5 @@ macro_rules! foo {
 }
 
 foo!();
+
+fn main() {}
diff --git a/src/test/ui/parser/macro/macro-repeat.rs b/src/test/ui/parser/macro/macro-repeat.rs
index 580a1daacbf31..3ffbea217e79e 100644
--- a/src/test/ui/parser/macro/macro-repeat.rs
+++ b/src/test/ui/parser/macro/macro-repeat.rs
@@ -1,9 +1,12 @@
 macro_rules! mac {
-    ( $($v:tt)* ) => (
-        $v  //~ ERROR still repeating at this depth
-    )
+    ( $($v:tt)* ) => {
+        $v
+        //~^ ERROR still repeating at this depth
+        //~| ERROR still repeating at this depth
+    };
 }
 
 fn main() {
     mac!(0);
+    mac!(1);
 }
diff --git a/src/test/ui/parser/macro/macro-repeat.stderr b/src/test/ui/parser/macro/macro-repeat.stderr
index c86684de74432..63554b197b91c 100644
--- a/src/test/ui/parser/macro/macro-repeat.stderr
+++ b/src/test/ui/parser/macro/macro-repeat.stderr
@@ -4,5 +4,11 @@ error: variable 'v' is still repeating at this depth
 LL |         $v
    |         ^^
 
-error: aborting due to previous error
+error: variable 'v' is still repeating at this depth
+  --> $DIR/macro-repeat.rs:3:9
+   |
+LL |         $v
+   |         ^^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/parser/nt-parsing-has-recovery.rs b/src/test/ui/parser/nt-parsing-has-recovery.rs
new file mode 100644
index 0000000000000..ccbeb398af5ba
--- /dev/null
+++ b/src/test/ui/parser/nt-parsing-has-recovery.rs
@@ -0,0 +1,10 @@
+macro_rules! foo {
+    ($e:expr) => {}
+}
+
+foo!(1 + @); //~ ERROR expected expression, found `@`
+foo!(1 + @); //~ ERROR expected expression, found `@`
+
+fn main() {
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/parser/nt-parsing-has-recovery.stderr b/src/test/ui/parser/nt-parsing-has-recovery.stderr
new file mode 100644
index 0000000000000..263c4ad53612e
--- /dev/null
+++ b/src/test/ui/parser/nt-parsing-has-recovery.stderr
@@ -0,0 +1,29 @@
+error: expected expression, found `@`
+  --> $DIR/nt-parsing-has-recovery.rs:5:10
+   |
+LL |     ($e:expr) => {}
+   |      ------- while parsing argument for this `expr` macro fragment
+...
+LL | foo!(1 + @);
+   |          ^ expected expression
+
+error: expected expression, found `@`
+  --> $DIR/nt-parsing-has-recovery.rs:6:10
+   |
+LL |     ($e:expr) => {}
+   |      ------- while parsing argument for this `expr` macro fragment
+...
+LL | foo!(1 + @);
+   |          ^ expected expression
+
+error[E0308]: mismatched types
+  --> $DIR/nt-parsing-has-recovery.rs:9:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/proc-macro/derive-bad.rs b/src/test/ui/proc-macro/derive-bad.rs
index 62c0741b6697a..cb5188b5fb43f 100644
--- a/src/test/ui/proc-macro/derive-bad.rs
+++ b/src/test/ui/proc-macro/derive-bad.rs
@@ -3,11 +3,9 @@
 #[macro_use]
 extern crate derive_bad;
 
-#[derive(
-    A
-)]
-//~^^ ERROR proc-macro derive produced unparseable tokens
+#[derive(A)]
+//~^ ERROR proc-macro derive produced unparseable tokens
 //~| ERROR expected `:`, found `}`
-struct A;
+struct A; //~ ERROR the name `A` is defined multiple times
 
 fn main() {}
diff --git a/src/test/ui/proc-macro/derive-bad.stderr b/src/test/ui/proc-macro/derive-bad.stderr
index 8667396c98989..bc5ed9815238a 100644
--- a/src/test/ui/proc-macro/derive-bad.stderr
+++ b/src/test/ui/proc-macro/derive-bad.stderr
@@ -1,16 +1,28 @@
 error: expected `:`, found `}`
-  --> $DIR/derive-bad.rs:7:5
+  --> $DIR/derive-bad.rs:6:10
    |
-LL |     A
-   |     ^ expected `:`
+LL | #[derive(A)]
+   |          ^ expected `:`
    |
    = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: proc-macro derive produced unparseable tokens
-  --> $DIR/derive-bad.rs:7:5
+  --> $DIR/derive-bad.rs:6:10
    |
-LL |     A
-   |     ^
+LL | #[derive(A)]
+   |          ^
 
-error: aborting due to 2 previous errors
+error[E0428]: the name `A` is defined multiple times
+  --> $DIR/derive-bad.rs:9:1
+   |
+LL | #[derive(A)]
+   |          - previous definition of the type `A` here
+...
+LL | struct A;
+   | ^^^^^^^^^ `A` redefined here
+   |
+   = note: `A` must be defined only once in the type namespace of this module
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0428`.
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-1.rs b/src/test/ui/proc-macro/invalid-punct-ident-1.rs
index 9de57da5af434..3f78dea917b19 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-1.rs
+++ b/src/test/ui/proc-macro/invalid-punct-ident-1.rs
@@ -14,3 +14,5 @@
 extern crate invalid_punct_ident;
 
 invalid_punct!(); //~ ERROR proc macro panicked
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-2.rs b/src/test/ui/proc-macro/invalid-punct-ident-2.rs
index 79f72324b1d89..4e89e80ae7c41 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-2.rs
+++ b/src/test/ui/proc-macro/invalid-punct-ident-2.rs
@@ -14,3 +14,5 @@
 extern crate invalid_punct_ident;
 
 invalid_ident!(); //~ ERROR proc macro panicked
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-3.rs b/src/test/ui/proc-macro/invalid-punct-ident-3.rs
index d01e9b699cac5..8d8ce8f932e71 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-3.rs
+++ b/src/test/ui/proc-macro/invalid-punct-ident-3.rs
@@ -14,3 +14,5 @@
 extern crate invalid_punct_ident;
 
 invalid_raw_ident!(); //~ ERROR proc macro panicked
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-4.rs b/src/test/ui/proc-macro/invalid-punct-ident-4.rs
index e50f24deae343..59b347dac679c 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-4.rs
+++ b/src/test/ui/proc-macro/invalid-punct-ident-4.rs
@@ -3,5 +3,10 @@
 #[macro_use]
 extern crate invalid_punct_ident;
 
-lexer_failure!(); //~ ERROR proc macro panicked
-                  //~| ERROR unexpected closing delimiter: `)`
+lexer_failure!();
+//~^ ERROR proc macro panicked
+//~| ERROR unexpected closing delimiter: `)`
+
+fn main() {
+    let _recovery_witness: () = 0; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-4.stderr b/src/test/ui/proc-macro/invalid-punct-ident-4.stderr
index fe3e55b31be5d..3b357aecea864 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-4.stderr
+++ b/src/test/ui/proc-macro/invalid-punct-ident-4.stderr
@@ -12,5 +12,14 @@ error: proc macro panicked
 LL | lexer_failure!();
    | ^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error[E0308]: mismatched types
+  --> $DIR/invalid-punct-ident-4.rs:11:33
+   |
+LL |     let _recovery_witness: () = 0;
+   |                            --   ^ expected `()`, found integer
+   |                            |
+   |                            expected due to this
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/proc-macro/issue-36935.rs b/src/test/ui/proc-macro/issue-36935.rs
index f809592d5f449..5c43a564c00c2 100644
--- a/src/test/ui/proc-macro/issue-36935.rs
+++ b/src/test/ui/proc-macro/issue-36935.rs
@@ -5,6 +5,7 @@ extern crate test_macros;
 
 #[derive(Identity, Panic)] //~ ERROR proc-macro derive panicked
 struct Baz {
+    //~^ ERROR the name `Baz` is defined multiple times
     a: i32,
     b: i32,
 }
diff --git a/src/test/ui/proc-macro/issue-36935.stderr b/src/test/ui/proc-macro/issue-36935.stderr
index da4366eb668d6..2b2e28fdb2fda 100644
--- a/src/test/ui/proc-macro/issue-36935.stderr
+++ b/src/test/ui/proc-macro/issue-36935.stderr
@@ -6,5 +6,17 @@ LL | #[derive(Identity, Panic)]
    |
    = help: message: panic-derive
 
-error: aborting due to previous error
+error[E0428]: the name `Baz` is defined multiple times
+  --> $DIR/issue-36935.rs:7:1
+   |
+LL | struct Baz {
+   | ^^^^^^^^^^
+   | |
+   | `Baz` redefined here
+   | previous definition of the type `Baz` here
+   |
+   = note: `Baz` must be defined only once in the type namespace of this module
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0428`.
diff --git a/src/test/ui/span/transitive-dep-span.rs b/src/test/ui/span/transitive-dep-span.rs
index b445d389c561a..2d46f74ad9bc9 100644
--- a/src/test/ui/span/transitive-dep-span.rs
+++ b/src/test/ui/span/transitive-dep-span.rs
@@ -11,3 +11,5 @@
 extern crate transitive_dep_two;
 
 transitive_dep_two::parse_error!(); //~ ERROR expected one of
+
+fn main() {}
diff --git a/src/test/ui/test-attrs/test-attr-non-associated-functions.rs b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
index e475f5b4a75a5..31e567c396067 100644
--- a/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
+++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
@@ -6,7 +6,13 @@ struct A {}
 
 impl A {
     #[test]
-    fn new() -> A { //~ ERROR `#[test]` attribute is only allowed on non associated functions
+    fn new() -> A {
+        //~^ ERROR `#[test]` attribute is only allowed on non associated functions
+        A {}
+    }
+    #[test]
+    fn recovery_witness() -> A {
+        //~^ ERROR `#[test]` attribute is only allowed on non associated functions
         A {}
     }
 }
diff --git a/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
index cb3ae51823e45..a81b8f3980c37 100644
--- a/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
+++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
@@ -2,9 +2,19 @@ error: `#[test]` attribute is only allowed on non associated functions
   --> $DIR/test-attr-non-associated-functions.rs:9:5
    |
 LL | /     fn new() -> A {
+LL | |
 LL | |         A {}
 LL | |     }
    | |_____^
 
-error: aborting due to previous error
+error: `#[test]` attribute is only allowed on non associated functions
+  --> $DIR/test-attr-non-associated-functions.rs:14:5
+   |
+LL | /     fn recovery_witness() -> A {
+LL | |
+LL | |         A {}
+LL | |     }
+   | |_____^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/type/ascription/issue-47666.rs b/src/test/ui/type/ascription/issue-47666.rs
index ceb1dd89daea9..8035de4a48a92 100644
--- a/src/test/ui/type/ascription/issue-47666.rs
+++ b/src/test/ui/type/ascription/issue-47666.rs
@@ -1,5 +1,7 @@
 fn main() {
     let _ = Option:Some(vec![0, 1]); //~ ERROR expected type, found
+    //~^ ERROR expected value, found enum `Option`
+    //~| ERROR expected type, found variant `Some`
 }
 
 // This case isn't currently being handled gracefully due to the macro invocation.
diff --git a/src/test/ui/type/ascription/issue-47666.stderr b/src/test/ui/type/ascription/issue-47666.stderr
index f4c9240ab5372..3cd3be70aa75b 100644
--- a/src/test/ui/type/ascription/issue-47666.stderr
+++ b/src/test/ui/type/ascription/issue-47666.stderr
@@ -13,5 +13,35 @@ LL |     let _ = Option:Some(vec![0, 1]);
    = note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to previous error
+error[E0423]: expected value, found enum `Option`
+  --> $DIR/issue-47666.rs:2:13
+   |
+LL |     let _ = Option:Some(vec![0, 1]);
+   |             ^^^^^^
+   |
+help: try using one of the enum's variants
+   |
+LL |     let _ = std::option::Option::None:Some(vec![0, 1]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = std::option::Option::Some:Some(vec![0, 1]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0573]: expected type, found variant `Some`
+  --> $DIR/issue-47666.rs:2:20
+   |
+LL |     let _ = Option:Some(vec![0, 1]);
+   |                    ^^^^^^^^^^^^^^^^ not a type
+   |
+help: try using the variant's enum
+   |
+LL |     let _ = Option:std::option::Option;
+   |                    ^^^^^^^^^^^^^^^^^^^
+help: maybe you meant to write a path separator here
+   |
+LL |     let _ = Option::Some(vec![0, 1]);
+   |                   ^^
+
+error: aborting due to 3 previous errors
 
+Some errors have detailed explanations: E0423, E0573.
+For more information about an error, try `rustc --explain E0423`.