diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 95abf55291506..85277bcca4df4 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -24,7 +24,7 @@ pub use UnsafeSource::*;
 
 use crate::ptr::P;
 use crate::token::{self, CommentKind, DelimToken};
-use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
+use crate::tokenstream::{DelimSpan, PreexpTokenStream, TokenStream, TokenTree};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
@@ -926,10 +926,8 @@ impl Stmt {
         self.kind = match self.kind {
             StmtKind::Expr(expr) => StmtKind::Semi(expr),
             StmtKind::MacCall(mac) => {
-                StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs }| MacCallStmt {
-                    mac,
-                    style: MacStmtStyle::Semicolon,
-                    attrs,
+                StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs, tokens }| {
+                    MacCallStmt { mac, style: MacStmtStyle::Semicolon, attrs, tokens }
                 }))
             }
             kind => kind,
@@ -973,6 +971,7 @@ pub struct MacCallStmt {
     pub mac: MacCall,
     pub style: MacStmtStyle,
     pub attrs: AttrVec,
+    pub tokens: Option<PreexpTokenStream>,
 }
 
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
@@ -998,6 +997,7 @@ pub struct Local {
     pub init: Option<P<Expr>>,
     pub span: Span,
     pub attrs: AttrVec,
+    pub tokens: Option<PreexpTokenStream>,
 }
 
 /// An arm of a 'match'.
@@ -1066,7 +1066,7 @@ pub struct Expr {
     pub kind: ExprKind,
     pub span: Span,
     pub attrs: AttrVec,
-    pub tokens: Option<TokenStream>,
+    pub tokens: Option<PreexpTokenStream>,
 }
 
 // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
@@ -2436,6 +2436,7 @@ pub struct Attribute {
     /// or the construct this attribute is contained within (inner).
     pub style: AttrStyle,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -2582,7 +2583,7 @@ pub struct Item<K = ItemKind> {
     ///
     /// Note that the tokens here do not include the outer attributes, but will
     /// include inner attributes.
-    pub tokens: Option<TokenStream>,
+    pub tokens: Option<PreexpTokenStream>,
 }
 
 impl Item {
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 2782869fb885a..c1dc03fa417f5 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Token};
-use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
+use crate::tokenstream::{DelimSpan, PreexpTokenStream, TokenStream, TokenTree, TreeAndSpacing};
 
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::source_map::{BytePos, Spanned};
@@ -334,7 +334,7 @@ pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attri
 }
 
 pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
-    Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span }
+    Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span, tokens: None }
 }
 
 /// Returns an inner attribute with the given value and span.
@@ -353,7 +353,13 @@ pub fn mk_doc_comment(
     data: Symbol,
     span: Span,
 ) -> Attribute {
-    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
+    Attribute {
+        kind: AttrKind::DocComment(comment_kind, data),
+        id: mk_attr_id(),
+        style,
+        span,
+        tokens: None,
+    }
 }
 
 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
@@ -585,6 +591,7 @@ impl NestedMetaItem {
 pub trait HasAttrs: Sized {
     fn attrs(&self) -> &[Attribute];
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream));
 }
 
 impl<T: HasAttrs> HasAttrs for Spanned<T> {
@@ -594,6 +601,9 @@ impl<T: HasAttrs> HasAttrs for Spanned<T> {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         self.node.visit_attrs(f);
     }
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        self.node.visit_tokens(f)
+    }
 }
 
 impl HasAttrs for Vec<Attribute> {
@@ -603,6 +613,7 @@ impl HasAttrs for Vec<Attribute> {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         f(self)
     }
+    fn visit_tokens(&mut self, _f: impl FnOnce(&mut PreexpTokenStream)) {}
 }
 
 impl HasAttrs for AttrVec {
@@ -616,6 +627,7 @@ impl HasAttrs for AttrVec {
             vec.into()
         });
     }
+    fn visit_tokens(&mut self, _f: impl FnOnce(&mut PreexpTokenStream)) {}
 }
 
 impl<T: HasAttrs + 'static> HasAttrs for P<T> {
@@ -625,6 +637,21 @@ impl<T: HasAttrs + 'static> HasAttrs for P<T> {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         (**self).visit_attrs(f);
     }
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        (**self).visit_tokens(f)
+    }
+}
+
+impl<T: HasAttrs> HasAttrs for Option<T> {
+    fn attrs(&self) -> &[Attribute] {
+        self.as_ref().map_or(&[], |inner| inner.attrs())
+    }
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        self.as_mut().map(|inner| inner.visit_attrs(f));
+    }
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        self.as_mut().map(|inner| inner.visit_tokens(f));
+    }
 }
 
 impl HasAttrs for StmtKind {
@@ -632,7 +659,8 @@ impl HasAttrs for StmtKind {
         match *self {
             StmtKind::Local(ref local) => local.attrs(),
             StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
-            StmtKind::Empty | StmtKind::Item(..) => &[],
+            StmtKind::Item(..) => &[],
+            StmtKind::Empty => &[],
             StmtKind::MacCall(ref mac) => mac.attrs.attrs(),
         }
     }
@@ -641,12 +669,24 @@ impl HasAttrs for StmtKind {
         match self {
             StmtKind::Local(local) => local.visit_attrs(f),
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
-            StmtKind::Empty | StmtKind::Item(..) => {}
+            StmtKind::Item(item) => item.visit_attrs(f),
+            StmtKind::Empty => {}
             StmtKind::MacCall(mac) => {
                 mac.attrs.visit_attrs(f);
             }
         }
     }
+
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        match self {
+            StmtKind::Local(local) => local.visit_tokens(f),
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_tokens(f),
+            // FIXME: Is this correct?
+            StmtKind::Item(item) => item.visit_tokens(f),
+            StmtKind::Empty => {}
+            StmtKind::MacCall(mac) => mac.attrs.visit_tokens(f),
+        }
+    }
 }
 
 impl HasAttrs for Stmt {
@@ -657,6 +697,9 @@ impl HasAttrs for Stmt {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         self.kind.visit_attrs(f);
     }
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        self.kind.visit_tokens(f);
+    }
 }
 
 macro_rules! derive_has_attrs {
@@ -669,11 +712,36 @@ macro_rules! derive_has_attrs {
             fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
                 self.attrs.visit_attrs(f);
             }
+            fn visit_tokens(&mut self, _f: impl FnOnce(&mut PreexpTokenStream)) {}
+        }
+    )* }
+}
+
+macro_rules! derive_has_attrs_with_tokens {
+    ($($ty:path),*) => { $(
+        impl HasAttrs for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                &self.attrs
+            }
+
+            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+                self.attrs.visit_attrs(f);
+            }
+
+            fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+                if let Some(tokens) = self.tokens.as_mut() {
+                    f(tokens)
+                }
+            }
         }
     )* }
 }
 
 derive_has_attrs! {
-    Item, Expr, Local, ast::AssocItem, ast::ForeignItem, ast::StructField, ast::Arm,
+    ast::StructField, ast::Arm,
     ast::Field, ast::FieldPat, ast::Variant, ast::Param, GenericParam
 }
+
+derive_has_attrs_with_tokens! {
+    Expr, Item, Local, ast::AssocItem, ast::ForeignItem
+}
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 425ef83b57af5..eda6a133356fe 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -567,7 +567,7 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
 }
 
 pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
-    let Local { id, pat, ty, init, span, attrs } = local.deref_mut();
+    let Local { id, pat, ty, init, span, attrs, tokens: _ } = local.deref_mut();
     vis.visit_id(id);
     vis.visit_pat(pat);
     visit_opt(ty, |ty| vis.visit_ty(ty));
@@ -577,7 +577,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
 }
 
 pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
-    let Attribute { kind, id: _, style: _, span } = attr;
+    let Attribute { kind, id: _, style: _, span, tokens: _ } = attr;
     match kind {
         AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
             vis.visit_path(path);
@@ -1311,7 +1311,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
         StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
         StmtKind::Empty => smallvec![StmtKind::Empty],
         StmtKind::MacCall(mut mac) => {
-            let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut();
+            let MacCallStmt { mac: mac_, style: _, attrs, tokens: _ } = mac.deref_mut();
             vis.visit_mac(mac_);
             visit_thin_attrs(attrs, vis);
             smallvec![StmtKind::MacCall(mac)]
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index f201f0b5c6643..7db1331c62a23 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -403,6 +403,10 @@ impl Cursor {
     pub fn look_ahead(&self, n: usize) -> Option<TokenTree> {
         self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone())
     }
+
+    pub fn index(&self) -> usize {
+        self.index
+    }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
@@ -428,3 +432,85 @@ impl DelimSpan {
         self.open.with_hi(self.close.hi())
     }
 }
+
+#[derive(Clone, Debug, Default, Encodable, Decodable)]
+pub struct PreexpTokenStream(pub Lrc<Vec<(PreexpTokenTree, Spacing)>>);
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub enum PreexpTokenTree {
+    Token(Token),
+    Delimited(DelimSpan, DelimToken, PreexpTokenStream),
+    OuterAttributes(AttributesData),
+}
+
+impl PreexpTokenStream {
+    pub fn new(tokens: Vec<(PreexpTokenTree, Spacing)>) -> PreexpTokenStream {
+        PreexpTokenStream(Lrc::new(tokens))
+    }
+
+    pub fn replace_attributes(&mut self, f: impl FnOnce(&mut AttributesData)) {
+        if let &[(PreexpTokenTree::OuterAttributes(ref data), joint)] = &**self.0 {
+            let mut data = data.clone();
+            f(&mut data);
+            *self = PreexpTokenStream::new(vec![(PreexpTokenTree::OuterAttributes(data), joint)]);
+        } else {
+            panic!("Expected a single PreexpTokenTree::OuterAttributes, found {:?}", self);
+        }
+    }
+
+    pub fn to_tokenstream(self) -> TokenStream {
+        let trees: Vec<_> = self
+            .0
+            .iter()
+            .flat_map(|tree| match &tree.0 {
+                PreexpTokenTree::Token(inner) => {
+                    smallvec![(TokenTree::Token(inner.clone()), tree.1)].into_iter()
+                }
+                PreexpTokenTree::Delimited(span, delim, stream) => smallvec![(
+                    TokenTree::Delimited(*span, *delim, stream.clone().to_tokenstream()),
+                    tree.1,
+                )]
+                .into_iter(),
+                PreexpTokenTree::OuterAttributes(data) => {
+                    let flat: SmallVec<[_; 1]> = data
+                        .attrs
+                        .iter()
+                        .filter(|attr| attr.style == crate::AttrStyle::Outer)
+                        .flat_map(|attr| {
+                            attr.tokens.as_ref().expect("Missing tokens").0.iter().cloned()
+                        })
+                        .chain(data.tokens.clone().to_tokenstream().0.iter().cloned())
+                        .collect();
+                    flat.into_iter()
+                }
+            })
+            .collect();
+        TokenStream::new(trees)
+    }
+
+    pub fn from_tokenstream(stream: TokenStream) -> PreexpTokenStream {
+        let trees: Vec<_> = stream
+            .0
+            .iter()
+            .cloned()
+            .map(|tree| {
+                let new_tree = match tree.0 {
+                    TokenTree::Token(token) => PreexpTokenTree::Token(token),
+                    TokenTree::Delimited(sp, delim, inner) => PreexpTokenTree::Delimited(
+                        sp,
+                        delim,
+                        PreexpTokenStream::from_tokenstream(inner),
+                    ),
+                };
+                (new_tree, tree.1)
+            })
+            .collect();
+        PreexpTokenStream::new(trees)
+    }
+}
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub struct AttributesData {
+    pub attrs: Vec<crate::Attribute>,
+    pub tokens: PreexpTokenStream,
+}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 86fd87f6c42c5..0becd658f41ee 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -692,7 +692,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
         StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
         StmtKind::Empty => {}
         StmtKind::MacCall(ref mac) => {
-            let MacCallStmt { ref mac, style: _, ref attrs } = **mac;
+            let MacCallStmt { ref mac, style: _, ref attrs, tokens: _ } = **mac;
             visitor.visit_mac(mac);
             for attr in attrs.iter() {
                 visitor.visit_attribute(attr);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a28d022c66139..eda5bfb521926 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -972,7 +972,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
 
-        Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
+        Attribute { kind, id: attr.id, style: attr.style, span: attr.span, tokens: None }
     }
 
     fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index d84b3956475d8..a51c797331f4a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -132,6 +132,7 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
         id: ast::DUMMY_NODE_ID,
         span: sp,
         attrs: ast::AttrVec::new(),
+        tokens: None,
     });
     ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None }
 }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 926e3dbfc5266..73fb5528973e2 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -4,7 +4,7 @@ use crate::module::DirectoryOwnership;
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::ptr::P;
 use rustc_ast::token;
-use rustc_ast::tokenstream::{self, TokenStream};
+use rustc_ast::tokenstream::{self, PreexpTokenStream, TokenStream};
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, Attribute, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
@@ -81,6 +81,24 @@ impl HasAttrs for Annotatable {
             Annotatable::Variant(v) => v.visit_attrs(f),
         }
     }
+
+    fn visit_tokens(&mut self, f: impl FnOnce(&mut PreexpTokenStream)) {
+        match self {
+            Annotatable::Item(item) => item.visit_tokens(f),
+            Annotatable::TraitItem(trait_item) => trait_item.visit_tokens(f),
+            Annotatable::ImplItem(impl_item) => impl_item.visit_tokens(f),
+            Annotatable::ForeignItem(foreign_item) => foreign_item.visit_tokens(f),
+            Annotatable::Stmt(stmt) => stmt.visit_tokens(f),
+            Annotatable::Expr(expr) => expr.visit_tokens(f),
+            Annotatable::Arm(arm) => arm.visit_tokens(f),
+            Annotatable::Field(field) => field.visit_tokens(f),
+            Annotatable::FieldPat(fp) => fp.visit_tokens(f),
+            Annotatable::GenericParam(gp) => gp.visit_tokens(f),
+            Annotatable::Param(p) => p.visit_tokens(f),
+            Annotatable::StructField(sf) => sf.visit_tokens(f),
+            Annotatable::Variant(v) => v.visit_tokens(f),
+        }
+    }
 }
 
 impl Annotatable {
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index a5a7ee6c9a349..3181120912a3a 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -180,6 +180,7 @@ impl<'a> ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             span: sp,
             attrs: AttrVec::new(),
+            tokens: None,
         });
         ast::Stmt {
             id: ast::DUMMY_NODE_ID,
@@ -198,6 +199,7 @@ impl<'a> ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             span,
             attrs: AttrVec::new(),
+            tokens: None,
         });
         ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None }
     }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index dd087ab91509b..4f7f9d95b39d9 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -3,7 +3,11 @@
 use rustc_ast::attr::HasAttrs;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem};
+use rustc_ast::token::{self, DelimToken, Token};
+use rustc_ast::tokenstream::{
+    DelimSpan, PreexpTokenStream, PreexpTokenTree, Spacing, TokenStream, TokenTree,
+};
+use rustc_ast::{self as ast, Attribute, MetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -12,6 +16,7 @@ use rustc_feature::{Feature, Features, State as FeatureState};
 use rustc_feature::{
     ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
 };
+use rustc_parse::parser::attr::CfgAttrItem;
 use rustc_parse::{parse_in, validate_attr};
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
@@ -237,9 +242,20 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
     #the-cfg_attr-attribute>";
 
 impl<'a> StripUnconfigured<'a> {
-    pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
+    pub fn configure_with_tokens<T: HasAttrs>(
+        &mut self,
+        mut node: T,
+        config_tokens: bool,
+    ) -> Option<T> {
         self.process_cfg_attrs(&mut node);
-        self.in_cfg(node.attrs()).then_some(node)
+        self.in_cfg(node.attrs().iter()).then(|| {
+            node.visit_tokens(|tokens| *tokens = self.configure_tokens(tokens, config_tokens));
+            node
+        })
+    }
+
+    pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+        self.configure_with_tokens(node, true)
     }
 
     /// Parse and expand all `cfg_attr` attributes into a list of attributes
@@ -249,9 +265,41 @@ impl<'a> StripUnconfigured<'a> {
     /// attributes and is in the original source code. Gives compiler errors if
     /// the syntax of any `cfg_attr` is incorrect.
     pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
-        node.visit_attrs(|attrs| {
-            attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
-        });
+        node.visit_attrs(|attrs| attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)));
+    }
+
+    fn configure_tokens(
+        &mut self,
+        stream: &PreexpTokenStream,
+        config_tokens: bool,
+    ) -> PreexpTokenStream {
+        let trees: Vec<_> = stream
+            .0
+            .iter()
+            .flat_map(|tree| match tree.0.clone() {
+                PreexpTokenTree::OuterAttributes(mut data) => {
+                    data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
+
+                    if self.in_cfg(data.attrs.iter()) {
+                        if config_tokens {
+                            data.tokens = self.configure_tokens(&data.tokens, config_tokens);
+                        }
+                        Some((PreexpTokenTree::OuterAttributes(data), tree.1)).into_iter()
+                    } else {
+                        None.into_iter()
+                    }
+                }
+                PreexpTokenTree::Delimited(sp, delim, mut inner) => {
+                    if config_tokens {
+                        inner = self.configure_tokens(&inner, config_tokens);
+                    }
+                    Some((PreexpTokenTree::Delimited(sp, delim, inner), tree.1))
+                }
+                .into_iter(),
+                token_tree @ PreexpTokenTree::Token(_) => Some((token_tree, tree.1)).into_iter(),
+            })
+            .collect();
+        PreexpTokenStream::new(trees)
     }
 
     /// Parse and expand a single `cfg_attr` attribute into a list of attributes
@@ -288,14 +336,28 @@ impl<'a> StripUnconfigured<'a> {
         //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
         expanded_attrs
             .into_iter()
-            .flat_map(|(item, span)| {
-                let attr = attr::mk_attr_from_item(attr.style, item, span);
+            .flat_map(|item| {
+                let mut attr = attr::mk_attr_from_item(attr.style, item.item, item.span);
+                // Construct synthetic `#[]` tokens
+                let tokens = vec![
+                    (TokenTree::Token(Token::new(token::Pound, attr.span)), Spacing::Alone),
+                    (
+                        TokenTree::Delimited(
+                            DelimSpan::from_single(attr.span),
+                            DelimToken::Bracket,
+                            item.tokens,
+                        ),
+                        Spacing::Alone,
+                    ),
+                ];
+
+                attr.tokens = Some(TokenStream::new(tokens));
                 self.process_cfg_attr(attr)
             })
             .collect()
     }
 
-    fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
+    fn parse_cfg_attr(&mut self, attr: &Attribute) -> Option<(MetaItem, Vec<CfgAttrItem>)> {
         match attr.get_normal_item().args {
             ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
                 let msg = "wrong `cfg_attr` delimiters";
@@ -316,7 +378,7 @@ impl<'a> StripUnconfigured<'a> {
         None
     }
 
-    fn error_malformed_cfg_attr_missing(&self, span: Span) {
+    fn error_malformed_cfg_attr_missing(&mut self, span: Span) {
         self.sess
             .parse_sess
             .span_diagnostic
@@ -332,8 +394,8 @@ impl<'a> StripUnconfigured<'a> {
     }
 
     /// Determines if a node with the given attributes should be included in this configuration.
-    pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
-        attrs.iter().all(|attr| {
+    pub fn in_cfg<'b>(&self, mut attrs: impl Iterator<Item = &'b Attribute>) -> bool {
+        attrs.all(|attr| {
             if !is_cfg(self.sess, attr) {
                 return true;
             }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index e5cfb866938e5..0a64cc17ee083 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -494,6 +494,22 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     let mut item = self.fully_configure(item);
                     item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive)));
 
+                    if item.derive_allowed() {
+                        if let Annotatable::Item(item) = &mut item {
+                            let tokens = if let Some(tokens) = item.tokens.as_mut() {
+                                tokens
+                            } else {
+                                panic!("Missing tokens for {:?}", item);
+                            };
+                            tokens.replace_attributes(|data| {
+                                data.attrs.retain(|a| !a.has_name(sym::derive))
+                            });
+                        } else {
+                            panic!("Derive on non-item {:?}", item);
+                        }
+                    }
+                    tracing::debug!("item after: {:?}", item);
+
                     let mut derive_placeholders = Vec::with_capacity(derives.len());
                     invocations.reserve(derives.len());
                     for path in derives {
@@ -764,7 +780,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     if *mark_used {
                         self.cx.sess.mark_attr_used(&attr);
                     }
-                    item.visit_attrs(|attrs| attrs.push(attr));
+
+                    item.visit_attrs(|attrs| attrs.push(attr.clone()));
+
+                    item.visit_tokens(|tokens| {
+                        tokens.replace_attributes(|data| data.attrs.push(attr));
+                    });
+
                     fragment_kind.expect_from_annotatables(iter::once(item))
                 }
                 _ => unreachable!(),
@@ -1053,19 +1075,26 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
     fn find_attr_invoc(
         &self,
-        attrs: &mut Vec<ast::Attribute>,
+        attr_target: &mut (impl HasAttrs + std::fmt::Debug),
         after_derive: &mut bool,
     ) -> Option<ast::Attribute> {
-        let attr = attrs
-            .iter()
-            .position(|a| {
-                if a.has_name(sym::derive) {
-                    *after_derive = true;
-                }
-                !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a)
-            })
-            .map(|i| attrs.remove(i));
+        let mut attr = None;
+
+        attr_target.visit_attrs(|attrs| {
+            attrs
+                .iter()
+                .position(|a| {
+                    if a.has_name(sym::derive) {
+                        *after_derive = true;
+                    }
+                    !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a)
+                })
+                .map(|i| attr = Some(attrs.remove(i)));
+        });
+
+        let mut has_inner = false;
         if let Some(attr) = &attr {
+            has_inner = attr.style == ast::AttrStyle::Inner;
             if !self.cx.ecfg.custom_inner_attributes()
                 && attr.style == ast::AttrStyle::Inner
                 && !attr.has_name(sym::test)
@@ -1079,18 +1108,56 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 .emit();
             }
         }
+
+        let mut token_attr = None;
+        // Only attempt to replace tokens if we actually
+        // removed an attribute from the AST struct.
+        // We may end up collecting tokens for an AST struct
+        // even if `attrs_require_tokens` returns `false`, if
+        // a macro-rules matcher ends up forcing token collection.
+        // In this case, our collected tokens will not be a single
+        // `PreexpTokenStream::OuterAttributes`.
+        //
+        // To handle this case, we only attempt to modify the `TokenStream`
+        // if we actually need to do so - that is, if we need to remove
+        // a corresponding attribute that was removed from the parsed
+        // AST struct.
+        //
+        // FIXME: Support inner attributes.
+        // For now, we don't attempt to modify the TokenStream, which will
+        // cause us to use the pretty-print/retokenized stream later
+        // on due to the mismatch.
+        if attr.is_some() && !has_inner {
+            attr_target.visit_tokens(|tokens| {
+                tokens.replace_attributes(|data| {
+                    token_attr = data
+                        .attrs
+                        .iter()
+                        .position(|a| !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a))
+                        .map(|i| data.attrs.remove(i));
+
+                    if token_attr.is_some() != attr.is_some() {
+                        panic!(
+                            "Mismatched AST and tokens: ast={:?} token_attr={:?}\ndata={:?}",
+                            attr, token_attr, data
+                        );
+                    }
+                });
+            });
+        }
+
         attr
     }
 
     /// If `item` is an attr invocation, remove and return the macro attribute and derive traits.
     fn classify_item(
         &mut self,
-        item: &mut impl HasAttrs,
+        item: &mut (impl HasAttrs + std::fmt::Debug),
     ) -> (Option<ast::Attribute>, Vec<Path>, /* after_derive */ bool) {
-        let (mut attr, mut traits, mut after_derive) = (None, Vec::new(), false);
+        let (mut traits, mut after_derive) = (Vec::new(), false);
 
+        let attr = self.find_attr_invoc(item, &mut after_derive);
         item.visit_attrs(|mut attrs| {
-            attr = self.find_attr_invoc(&mut attrs, &mut after_derive);
             traits = collect_derives(&mut self.cx, &mut attrs);
         });
 
@@ -1102,19 +1169,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// is a breaking change)
     fn classify_nonitem(
         &mut self,
-        nonitem: &mut impl HasAttrs,
+        nonitem: &mut (impl HasAttrs + std::fmt::Debug),
     ) -> (Option<ast::Attribute>, /* after_derive */ bool) {
-        let (mut attr, mut after_derive) = (None, false);
+        let mut after_derive = false;
 
-        nonitem.visit_attrs(|mut attrs| {
-            attr = self.find_attr_invoc(&mut attrs, &mut after_derive);
-        });
+        let attr = self.find_attr_invoc(nonitem, &mut after_derive);
 
         (attr, after_derive)
     }
 
     fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
-        self.cfg.configure(node)
+        self.cfg.configure_with_tokens(node, false)
     }
 
     // Detect use of feature-gated or invalid attributes on macro invocations
@@ -1357,7 +1422,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         // we'll expand attributes on expressions separately
         if !stmt.is_expr() {
             let (attr, derives, after_derive) = if stmt.is_item() {
-                self.classify_item(&mut stmt)
+                (None, vec![], false)
+            //self.classify_item(&mut stmt)
             } else {
                 // ignore derives on non-item statements so it falls through
                 // to the unused-attributes lint
@@ -1379,7 +1445,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         }
 
         if let StmtKind::MacCall(mac) = stmt.kind {
-            let MacCallStmt { mac, style, attrs } = mac.into_inner();
+            let MacCallStmt { mac, style, attrs, tokens: _ } = mac.into_inner();
             self.check_attributes(&attrs);
             let mut placeholder =
                 self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts();
@@ -1785,6 +1851,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 span: at.span,
                 id: at.id,
                 style: at.style,
+                tokens: None,
             };
         } else {
             noop_visit_attribute(at, self)
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 47247294f5dc6..49a8fb3171d4a 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(proc_macro_internals)]
 #![feature(proc_macro_span)]
 #![feature(try_blocks)]
+#![recursion_limit = "256"]
 
 #[macro_use]
 extern crate rustc_macros;
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 4c9271a58df58..adc7f023b007e 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -104,6 +104,7 @@ pub fn placeholder(
                 mac: mac_placeholder(),
                 style: ast::MacStmtStyle::Braces,
                 attrs: ast::AttrVec::new(),
+                tokens: None,
             });
             ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None }
         }]),
diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs
index e3d4655831b32..5182dbaf88e35 100644
--- a/compiler/rustc_middle/src/ich/impls_syntax.rs
+++ b/compiler/rustc_middle/src/ich/impls_syntax.rs
@@ -40,7 +40,7 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {
         debug_assert!(!attr.ident().map_or(false, |ident| self.is_ignored_attr(ident.name)));
         debug_assert!(!attr.is_doc_comment());
 
-        let ast::Attribute { kind, id: _, style, span } = attr;
+        let ast::Attribute { kind, id: _, style, span, tokens: _ } = attr;
         if let ast::AttrKind::Normal(item) = kind {
             item.hash_stable(self, hasher);
             style.hash_stable(self, hasher);
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 21bbdc9ba8dce..928b3b21ec299 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -266,7 +266,8 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke
     // before we fall back to the stringification.
     let tokens = match *nt {
         Nonterminal::NtItem(ref item) => {
-            prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
+            debug!("converting item tokenstream: {:?}", item.tokens);
+            item.tokens.clone().map(|t| t.to_tokenstream())
         }
         Nonterminal::NtBlock(ref block) => block.tokens.clone(),
         Nonterminal::NtStmt(ref stmt) => {
@@ -291,8 +292,10 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke
         Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => {
             if expr.tokens.is_none() {
                 debug!("missing tokens for expr {:?}", expr);
+            } else {
+                debug!("converting expr tokenstream: {:?}", expr.tokens);
             }
-            prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span)
+            expr.tokens.clone().map(|t| t.to_tokenstream())
         }
     };
 
@@ -326,12 +329,16 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke
     // tokens such as extra braces and commas, don't happen.
     if let Some(tokens) = tokens {
         if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real, sess) {
+            debug!("matching tokens: {:?}", tokens);
+            debug!("reparsed tokens: {:?}", tokens_for_real);
             return tokens;
         }
         info!(
             "cached tokens found, but they're not \"probably equal\", \
                 going with stringified version"
         );
+        info!("cached stringified:   {:?}", pprust::tts_to_string(&tokens));
+        info!("reparsed stringified: {:?}", pprust::tts_to_string(&tokens_for_real));
         info!("cached tokens: {:?}", tokens);
         info!("reparsed tokens: {:?}", tokens_for_real);
     }
@@ -541,66 +548,3 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
         _ => panic!("forgot to add a token?"),
     }
 }
-
-fn prepend_attrs(
-    sess: &ParseSess,
-    attrs: &[ast::Attribute],
-    tokens: Option<&tokenstream::TokenStream>,
-    span: rustc_span::Span,
-) -> Option<tokenstream::TokenStream> {
-    let tokens = tokens?;
-    if attrs.is_empty() {
-        return Some(tokens.clone());
-    }
-    let mut builder = tokenstream::TokenStreamBuilder::new();
-    for attr in attrs {
-        assert_eq!(
-            attr.style,
-            ast::AttrStyle::Outer,
-            "inner attributes should prevent cached tokens from existing"
-        );
-
-        let source = pprust::attribute_to_string(attr);
-        let macro_filename = FileName::macro_expansion_source_code(&source);
-
-        let item = match attr.kind {
-            ast::AttrKind::Normal(ref item) => item,
-            ast::AttrKind::DocComment(..) => {
-                let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
-                builder.push(stream);
-                continue;
-            }
-        };
-
-        // synthesize # [ $path $tokens ] manually here
-        let mut brackets = tokenstream::TokenStreamBuilder::new();
-
-        // For simple paths, push the identifier directly
-        if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() {
-            let ident = item.path.segments[0].ident;
-            let token = token::Ident(ident.name, ident.as_str().starts_with("r#"));
-            brackets.push(tokenstream::TokenTree::token(token, ident.span));
-
-        // ... and for more complicated paths, fall back to a reparse hack that
-        // should eventually be removed.
-        } else {
-            let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
-            brackets.push(stream);
-        }
-
-        brackets.push(item.args.outer_tokens());
-
-        // The span we list here for `#` and for `[ ... ]` are both wrong in
-        // that it encompasses more than each token, but it hopefully is "good
-        // enough" for now at least.
-        builder.push(tokenstream::TokenTree::token(token::Pound, attr.span));
-        let delim_span = tokenstream::DelimSpan::from_single(attr.span);
-        builder.push(tokenstream::TokenTree::Delimited(
-            delim_span,
-            token::DelimToken::Bracket,
-            brackets.build(),
-        ));
-    }
-    builder.push(tokens.clone());
-    Some(builder.build())
-}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 98f94098bfc15..13ccb82354ca1 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,10 +1,15 @@
-use super::{Parser, PathStyle};
+use super::{FlatToken, Parser, PathStyle};
 use rustc_ast as ast;
-use rustc_ast::attr;
-use rustc_ast::token::{self, Nonterminal};
+use rustc_ast::attr::{self, HasAttrs};
+use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
+use rustc_ast::tokenstream::{
+    AttributesData, DelimSpan, PreexpTokenStream, PreexpTokenTree, Spacing, TokenStream,
+};
+use rustc_ast::{AttrVec, Attribute};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{error_code, PResult};
-use rustc_span::Span;
+use rustc_errors::{error_code, Handler, PResult};
+use rustc_span::symbol::sym;
+use rustc_span::{Span, DUMMY_SP};
 
 use tracing::debug;
 
@@ -17,6 +22,12 @@ pub(super) enum InnerAttrPolicy<'a> {
 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
                                                      permitted in this context";
 
+pub struct CfgAttrItem {
+    pub item: ast::AttrItem,
+    pub span: Span,
+    pub tokens: TokenStream,
+}
+
 pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
     reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
     saw_doc_comment: false,
@@ -24,47 +35,60 @@ pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPo
 };
 
 impl<'a> Parser<'a> {
-    /// Parses attributes that appear before an item.
-    pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
+    fn has_any_attributes(&mut self) -> bool {
+        self.check(&token::Pound) || matches!(self.token.kind, token::DocComment(..))
+    }
+
+    fn parse_outer_attributes_(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = Vec::new();
         let mut just_parsed_doc_comment = false;
+
         loop {
-            debug!("parse_outer_attributes: self.token={:?}", self.token);
-            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"
+            let (attr, tokens) = self.collect_tokens_keep_in_stream(false, |this| {
+                debug!("parse_outer_attributes: self.token={:?}", this.token);
+                if this.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 = this.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
+                    just_parsed_doc_comment = false;
+                    Ok((Some(attr), Vec::new())) // Attributes don't have their own attributes
+                } else if let token::DocComment(comment_kind, attr_style, data) = this.token.kind {
+                    let attr =
+                        attr::mk_doc_comment(comment_kind, attr_style, data, this.token.span);
+                    if attr.style != ast::AttrStyle::Outer {
+                        this.sess
+                            .span_diagnostic
+                            .struct_span_err_with_code(
+                                this.token.span,
+                                "expected outer doc comment",
+                                error_code!(E0753),
+                            )
+                            .note(
+                                "inner doc comments like this (starting with \
+                                 `//!` or `/*!`) can only appear before items",
+                            )
+                            .emit();
+                    }
+                    this.bump();
+                    just_parsed_doc_comment = true;
+                    Ok((Some(attr), Vec::new()))
                 } 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(comment_kind, attr_style, data) = self.token.kind {
-                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
-                if attr.style != ast::AttrStyle::Outer {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_err_with_code(
-                            self.token.span,
-                            "expected outer doc comment",
-                            error_code!(E0753),
-                        )
-                        .note(
-                            "inner doc comments like this (starting with \
-                             `//!` or `/*!`) can only appear before items",
-                        )
-                        .emit();
+                    Ok((None, Vec::new()))
                 }
+            })?;
+            if let Some(mut attr) = attr {
+                attr.tokens = Some(tokens.to_tokenstream());
                 attrs.push(attr);
-                self.bump();
-                just_parsed_doc_comment = true;
             } else {
                 break;
             }
@@ -72,6 +96,76 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
+    pub(super) fn parse_or_use_outer_attributes<
+        R: HasAttrs,
+        F: FnOnce(&mut Self, AttrVec) -> PResult<'a, R>,
+    >(
+        &mut self,
+        already_parsed_attrs: Option<AttrVec>,
+        f: F,
+    ) -> PResult<'a, (R, Option<PreexpTokenStream>)> {
+        let in_derive = self.in_derive;
+        let needs_tokens = |attrs: &[Attribute]| attrs_require_tokens(in_derive, attrs);
+
+        let make_capture_res = |this: &mut Self, f: F, attrs: AttrVec| {
+            let (res, tokens) = this.collect_tokens(|this| {
+                let mut new_attrs = attrs.clone().to_vec();
+
+                let old_in_derive = this.in_derive;
+                this.in_derive =
+                    old_in_derive || new_attrs.iter().any(|attr| attr.has_name(sym::derive));
+                let res = f(this, attrs);
+                this.in_derive = old_in_derive;
+
+                let mut res = res?;
+
+                res.visit_attrs(|attrs| {
+                    new_attrs = attrs.clone();
+                });
+                Ok((res, new_attrs))
+            })?;
+            Ok((res, Some(tokens)))
+        };
+
+        if let Some(attrs) = already_parsed_attrs {
+            if needs_tokens(&attrs) {
+                return make_capture_res(self, f, attrs);
+            } else {
+                return f(self, attrs).map(|res| (res, None));
+            }
+        } else {
+            // If we are already collecting tokens, we need to
+            // perform token collection here even if we have no
+            // outer attributes, since there may be inner attributes
+            // parsed by 'f'.
+            if !self.has_any_attributes() && !self.in_derive {
+                return Ok((f(self, AttrVec::new())?, None));
+            }
+
+            let attrs = self.parse_outer_attributes_()?;
+            if !needs_tokens(&attrs) {
+                return Ok((f(self, attrs.into())?, None));
+            }
+
+            return make_capture_res(self, f, attrs.into());
+        }
+    }
+
+    pub(super) fn parse_outer_attributes<R: HasAttrs>(
+        &mut self,
+        f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, R>,
+    ) -> PResult<'a, R> {
+        self.parse_outer_attributes_with_tokens(f).map(|(res, _tokens)| res)
+    }
+
+    /// Parses attributes that appear before an item.
+    pub(super) fn parse_outer_attributes_with_tokens<R: HasAttrs>(
+        &mut self,
+        f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, R>,
+    ) -> PResult<'a, (R, Option<PreexpTokenStream>)> {
+        self.parse_or_use_outer_attributes(None, |this, attrs| f(this, attrs.into()))
+    }
+
     /// Matches `attribute = # ! [ meta_item ]`.
     ///
     /// If `permit_inner` is `true`, then a leading `!` indicates an inner
@@ -174,20 +268,29 @@ impl<'a> Parser<'a> {
     crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
-            // 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(comment_kind, attr_style, data) = self.token.kind {
-                // We need to get the position of this token before we bump.
-                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
-                if attr.style == ast::AttrStyle::Inner {
-                    attrs.push(attr);
-                    self.bump();
+            let (attr, tokens) = self.collect_tokens_no_attrs(|this| {
+                // Only try to parse if it is an inner attribute (has `!`).
+                if this.check(&token::Pound) && this.look_ahead(1, |t| t == &token::Not) {
+                    let attr = this.parse_attribute(true)?;
+                    assert_eq!(attr.style, ast::AttrStyle::Inner);
+                    Ok(Some(attr))
+                } else if let token::DocComment(comment_kind, attr_style, data) = this.token.kind {
+                    // We need to get the position of this token before we bump.
+                    let attr =
+                        attr::mk_doc_comment(comment_kind, attr_style, data, this.token.span);
+                    if attr.style == ast::AttrStyle::Inner {
+                        this.bump();
+                        Ok(Some(attr))
+                    } else {
+                        Ok(None)
+                    }
                 } else {
-                    break;
+                    Ok(None)
                 }
+            })?;
+            if let Some(mut attr) = attr {
+                attr.tokens = Some(tokens.to_tokenstream());
+                attrs.push(attr)
             } else {
                 break;
             }
@@ -212,7 +315,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
-    pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
+    pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<CfgAttrItem>)> {
         let cfg_predicate = self.parse_meta_item()?;
         self.expect(&token::Comma)?;
 
@@ -220,8 +323,14 @@ impl<'a> Parser<'a> {
         let mut expanded_attrs = Vec::with_capacity(1);
         while self.token.kind != token::Eof {
             let lo = self.token.span;
-            let item = self.parse_attr_item()?;
-            expanded_attrs.push((item, lo.to(self.prev_token.span)));
+            let (item, tokens) =
+                self.collect_tokens(|this| this.parse_attr_item().map(|item| (item, Vec::new())))?;
+            expanded_attrs.push(CfgAttrItem {
+                item,
+                span: lo.to(self.prev_token.span),
+                tokens: tokens.to_tokenstream(),
+            });
+
             if !self.eat(&token::Comma) {
                 break;
             }
@@ -301,4 +410,146 @@ impl<'a> Parser<'a> {
         let msg = format!("expected unsuffixed literal or identifier, found `{}`", found);
         Err(self.struct_span_err(self.token.span, &msg))
     }
+
+    pub(super) fn collect_tokens_keep_in_stream<R>(
+        &mut self,
+        keep_in_stream: bool,
+        f: impl FnOnce(&mut Self) -> PResult<'a, (R, Vec<ast::Attribute>)>,
+    ) -> PResult<'a, (R, PreexpTokenStream)> {
+        let start_pos = self.token_cursor.collecting_buf.len() - 1;
+        let prev_collecting = std::mem::replace(&mut self.token_cursor.is_collecting, true);
+
+        let ret = f(self);
+
+        let err_stream = if ret.is_err() {
+            // Rustdoc tries to parse an item, and then cancels the error
+            // if it fails.
+            // FIXME: Come up with a better way of doing this
+            if !self.is_rustdoc {
+                self.sess
+                    .span_diagnostic
+                    .delay_span_bug(self.token.span, "Parse error during token collection");
+            }
+            Some(PreexpTokenStream::new(vec![]))
+        } else {
+            None
+        };
+
+        fn make_stream(
+            handler: &Handler,
+            iter: impl Iterator<Item = (FlatToken, Spacing)>,
+            err_stream: Option<PreexpTokenStream>,
+        ) -> PreexpTokenStream {
+            err_stream.unwrap_or_else(|| make_preexp_stream(handler, iter))
+        }
+
+        let last_token = self.token_cursor.collecting_buf.pop().unwrap();
+        let mut stream = if prev_collecting {
+            if keep_in_stream {
+                make_stream(
+                    &self.sess.span_diagnostic,
+                    self.token_cursor.collecting_buf[start_pos..].iter().cloned(),
+                    err_stream,
+                )
+            } else {
+                make_stream(
+                    &self.sess.span_diagnostic,
+                    self.token_cursor.collecting_buf.drain(start_pos..),
+                    err_stream,
+                )
+            }
+        } else {
+            debug_assert_eq!(start_pos, 0);
+            make_stream(
+                &self.sess.span_diagnostic,
+                std::mem::take(&mut self.token_cursor.collecting_buf).into_iter(),
+                err_stream,
+            )
+        };
+
+        if let Ok((_, attrs)) = ret.as_ref() {
+            if !attrs.is_empty() {
+                let data = AttributesData { attrs: attrs.clone(), tokens: stream };
+                let tree = (PreexpTokenTree::OuterAttributes(data.clone()), Spacing::Alone);
+                stream = PreexpTokenStream::new(vec![tree]);
+
+                if prev_collecting {
+                    assert!(keep_in_stream);
+                    self.token_cursor.collecting_buf.splice(
+                        start_pos..,
+                        std::iter::once((FlatToken::OuterAttributes(data), Spacing::Alone)),
+                    );
+                }
+            }
+        }
+
+        self.token_cursor.collecting_buf.push(last_token);
+        self.token_cursor.is_collecting = prev_collecting;
+
+        Ok((ret?.0, stream))
+    }
+}
+
+fn make_preexp_stream(
+    handler: &Handler,
+    tokens: impl Iterator<Item = (FlatToken, Spacing)>,
+) -> PreexpTokenStream {
+    #[derive(Debug)]
+    struct FrameData {
+        open: Span,
+        inner: Vec<(PreexpTokenTree, Spacing)>,
+    }
+    let mut stack = vec![FrameData { open: DUMMY_SP, inner: vec![] }];
+    for tree in tokens {
+        match tree.0 {
+            FlatToken::Token(Token { kind: TokenKind::OpenDelim(_), span }) => {
+                stack.push(FrameData { open: span, inner: vec![] });
+            }
+            FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => {
+                let frame_data = stack.pop().expect("Token stack was empty!");
+                let dspan = DelimSpan::from_pair(frame_data.open, span);
+                let stream = PreexpTokenStream::new(frame_data.inner);
+                let delimited = PreexpTokenTree::Delimited(dspan, delim, stream);
+                stack
+                    .last_mut()
+                    .expect("Bottom token frame is missing!")
+                    .inner
+                    .push((delimited, Spacing::Alone));
+            }
+            FlatToken::Token(token) => stack
+                .last_mut()
+                .expect("Bottom token frame is missing!")
+                .inner
+                .push((PreexpTokenTree::Token(token), tree.1)),
+            FlatToken::OuterAttributes(data) => stack
+                .last_mut()
+                .expect("Bottom token frame is missing!")
+                .inner
+                .push((PreexpTokenTree::OuterAttributes(data), Spacing::Alone)),
+        }
+    }
+    let final_buf = stack.pop().expect("Missing final buf!");
+    if !stack.is_empty() {
+        handler.delay_span_bug(
+            stack[0].open,
+            &format!("Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack),
+        );
+    }
+    PreexpTokenStream::new(final_buf.inner)
+}
+
+pub fn attrs_require_tokens(in_derive: bool, attrs: &[Attribute]) -> bool {
+    attrs.iter().any(|attr| {
+        if let Some(ident) = attr.ident() {
+            ident.name == sym::derive
+                // We only need tokens for 'cfgs' inside a derive,
+                // since cfg-stripping occurs before derive expansion
+                || (ident.name == sym::cfg && in_derive)
+                // This might apply a custom attribute/derive
+                || ident.name == sym::cfg_attr
+                || !rustc_feature::is_builtin_attr_name(ident.name)
+        } else {
+            true
+        }
+    })
 }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 69d13b5cf53a2..762d1c1c82b4a 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -17,7 +17,6 @@ use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
 use rustc_span::source_map::{self, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use std::mem;
-use tracing::debug;
 
 /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
 /// dropped into the token stream, which happens while parsing the result of
@@ -446,23 +445,25 @@ impl<'a> Parser<'a> {
             _ => RangeLimits::Closed,
         };
         let op = AssocOp::from_token(&self.token);
-        let attrs = self.parse_or_use_outer_attributes(attrs)?;
-        let lo = self.token.span;
-        self.bump();
-        let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() {
-            // RHS must be parsed with more associativity than the dots.
-            self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed)
-                .map(|x| (lo.to(x.span), Some(x)))?
-        } else {
-            (lo, None)
-        };
-        Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs))
+        let (mut expr, tokens) = self.parse_or_use_outer_attributes(attrs, |this, attrs| {
+            let lo = this.token.span;
+            this.bump();
+            let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
+                // RHS must be parsed with more associativity than the dots.
+                this.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed)
+                    .map(|x| (lo.to(x.span), Some(x)))?
+            } else {
+                (lo, None)
+            };
+            Ok(this.mk_expr(span, this.mk_range(None, opt_end, limits)?, attrs))
+        })?;
+        expr.tokens = tokens;
+        Ok(expr)
     }
 
     /// Parses a prefix-unary-operator expr.
     fn parse_prefix_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
-        let attrs = self.parse_or_use_outer_attributes(attrs)?;
-        self.maybe_collect_tokens(!attrs.is_empty(), |this| {
+        let (mut expr, tokens) = self.parse_or_use_outer_attributes(attrs, |this, attrs| {
             let lo = this.token.span;
             // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()
             let (hi, ex) = match this.token.uninterpolate().kind {
@@ -475,10 +476,12 @@ impl<'a> Parser<'a> {
                 token::Ident(..) if this.is_mistaken_not_ident_negation() => {
                     this.recover_not_expr(lo)
                 }
-                _ => return this.parse_dot_or_call_expr(Some(attrs)),
+                _ => return this.parse_dot_or_call_expr(attrs),
             }?;
             Ok(this.mk_expr(lo.to(hi), ex, attrs))
-        })
+        })?;
+        expr.tokens = tokens;
+        Ok(expr)
     }
 
     fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P<Expr>)> {
@@ -748,8 +751,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses `a.b` or `a(13)` or `a[4]` or just `a`.
-    fn parse_dot_or_call_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
-        let attrs = self.parse_or_use_outer_attributes(attrs)?;
+    fn parse_dot_or_call_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let base = self.parse_bottom_expr();
         let (span, base) = self.interpolated_or_expr_span(base)?;
         self.parse_dot_or_call_expr_with(base, span, attrs)
@@ -1079,21 +1081,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn maybe_collect_tokens(
-        &mut self,
-        has_outer_attrs: bool,
-        f: impl FnOnce(&mut Self) -> PResult<'a, P<Expr>>,
-    ) -> PResult<'a, P<Expr>> {
-        if has_outer_attrs {
-            let (mut expr, tokens) = self.collect_tokens(f)?;
-            debug!("maybe_collect_tokens: Collected tokens for {:?} (tokens {:?}", expr, tokens);
-            expr.tokens = Some(tokens);
-            Ok(expr)
-        } else {
-            f(self)
-        }
-    }
-
     fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
         match self.parse_opt_lit() {
@@ -1611,20 +1598,21 @@ impl<'a> Parser<'a> {
     /// Parses a parameter in a closure header (e.g., `|arg, arg|`).
     fn parse_fn_block_param(&mut self) -> PResult<'a, Param> {
         let lo = self.token.span;
-        let attrs = self.parse_outer_attributes()?;
-        let pat = self.parse_pat(PARAM_EXPECTED)?;
-        let ty = if self.eat(&token::Colon) {
-            self.parse_ty()?
-        } else {
-            self.mk_ty(self.prev_token.span, TyKind::Infer)
-        };
-        Ok(Param {
-            attrs: attrs.into(),
-            ty,
-            pat,
-            span: lo.to(self.token.span),
-            id: DUMMY_NODE_ID,
-            is_placeholder: false,
+        self.parse_outer_attributes(|this, attrs| {
+            let pat = this.parse_pat(PARAM_EXPECTED)?;
+            let ty = if this.eat(&token::Colon) {
+                this.parse_ty()?
+            } else {
+                this.mk_ty(this.prev_token.span, TyKind::Infer)
+            };
+            Ok(Param {
+                attrs: attrs.into(),
+                ty,
+                pat,
+                span: lo.to(this.token.span),
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+            })
         })
     }
 
@@ -1640,7 +1628,8 @@ 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.
+            // For recovery.
+            let attrs = self.parse_outer_attributes(|_this, attrs| Ok(attrs))?;
             let not_block = self.token != token::OpenDelim(token::Brace);
             let block = self.parse_block().map_err(|mut err| {
                 if not_block {
@@ -1697,15 +1686,16 @@ impl<'a> Parser<'a> {
     /// Parses an `else { ... }` expression (`else` token already eaten).
     fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
         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()?;
-            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)
+        self.parse_outer_attributes(|this, attrs| {
+            let expr = if this.eat_keyword(kw::If) {
+                this.parse_if_expr(AttrVec::new())?
+            } else {
+                let blk = this.parse_block()?;
+                this.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())
+            };
+            this.error_on_if_block_attrs(ctx_span, true, expr.span, &attrs);
+            Ok(expr)
+        })
     }
 
     fn error_on_if_block_attrs(
@@ -1855,86 +1845,88 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
-        let attrs = self.parse_outer_attributes()?;
-        let lo = self.token.span;
-        let pat = self.parse_top_pat(GateOr::No)?;
-        let guard = if self.eat_keyword(kw::If) {
-            let if_span = self.prev_token.span;
-            let cond = self.parse_expr()?;
-            if let ExprKind::Let(..) = cond.kind {
-                // Remove the last feature gating of a `let` expression since it's stable.
-                self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
-                let span = if_span.to(cond.span);
-                self.sess.gated_spans.gate(sym::if_let_guard, span);
-            }
-            Some(cond)
-        } else {
-            None
-        };
-        let arrow_span = self.token.span;
-        self.expect(&token::FatArrow)?;
-        let arm_start_span = self.token.span;
+        self.parse_outer_attributes(|this, attrs| {
+            let lo = this.token.span;
+            let pat = this.parse_top_pat(GateOr::No)?;
+            let guard = if this.eat_keyword(kw::If) {
+                let if_span = this.prev_token.span;
+                let cond = this.parse_expr()?;
+                if let ExprKind::Let(..) = cond.kind {
+                    // Remove the last feature gating of a `let` expression since it's stable.
+                    this.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+                    let span = if_span.to(cond.span);
+                    this.sess.gated_spans.gate(sym::if_let_guard, span);
+                }
+                Some(cond)
+            } else {
+                None
+            };
+            let arrow_span = this.token.span;
+            this.expect(&token::FatArrow)?;
+            let arm_start_span = this.token.span;
 
-        let expr = self.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| {
-            err.span_label(arrow_span, "while parsing the `match` arm starting here");
-            err
-        })?;
+            let expr = this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| {
+                err.span_label(arrow_span, "while parsing the `match` arm starting here");
+                err
+            })?;
 
-        let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
-            && self.token != token::CloseDelim(token::Brace);
-
-        let hi = self.prev_token.span;
-
-        if require_comma {
-            let sm = self.sess.source_map();
-            self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err(
-                |mut err| {
-                    match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) {
-                        (Ok(ref expr_lines), Ok(ref arm_start_lines))
-                            if arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col
-                                && expr_lines.lines.len() == 2
-                                && self.token == token::FatArrow =>
-                        {
-                            // We check whether there's any trailing code in the parse span,
-                            // if there isn't, we very likely have the following:
-                            //
-                            // X |     &Y => "y"
-                            //   |        --    - missing comma
-                            //   |        |
-                            //   |        arrow_span
-                            // X |     &X => "x"
-                            //   |      - ^^ self.token.span
-                            //   |      |
-                            //   |      parsed until here as `"y" & X`
-                            err.span_suggestion_short(
-                                arm_start_span.shrink_to_hi(),
-                                "missing a comma here to end this `match` arm",
-                                ",".to_owned(),
-                                Applicability::MachineApplicable,
-                            );
-                        }
-                        _ => {
-                            err.span_label(
-                                arrow_span,
-                                "while parsing the `match` arm starting here",
-                            );
+            let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
+                && this.token != token::CloseDelim(token::Brace);
+
+            let hi = this.prev_token.span;
+
+            if require_comma {
+                let sm = this.sess.source_map();
+                this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err(
+                    |mut err| {
+                        match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) {
+                            (Ok(ref expr_lines), Ok(ref arm_start_lines))
+                                if arm_start_lines.lines[0].end_col
+                                    == expr_lines.lines[0].end_col
+                                    && expr_lines.lines.len() == 2
+                                    && this.token == token::FatArrow =>
+                            {
+                                // We check whether there's any trailing code in the parse span,
+                                // if there isn't, we very likely have the following:
+                                //
+                                // X |     &Y => "y"
+                                //   |        --    - missing comma
+                                //   |        |
+                                //   |        arrow_span
+                                // X |     &X => "x"
+                                //   |      - ^^ self.token.span
+                                //   |      |
+                                //   |      parsed until here as `"y" & X`
+                                err.span_suggestion_short(
+                                    arm_start_span.shrink_to_hi(),
+                                    "missing a comma here to end this `match` arm",
+                                    ",".to_owned(),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                            _ => {
+                                err.span_label(
+                                    arrow_span,
+                                    "while parsing the `match` arm starting here",
+                                );
+                            }
                         }
-                    }
-                    err
-                },
-            )?;
-        } else {
-            self.eat(&token::Comma);
-        }
+                        err
+                    },
+                )?;
+            } else {
+                this.eat(&token::Comma);
+            }
 
-        Ok(ast::Arm {
-            attrs,
-            pat,
-            guard,
-            body: expr,
-            span: lo.to(hi),
-            id: DUMMY_NODE_ID,
-            is_placeholder: false,
+            Ok(ast::Arm {
+                attrs,
+                pat,
+                guard,
+                body: expr,
+                span: lo.to(hi),
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+            })
         })
     }
 
@@ -2169,30 +2161,32 @@ impl<'a> Parser<'a> {
 
     /// Parses `ident (COLON expr)?`.
     fn parse_field(&mut self) -> PResult<'a, Field> {
-        let attrs = self.parse_outer_attributes()?.into();
-        let lo = self.token.span;
+        self.parse_outer_attributes(|this, attrs| {
+            let attrs = attrs.into();
+            let lo = this.token.span;
 
-        // Check if a colon exists one ahead. This means we're parsing a fieldname.
-        let is_shorthand = !self.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
-        let (ident, expr) = if is_shorthand {
-            // Mimic `x: x` for the `x` field shorthand.
-            let ident = self.parse_ident_common(false)?;
-            let path = ast::Path::from_ident(ident);
-            (ident, self.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new()))
-        } else {
-            let ident = self.parse_field_name()?;
-            self.error_on_eq_field_init(ident);
-            self.bump(); // `:`
-            (ident, self.parse_expr()?)
-        };
-        Ok(ast::Field {
-            ident,
-            span: lo.to(expr.span),
-            expr,
-            is_shorthand,
-            attrs,
-            id: DUMMY_NODE_ID,
-            is_placeholder: false,
+            // Check if a colon exists one ahead. This means we're parsing a fieldname.
+            let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
+            let (ident, expr) = if is_shorthand {
+                // Mimic `x: x` for the `x` field shorthand.
+                let ident = this.parse_ident_common(false)?;
+                let path = ast::Path::from_ident(ident);
+                (ident, this.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new()))
+            } else {
+                let ident = this.parse_field_name()?;
+                this.error_on_eq_field_init(ident);
+                this.bump(); // `:`
+                (ident, this.parse_expr()?)
+            };
+            Ok(ast::Field {
+                ident,
+                span: lo.to(expr.span),
+                expr,
+                is_shorthand,
+                attrs,
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+            })
         })
     }
 
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index dd99a7587dd54..b1421a83e7ee7 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -73,67 +73,88 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
         let mut params = Vec::new();
         loop {
-            let attrs = self.parse_outer_attributes()?;
-            if self.check_lifetime() {
-                let lifetime = self.expect_lifetime();
-                // Parse lifetime parameter.
-                let bounds =
-                    if self.eat(&token::Colon) { self.parse_lt_param_bounds() } else { Vec::new() };
-                params.push(ast::GenericParam {
-                    ident: lifetime.ident,
-                    id: lifetime.id,
-                    attrs: attrs.into(),
-                    bounds,
-                    kind: ast::GenericParamKind::Lifetime,
-                    is_placeholder: false,
-                });
-            } else if self.check_keyword(kw::Const) {
-                // Parse const parameter.
-                params.push(self.parse_const_param(attrs)?);
-            } else if self.check_ident() {
-                // Parse type parameter.
-                params.push(self.parse_ty_param(attrs)?);
-            } else if self.token.can_begin_type() {
-                // Trying to write an associated type bound? (#26271)
-                let snapshot = self.clone();
-                match self.parse_ty_where_predicate() {
-                    Ok(where_predicate) => {
-                        self.struct_span_err(
-                            where_predicate.span(),
-                            "bounds on associated types do not belong here",
-                        )
-                        .span_label(where_predicate.span(), "belongs in `where` clause")
-                        .emit();
-                    }
-                    Err(mut err) => {
-                        err.cancel();
-                        *self = snapshot;
-                        break;
-                    }
-                }
-            } else {
-                // Check for trailing attributes and stop parsing.
-                if !attrs.is_empty() {
-                    if !params.is_empty() {
-                        self.struct_span_err(
-                            attrs[0].span,
-                            "trailing attribute after generic parameter",
-                        )
-                        .span_label(attrs[0].span, "attributes must go before parameters")
-                        .emit();
+            let mut should_break = false;
+            let param = self.parse_outer_attributes(|this, attrs| {
+                let param = if this.check_lifetime() {
+                    let lifetime = this.expect_lifetime();
+                    // Parse lifetime parameter.
+                    let bounds = if this.eat(&token::Colon) {
+                        this.parse_lt_param_bounds()
                     } else {
-                        self.struct_span_err(attrs[0].span, "attribute without generic parameters")
+                        Vec::new()
+                    };
+                    Some(ast::GenericParam {
+                        ident: lifetime.ident,
+                        id: lifetime.id,
+                        attrs: attrs.into(),
+                        bounds,
+                        kind: ast::GenericParamKind::Lifetime,
+                        is_placeholder: false,
+                    })
+                } else if this.check_keyword(kw::Const) {
+                    // Parse const parameter.
+                    Some(this.parse_const_param(attrs)?)
+                } else if this.check_ident() {
+                    // Parse type parameter.
+                    Some(this.parse_ty_param(attrs)?)
+                } else if this.token.can_begin_type() {
+                    // Trying to write an associated type bound? (#26271)
+                    let snapshot = this.clone();
+                    match this.parse_ty_where_predicate() {
+                        Ok(where_predicate) => {
+                            this.struct_span_err(
+                                where_predicate.span(),
+                                "bounds on associated types do not belong here",
+                            )
+                            .span_label(where_predicate.span(), "belongs in `where` clause")
+                            .emit();
+
+                            None
+                        }
+                        Err(mut err) => {
+                            err.cancel();
+                            *this = snapshot;
+                            None
+                        }
+                    }
+                } else {
+                    // Check for trailing attributes and stop parsing.
+                    if !attrs.is_empty() {
+                        if !params.is_empty() {
+                            this.struct_span_err(
+                                attrs[0].span,
+                                "trailing attribute after generic parameter",
+                            )
+                            .span_label(attrs[0].span, "attributes must go before parameters")
+                            .emit();
+                        } else {
+                            this.struct_span_err(
+                                attrs[0].span,
+                                "attribute without generic parameters",
+                            )
                             .span_label(
                                 attrs[0].span,
                                 "attributes are only permitted when preceding parameters",
                             )
                             .emit();
+                        }
+                    }
+                    None
+                };
+                if param.is_some() {
+                    if !this.eat(&token::Comma) {
+                        should_break = true;
                     }
                 }
-                break;
-            }
+                Ok(param)
+            })?;
 
-            if !self.eat(&token::Comma) {
+            if let Some(param) = param {
+                params.push(param);
+                if should_break {
+                    break;
+                }
+            } else {
                 break;
             }
         }
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 26ca99801277b..b38dc4c5a3cf5 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -3,11 +3,14 @@ use super::ty::{AllowPlus, RecoverQPath};
 use super::{FollowedByType, Parser, PathStyle};
 
 use crate::maybe_whole;
+use crate::parser::attr::attrs_require_tokens;
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, TokenKind};
-use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
-use rustc_ast::{self as ast, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID};
+use rustc_ast::token::{self, DelimToken, TokenKind};
+use rustc_ast::tokenstream::{
+    AttributesData, DelimSpan, PreexpTokenStream, PreexpTokenTree, Spacing, TokenStream, TokenTree,
+};
+use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
 use rustc_ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, Mod};
 use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
 use rustc_ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind};
@@ -98,8 +101,17 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_item_(&mut self, req_name: ReqName) -> PResult<'a, Option<Item>> {
-        let attrs = self.parse_outer_attributes()?;
-        self.parse_item_common(attrs, true, false, req_name)
+        let res = self.parse_outer_attributes_with_tokens(|this, attrs| {
+            this.parse_item_common(attrs, true, false, req_name)
+        });
+        res.map(|(mut item, tokens)| {
+            if let Some(item) = item.as_mut() {
+                if item.tokens.is_none() {
+                    item.tokens = tokens;
+                }
+            }
+            item
+        })
     }
 
     pub(super) fn parse_item_common(
@@ -110,51 +122,44 @@ impl<'a> Parser<'a> {
         req_name: ReqName,
     ) -> PResult<'a, Option<Item>> {
         maybe_whole!(self, NtItem, |item| {
-            let mut item = item;
+            let mut item = item.into_inner();
+
+            if !attrs.is_empty() {
+                if let Some(tokens) = item.tokens.as_mut() {
+                    if let &[(PreexpTokenTree::OuterAttributes(ref data), joint)] = &**tokens.0 {
+                        let mut data = data.clone();
+                        let mut attrs = attrs.clone();
+                        mem::swap(&mut data.attrs, &mut attrs);
+                        data.attrs.extend(attrs);
+                        debug!("new data: {:?}", data);
+                        *tokens = PreexpTokenStream::new(vec![(
+                            PreexpTokenTree::OuterAttributes(data),
+                            joint,
+                        )]);
+                    } else {
+                        assert!(
+                            !attrs_require_tokens(self.in_derive, &item.attrs),
+                            "Attributes needed tokens: {:?}",
+                            item.attrs
+                        );
+                        let data = AttributesData { attrs: attrs.clone(), tokens: tokens.clone() };
+                        *tokens = PreexpTokenStream::new(vec![(
+                            PreexpTokenTree::OuterAttributes(data),
+                            Spacing::Alone,
+                        )])
+                    }
+                } else {
+                    panic!("Missing tokens for {:?}", item);
+                }
+            }
+
             mem::swap(&mut item.attrs, &mut attrs);
             item.attrs.extend(attrs);
-            Some(item.into_inner())
+            Some(item)
         });
 
-        let mut unclosed_delims = vec![];
-        let has_attrs = !attrs.is_empty();
-        let parse_item = |this: &mut Self| {
-            let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
-            unclosed_delims.append(&mut this.unclosed_delims);
-            item
-        };
-
-        let (mut item, tokens) = if has_attrs {
-            let (item, tokens) = self.collect_tokens(parse_item)?;
-            (item, Some(tokens))
-        } else {
-            (parse_item(self)?, None)
-        };
-
-        self.unclosed_delims.append(&mut unclosed_delims);
-
-        // Once we've parsed an item and recorded the tokens we got while
-        // parsing we may want to store `tokens` into the item we're about to
-        // return. Note, though, that we specifically didn't capture tokens
-        // related to outer attributes. The `tokens` field here may later be
-        // used with procedural macros to convert this item back into a token
-        // stream, but during expansion we may be removing attributes as we go
-        // along.
-        //
-        // If we've got inner attributes then the `tokens` we've got above holds
-        // these inner attributes. If an inner attribute is expanded we won't
-        // actually remove it from the token stream, so we'll just keep yielding
-        // it (bad!). To work around this case for now we just avoid recording
-        // `tokens` if we detect any inner attributes. This should help keep
-        // expansion correct, but we should fix this bug one day!
-        if let Some(tokens) = tokens {
-            if let Some(item) = &mut item {
-                if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) {
-                    item.tokens = Some(tokens);
-                }
-            }
-        }
-        Ok(item)
+        let item = self.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
+        item
     }
 
     fn parse_item_common_(
@@ -1060,11 +1065,7 @@ impl<'a> Parser<'a> {
         let mut generics = self.parse_generics()?;
         generics.where_clause = self.parse_where_clause()?;
 
-        let (variants, _) =
-            self.parse_delim_comma_seq(token::Brace, |p| p.parse_enum_variant()).map_err(|e| {
-                self.recover_stmt();
-                e
-            })?;
+        let (variants, _) = self.parse_enum_body()?;
 
         let enum_definition =
             EnumDef { variants: variants.into_iter().filter_map(|v| v).collect() };
@@ -1072,40 +1073,57 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
-        let variant_attrs = self.parse_outer_attributes()?;
-        let vlo = self.token.span;
+        self.parse_outer_attributes(|this, variant_attrs| {
+            let vlo = this.token.span;
 
-        let vis = self.parse_visibility(FollowedByType::No)?;
-        if !self.recover_nested_adt_item(kw::Enum)? {
-            return Ok(None);
-        }
-        let ident = self.parse_ident()?;
+            let vis = this.parse_visibility(FollowedByType::No)?;
+            if !this.recover_nested_adt_item(kw::Enum)? {
+                this.eat(&token::Comma);
+                return Ok(None);
+            }
+            let ident = this.parse_ident()?;
 
-        let struct_def = if self.check(&token::OpenDelim(token::Brace)) {
-            // Parse a struct variant.
-            let (fields, recovered) = self.parse_record_struct_body()?;
-            VariantData::Struct(fields, recovered)
-        } else if self.check(&token::OpenDelim(token::Paren)) {
-            VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID)
-        } else {
-            VariantData::Unit(DUMMY_NODE_ID)
-        };
+            let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
+                // Parse a struct variant.
+                let (fields, recovered) = this.parse_record_struct_body()?;
+                VariantData::Struct(fields, recovered)
+            } else if this.check(&token::OpenDelim(token::Paren)) {
+                VariantData::Tuple(this.parse_tuple_struct_body("enum")?, DUMMY_NODE_ID)
+            } else {
+                VariantData::Unit(DUMMY_NODE_ID)
+            };
 
-        let disr_expr =
-            if self.eat(&token::Eq) { Some(self.parse_anon_const_expr()?) } else { None };
+            let disr_expr =
+                if this.eat(&token::Eq) { Some(this.parse_anon_const_expr()?) } else { None };
 
-        let vr = ast::Variant {
-            ident,
-            vis,
-            id: DUMMY_NODE_ID,
-            attrs: variant_attrs,
-            data: struct_def,
-            disr_expr,
-            span: vlo.to(self.prev_token.span),
-            is_placeholder: false,
-        };
+            if !matches!(this.token.kind, token::CloseDelim(..)) {
+                if let Err(mut e) = this.expect(&token::Comma) {
+                    if this.token.is_ident() {
+                        let sp = this.sess.source_map().next_point(this.prev_token.span);
+                        e.span_suggestion(
+                            sp,
+                            "try adding a comma",
+                            ",".into(),
+                            Applicability::MachineApplicable,
+                        )
+                        .emit();
+                    }
+                }
+            }
 
-        Ok(Some(vr))
+            let vr = ast::Variant {
+                ident,
+                vis,
+                id: DUMMY_NODE_ID,
+                attrs: variant_attrs,
+                data: struct_def,
+                disr_expr,
+                span: vlo.to(this.prev_token.span),
+                is_placeholder: false,
+            };
+
+            Ok(Some(vr))
+        })
     }
 
     /// Parses `struct Foo { ... }`.
@@ -1147,7 +1165,7 @@ impl<'a> Parser<'a> {
             VariantData::Struct(fields, recovered)
         // Tuple-style struct definition with optional where-clause.
         } else if self.token == token::OpenDelim(token::Paren) {
-            let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
+            let body = VariantData::Tuple(self.parse_tuple_struct_body("struct")?, DUMMY_NODE_ID);
             generics.where_clause = self.parse_where_clause()?;
             self.expect_semi()?;
             body
@@ -1192,11 +1210,26 @@ impl<'a> Parser<'a> {
     fn parse_record_struct_body(
         &mut self,
     ) -> PResult<'a, (Vec<StructField>, /* recovered */ bool)> {
+        self.parse_struct_or_enum_body("struct", DelimToken::Brace, |this| {
+            this.parse_struct_decl_field()
+        })
+    }
+
+    fn parse_enum_body(&mut self) -> PResult<'a, (Vec<Option<Variant>>, bool)> {
+        self.parse_struct_or_enum_body("enum", DelimToken::Brace, |this| this.parse_enum_variant())
+    }
+
+    fn parse_struct_or_enum_body<T>(
+        &mut self,
+        name: &str,
+        delim: DelimToken,
+        mut parse: impl FnMut(&mut Self) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, /* recovered */ bool)> {
         let mut fields = Vec::new();
         let mut recovered = false;
-        if self.eat(&token::OpenDelim(token::Brace)) {
-            while self.token != token::CloseDelim(token::Brace) {
-                let field = self.parse_struct_decl_field().map_err(|e| {
+        if self.eat(&token::OpenDelim(delim)) {
+            while self.token != token::CloseDelim(delim) {
+                let field = parse(self).map_err(|e| {
                     self.consume_block(token::Brace, ConsumeClosingDelim::No);
                     recovered = true;
                     e
@@ -1209,45 +1242,75 @@ impl<'a> Parser<'a> {
                     }
                 }
             }
-            self.eat(&token::CloseDelim(token::Brace));
+            self.eat(&token::CloseDelim(delim));
         } else {
+            let delim_descr = match delim {
+                DelimToken::Paren => "(",
+                DelimToken::Bracket => "[",
+                DelimToken::Brace => "{",
+                DelimToken::NoDelim => panic!("Delimiter `NoDelim` not supported!"),
+            };
             let token_str = super::token_descr(&self.token);
-            let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str);
+            let msg = &format!(
+                "expected `where`, or `{}` after {} name, found {}",
+                delim_descr, name, token_str
+            );
             let mut err = self.struct_span_err(self.token.span, msg);
-            err.span_label(self.token.span, "expected `where`, or `{` after struct name");
+            err.span_label(
+                self.token.span,
+                &format!("expected `where`, or `{}` after {} name", delim_descr, name),
+            );
             return Err(err);
         }
 
         Ok((fields, recovered))
     }
 
-    fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> {
+    fn parse_tuple_struct_body(&mut self, name: &str) -> PResult<'a, Vec<StructField>> {
         // This is the case where we find `struct Foo<T>(T) where T: Copy;`
         // Unit like structs are handled in parse_item_struct function
-        self.parse_paren_comma_seq(|p| {
-            let attrs = p.parse_outer_attributes()?;
-            let lo = p.token.span;
-            let vis = p.parse_visibility(FollowedByType::Yes)?;
-            let ty = p.parse_ty()?;
-            Ok(StructField {
-                span: lo.to(ty.span),
-                vis,
-                ident: None,
-                id: DUMMY_NODE_ID,
-                ty,
-                attrs,
-                is_placeholder: false,
+        let (fields, _) = self.parse_struct_or_enum_body(name, DelimToken::Paren, |p| {
+            p.parse_outer_attributes(|p, attrs| {
+                let lo = p.token.span;
+                let vis = p.parse_visibility(FollowedByType::Yes)?;
+                let ty = p.parse_ty()?;
+
+                if !matches!(p.token.kind, token::CloseDelim(..)) {
+                    if let Err(mut e) = p.expect(&token::Comma) {
+                        if p.token.is_ident() {
+                            let sp = p.sess.source_map().next_point(p.prev_token.span);
+                            e.span_suggestion(
+                                sp,
+                                "try adding a comma",
+                                ",".into(),
+                                Applicability::MachineApplicable,
+                            )
+                            .emit();
+                        }
+                    }
+                }
+
+                Ok(StructField {
+                    span: lo.to(ty.span),
+                    vis,
+                    ident: None,
+                    id: DUMMY_NODE_ID,
+                    ty,
+                    attrs,
+                    is_placeholder: false,
+                })
             })
-        })
-        .map(|(r, _)| r)
+        })?;
+        Ok(fields)
     }
 
     /// Parses an element of a struct declaration.
     fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> {
-        let attrs = self.parse_outer_attributes()?;
-        let lo = self.token.span;
-        let vis = self.parse_visibility(FollowedByType::No)?;
-        self.parse_single_struct_field(lo, vis, attrs)
+        self.parse_outer_attributes(|this, attrs| {
+            let lo = this.token.span;
+            let vis = this.parse_visibility(FollowedByType::No)?;
+            this.parse_single_struct_field(lo, vis, attrs)
+        })
     }
 
     /// Parses a structure field declaration.
@@ -1684,75 +1747,75 @@ impl<'a> Parser<'a> {
     /// - `self` is syntactically allowed when `first_param` holds.
     fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResult<'a, Param> {
         let lo = self.token.span;
-        let attrs = self.parse_outer_attributes()?;
-
-        // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
-        if let Some(mut param) = self.parse_self_param()? {
-            param.attrs = attrs.into();
-            return if first_param { Ok(param) } else { self.recover_bad_self_param(param) };
-        }
+        self.parse_outer_attributes(|this, attrs| {
+            // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
+            if let Some(mut param) = this.parse_self_param()? {
+                param.attrs = attrs.into();
+                return if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
+            }
 
-        let is_name_required = match self.token.kind {
-            token::DotDotDot => false,
-            _ => req_name(self.token.span.edition()),
-        };
-        let (pat, ty) = if is_name_required || self.is_named_param() {
-            debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
+            let is_name_required = match this.token.kind {
+                token::DotDotDot => false,
+                _ => req_name(this.token.span.edition()),
+            };
+            let (pat, ty) = if is_name_required || this.is_named_param() {
+                debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
+
+                let pat = this.parse_fn_param_pat()?;
+                if let Err(mut err) = this.expect(&token::Colon) {
+                    return if let Some(ident) =
+                        this.parameter_without_type(&mut err, pat, is_name_required, first_param)
+                    {
+                        err.emit();
+                        Ok(dummy_arg(ident))
+                    } else {
+                        Err(err)
+                    };
+                }
 
-            let pat = self.parse_fn_param_pat()?;
-            if let Err(mut err) = self.expect(&token::Colon) {
-                return if let Some(ident) =
-                    self.parameter_without_type(&mut err, pat, is_name_required, first_param)
+                this.eat_incorrect_doc_comment_for_param_type();
+                (pat, this.parse_ty_for_param()?)
+            } else {
+                debug!("parse_param_general ident_to_pat");
+                let parser_snapshot_before_ty = this.clone();
+                this.eat_incorrect_doc_comment_for_param_type();
+                let mut ty = this.parse_ty_for_param();
+                if ty.is_ok()
+                    && this.token != token::Comma
+                    && this.token != token::CloseDelim(token::Paren)
                 {
-                    err.emit();
-                    Ok(dummy_arg(ident))
-                } else {
-                    Err(err)
-                };
-            }
-
-            self.eat_incorrect_doc_comment_for_param_type();
-            (pat, self.parse_ty_for_param()?)
-        } else {
-            debug!("parse_param_general ident_to_pat");
-            let parser_snapshot_before_ty = self.clone();
-            self.eat_incorrect_doc_comment_for_param_type();
-            let mut ty = self.parse_ty_for_param();
-            if ty.is_ok()
-                && self.token != token::Comma
-                && self.token != token::CloseDelim(token::Paren)
-            {
-                // This wasn't actually a type, but a pattern looking like a type,
-                // so we are going to rollback and re-parse for recovery.
-                ty = self.unexpected();
-            }
-            match ty {
-                Ok(ty) => {
-                    let ident = Ident::new(kw::Invalid, self.prev_token.span);
-                    let bm = BindingMode::ByValue(Mutability::Not);
-                    let pat = self.mk_pat_ident(ty.span, bm, ident);
-                    (pat, ty)
+                    // This wasn't actually a type, but a pattern looking like a type,
+                    // so we are going to rollback and re-parse for recovery.
+                    ty = this.unexpected();
                 }
-                // If this is a C-variadic argument and we hit an error, return the error.
-                Err(err) if self.token == token::DotDotDot => return Err(err),
-                // Recover from attempting to parse the argument as a type without pattern.
-                Err(mut err) => {
-                    err.cancel();
-                    *self = parser_snapshot_before_ty;
-                    self.recover_arg_parse()?
+                match ty {
+                    Ok(ty) => {
+                        let ident = Ident::new(kw::Invalid, this.prev_token.span);
+                        let bm = BindingMode::ByValue(Mutability::Not);
+                        let pat = this.mk_pat_ident(ty.span, bm, ident);
+                        (pat, ty)
+                    }
+                    // If this is a C-variadic argument and we hit an error, return the error.
+                    Err(err) if this.token == token::DotDotDot => return Err(err),
+                    // Recover from attempting to parse the argument as a type without pattern.
+                    Err(mut err) => {
+                        err.cancel();
+                        *this = parser_snapshot_before_ty;
+                        this.recover_arg_parse()?
+                    }
                 }
-            }
-        };
+            };
 
-        let span = lo.to(self.token.span);
+            let span = lo.to(this.token.span);
 
-        Ok(Param {
-            attrs: attrs.into(),
-            id: ast::DUMMY_NODE_ID,
-            is_placeholder: false,
-            pat,
-            span,
-            ty,
+            Ok(Param {
+                attrs: attrs.into(),
+                id: ast::DUMMY_NODE_ID,
+                is_placeholder: false,
+                pat,
+                span,
+                ty,
+            })
         })
     }
 
@@ -1871,11 +1934,10 @@ impl<'a> Parser<'a> {
     }
 
     fn recover_first_param(&mut self) -> &'static str {
-        match self
-            .parse_outer_attributes()
-            .and_then(|_| self.parse_self_param())
-            .map_err(|mut e| e.cancel())
-        {
+        let res = self
+            .parse_outer_attributes(|this, _attrs| this.parse_self_param())
+            .map_err(|mut err| err.cancel());
+        match res {
             Ok(Some(_)) => "method",
             _ => "function",
         }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 7340c5744808c..2dbbff66b5480 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -15,9 +15,12 @@ pub use path::PathStyle;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, DelimToken, Token, TokenKind};
-use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
+use rustc_ast::tokenstream::{
+    self, AttributesData, DelimSpan, PreexpTokenStream, PreexpTokenTree, Spacing, TokenStream,
+    TokenTree,
+};
 use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe};
+use rustc_ast::{self as ast, AttrStyle, Attribute, Const, CrateSugar, Extern, Unsafe};
 use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
@@ -106,6 +109,8 @@ pub struct Parser<'a> {
     pub last_type_ascription: Option<(Span, bool /* likely path typo */)>,
     /// If present, this `Parser` is not parsing Rust code but rather a macro call.
     subparser_name: Option<&'static str>,
+    pub is_rustdoc: bool,
+    in_derive: bool,
 }
 
 impl<'a> Drop for Parser<'a> {
@@ -118,8 +123,8 @@ impl<'a> Drop for Parser<'a> {
 struct TokenCursor {
     frame: TokenCursorFrame,
     stack: Vec<TokenCursorFrame>,
-    cur_token: Option<TreeAndSpacing>,
-    collecting: Option<Collecting>,
+    collecting_buf: Vec<(FlatToken, Spacing)>,
+    is_collecting: bool,
 }
 
 #[derive(Clone)]
@@ -129,24 +134,13 @@ struct TokenCursorFrame {
     open_delim: bool,
     tree_cursor: tokenstream::Cursor,
     close_delim: bool,
+    modified_stream: Vec<(PreexpTokenTree, Spacing)>,
 }
 
-/// Used to track additional state needed by `collect_tokens`
-#[derive(Clone, Debug)]
-struct Collecting {
-    /// Holds the current tokens captured during the most
-    /// recent call to `collect_tokens`
-    buf: Vec<TreeAndSpacing>,
-    /// The depth of the `TokenCursor` stack at the time
-    /// collection was started. When we encounter a `TokenTree::Delimited`,
-    /// we want to record the `TokenTree::Delimited` itself,
-    /// but *not* any of the inner tokens while we are inside
-    /// the new frame (this would cause us to record duplicate tokens).
-    ///
-    /// This `depth` fields tracks stack depth we are recording tokens.
-    /// Only tokens encountered at this depth will be recorded. See
-    /// `TokenCursor::next` for more details.
-    depth: usize,
+#[derive(Clone)]
+enum FlatToken {
+    Token(Token),
+    OuterAttributes(AttributesData),
 }
 
 impl TokenCursorFrame {
@@ -157,6 +151,7 @@ impl TokenCursorFrame {
             open_delim: delim == token::NoDelim,
             tree_cursor: tts.clone().into_trees(),
             close_delim: delim == token::NoDelim,
+            modified_stream: vec![],
         }
     }
 }
@@ -176,29 +171,19 @@ impl TokenCursor {
                 self.frame = frame;
                 continue;
             } else {
-                return Token::new(token::Eof, DUMMY_SP);
+                (TokenTree::Token(Token::new(token::Eof, DUMMY_SP)), Spacing::Alone)
             };
 
-            // Don't set an open delimiter as our current token - we want
-            // to leave it as the full `TokenTree::Delimited` from the previous
-            // iteration of this loop
-            if !matches!(tree.0, TokenTree::Token(Token { kind: TokenKind::OpenDelim(_), .. })) {
-                self.cur_token = Some(tree.clone());
-            }
-
-            if let Some(collecting) = &mut self.collecting {
-                if collecting.depth == self.stack.len() {
-                    debug!(
-                        "TokenCursor::next():  collected {:?} at depth {:?}",
-                        tree,
-                        self.stack.len()
-                    );
-                    collecting.buf.push(tree.clone())
+            match tree.0.clone() {
+                TokenTree::Token(token) => {
+                    if self.is_collecting {
+                        self.collecting_buf.push((FlatToken::Token(token.clone()), tree.1));
+                    } else {
+                        self.collecting_buf[0] = (FlatToken::Token(token.clone()), tree.1);
+                        debug_assert_eq!(self.collecting_buf.len(), 1);
+                    }
+                    return token;
                 }
-            }
-
-            match tree.0 {
-                TokenTree::Token(token) => return token,
                 TokenTree::Delimited(sp, delim, tts) => {
                     let frame = TokenCursorFrame::new(sp, delim, &tts);
                     self.stack.push(mem::replace(&mut self.frame, frame));
@@ -355,8 +340,12 @@ impl<'a> Parser<'a> {
             token_cursor: TokenCursor {
                 frame: TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, &tokens),
                 stack: Vec::new(),
-                cur_token: None,
-                collecting: None,
+                // Will get overwritten when we bump the parser below
+                collecting_buf: vec![(
+                    FlatToken::Token(Token::new(token::Eof, DUMMY_SP)),
+                    Spacing::Alone,
+                )],
+                is_collecting: false,
             },
             desugar_doc_comments,
             unmatched_angle_bracket_count: 0,
@@ -365,6 +354,8 @@ impl<'a> Parser<'a> {
             last_unexpected_token_span: None,
             last_type_ascription: None,
             subparser_name,
+            is_rustdoc: false,
+            in_derive: false,
         };
 
         // Make parser point to the first token.
@@ -947,28 +938,23 @@ impl<'a> Parser<'a> {
         )
     }
 
-    fn parse_or_use_outer_attributes(
-        &mut self,
-        already_parsed_attrs: Option<AttrVec>,
-    ) -> PResult<'a, AttrVec> {
-        if let Some(attrs) = already_parsed_attrs {
-            Ok(attrs)
-        } else {
-            self.parse_outer_attributes().map(|a| a.into())
-        }
-    }
-
     /// Parses a single token tree from the input.
     pub(crate) fn parse_token_tree(&mut self) -> TokenTree {
         match self.token.kind {
             token::OpenDelim(..) => {
-                let frame = mem::replace(
-                    &mut self.token_cursor.frame,
-                    self.token_cursor.stack.pop().unwrap(),
-                );
-                self.token = Token::new(TokenKind::CloseDelim(frame.delim), frame.span.close);
+                let depth = self.token_cursor.stack.len();
+                while !(depth == self.token_cursor.stack.len()
+                    && matches!(self.token.kind, token::CloseDelim(_)))
+                {
+                    self.bump();
+                }
+                let frame = &self.token_cursor.frame;
+                let stream = frame.tree_cursor.stream.clone();
+                let span = frame.span;
+                let delim = frame.delim;
+                // Consume close delimiter
                 self.bump();
-                TokenTree::Delimited(frame.span, frame.delim, frame.tree_cursor.stream)
+                TokenTree::Delimited(span, delim, stream)
             }
             token::CloseDelim(_) | token::Eof => unreachable!(),
             _ => {
@@ -1157,6 +1143,13 @@ impl<'a> Parser<'a> {
         }
     }
 
+    pub fn collect_tokens_no_attrs<R>(
+        &mut self,
+        f: impl FnOnce(&mut Self) -> PResult<'a, R>,
+    ) -> PResult<'a, (R, PreexpTokenStream)> {
+        self.collect_tokens(|this| f(this).map(|res| (res, Vec::new())))
+    }
+
     /// Records all tokens consumed by the provided callback,
     /// including the current token. These tokens are collected
     /// into a `TokenStream`, and returned along with the result
@@ -1175,80 +1168,9 @@ impl<'a> Parser<'a> {
     /// a parsed AST item, which always has matching delimiters.
     pub fn collect_tokens<R>(
         &mut self,
-        f: impl FnOnce(&mut Self) -> PResult<'a, R>,
-    ) -> PResult<'a, (R, TokenStream)> {
-        // Record all tokens we parse when parsing this item.
-        let tokens: Vec<TreeAndSpacing> = self.token_cursor.cur_token.clone().into_iter().collect();
-        debug!("collect_tokens: starting with {:?}", tokens);
-
-        // We need special handling for the case where `collect_tokens` is called
-        // on an opening delimeter (e.g. '('). At this point, we have already pushed
-        // a new frame - however, we want to record the original `TokenTree::Delimited`,
-        // for consistency with the case where we start recording one token earlier.
-        // See `TokenCursor::next` to see how `cur_token` is set up.
-        let prev_depth =
-            if matches!(self.token_cursor.cur_token, Some((TokenTree::Delimited(..), _))) {
-                if self.token_cursor.stack.is_empty() {
-                    // There is nothing below us in the stack that
-                    // the function could consume, so the only thing it can legally
-                    // capture is the entire contents of the current frame.
-                    return Ok((f(self)?, TokenStream::new(tokens)));
-                }
-                // We have already recorded the full `TokenTree::Delimited` when we created
-                // our `tokens` vector at the start of this function. We are now inside
-                // a new frame corresponding to the `TokenTree::Delimited` we already recoreded.
-                // We don't want to record any of the tokens inside this frame, since they
-                // will be duplicates of the tokens nested inside the `TokenTree::Delimited`.
-                // Therefore, we set our recording depth to the *previous* frame. This allows
-                // us to record a sequence like: `(foo).bar()`: the `(foo)` will be recored
-                // as our initial `cur_token`, while the `.bar()` will be recored after we
-                // pop the `(foo)` frame.
-                self.token_cursor.stack.len() - 1
-            } else {
-                self.token_cursor.stack.len()
-            };
-        let prev_collecting =
-            self.token_cursor.collecting.replace(Collecting { buf: tokens, depth: prev_depth });
-
-        let ret = f(self);
-
-        let mut collected_tokens = if let Some(collecting) = self.token_cursor.collecting.take() {
-            collecting.buf
-        } else {
-            let msg = "our vector went away?";
-            debug!("collect_tokens: {}", msg);
-            self.sess.span_diagnostic.delay_span_bug(self.token.span, &msg);
-            // This can happen due to a bad interaction of two unrelated recovery mechanisms
-            // with mismatched delimiters *and* recovery lookahead on the likely typo
-            // `pub ident(` (#62895, different but similar to the case above).
-            return Ok((ret?, TokenStream::default()));
-        };
-
-        debug!("collect_tokens: got raw tokens {:?}", collected_tokens);
-
-        // If we're not at EOF our current token wasn't actually consumed by
-        // `f`, but it'll still be in our list that we pulled out. In that case
-        // put it back.
-        let extra_token = if self.token != token::Eof { collected_tokens.pop() } else { None };
-
-        if let Some(mut collecting) = prev_collecting {
-            // If we were previously collecting at the same depth,
-            // then the previous call to `collect_tokens` needs to see
-            // the tokens we just recorded.
-            //
-            // If we were previously recording at an lower `depth`,
-            // then the previous `collect_tokens` call already recorded
-            // this entire frame in the form of a `TokenTree::Delimited`,
-            // so there is nothing else for us to do.
-            if collecting.depth == prev_depth {
-                collecting.buf.extend(collected_tokens.iter().cloned());
-                collecting.buf.extend(extra_token);
-                debug!("collect_tokens: updating previous buf to {:?}", collecting);
-            }
-            self.token_cursor.collecting = Some(collecting)
-        }
-
-        Ok((ret?, TokenStream::new(collected_tokens)))
+        f: impl FnOnce(&mut Self) -> PResult<'a, (R, Vec<Attribute>)>,
+    ) -> PResult<'a, (R, PreexpTokenStream)> {
+        self.collect_tokens_keep_in_stream(true, f)
     }
 
     /// `::{` or `::*`
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 15660fd574c13..c8a1823476b8f 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -98,33 +98,38 @@ impl<'a> Parser<'a> {
         // in advance whether or not a proc-macro will be (transitively) invoked,
         // we always capture tokens for any `Nonterminal` which needs them.
         Ok(match kind {
-            NonterminalKind::Item => match self.collect_tokens(|this| this.parse_item())? {
-                (Some(mut item), tokens) => {
-                    // If we captured tokens during parsing (due to outer attributes),
-                    // use those.
-                    if item.tokens.is_none() {
-                        item.tokens = Some(tokens);
+            NonterminalKind::Item => {
+                match self.collect_tokens_no_attrs(|this| this.parse_item())? {
+                    (Some(mut item), tokens) => {
+                        // If we captured tokens during parsing (due to outer attributes),
+                        // use those.
+                        if item.tokens.is_none() {
+                            item.tokens = Some(tokens);
+                        }
+                        token::NtItem(item)
+                    }
+                    (None, _) => {
+                        return Err(
+                            self.struct_span_err(self.token.span, "expected an item keyword")
+                        );
                     }
-                    token::NtItem(item)
-                }
-                (None, _) => {
-                    return Err(self.struct_span_err(self.token.span, "expected an item keyword"));
                 }
-            },
+            }
             NonterminalKind::Block => {
-                let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?;
+                let (mut block, tokens) =
+                    self.collect_tokens_no_attrs(|this| this.parse_block())?;
                 // We have have eaten an NtBlock, which could already have tokens
                 if block.tokens.is_none() {
-                    block.tokens = Some(tokens);
+                    block.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtBlock(block)
             }
             NonterminalKind::Stmt => {
-                let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?;
+                let (stmt, tokens) = self.collect_tokens_no_attrs(|this| this.parse_stmt())?;
                 match stmt {
                     Some(mut s) => {
                         if s.tokens.is_none() {
-                            s.tokens = Some(tokens);
+                            s.tokens = Some(tokens.to_tokenstream());
                         }
                         token::NtStmt(s)
                     }
@@ -134,15 +139,16 @@ impl<'a> Parser<'a> {
                 }
             }
             NonterminalKind::Pat => {
-                let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?;
+                let (mut pat, tokens) =
+                    self.collect_tokens_no_attrs(|this| this.parse_pat(None))?;
                 // We have have eaten an NtPat, which could already have tokens
                 if pat.tokens.is_none() {
-                    pat.tokens = Some(tokens);
+                    pat.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtPat(pat)
             }
             NonterminalKind::Expr => {
-                let (mut expr, tokens) = self.collect_tokens(|this| this.parse_expr())?;
+                let (mut expr, tokens) = self.collect_tokens_no_attrs(|this| this.parse_expr())?;
                 // If we captured tokens during parsing (due to outer attributes),
                 // use those.
                 if expr.tokens.is_none() {
@@ -152,7 +158,7 @@ impl<'a> Parser<'a> {
             }
             NonterminalKind::Literal => {
                 let (mut lit, tokens) =
-                    self.collect_tokens(|this| this.parse_literal_maybe_minus())?;
+                    self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?;
                 // We have have eaten a nonterminal, which  could already have tokens
                 if lit.tokens.is_none() {
                     lit.tokens = Some(tokens);
@@ -160,10 +166,10 @@ impl<'a> Parser<'a> {
                 token::NtLiteral(lit)
             }
             NonterminalKind::Ty => {
-                let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?;
+                let (mut ty, tokens) = self.collect_tokens_no_attrs(|this| this.parse_ty())?;
                 // We have an eaten an NtTy, which could already have tokens
                 if ty.tokens.is_none() {
-                    ty.tokens = Some(tokens);
+                    ty.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtTy(ty)
             }
@@ -180,28 +186,29 @@ impl<'a> Parser<'a> {
             }
             NonterminalKind::Path => {
                 let (mut path, tokens) =
-                    self.collect_tokens(|this| this.parse_path(PathStyle::Type))?;
+                    self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?;
                 // We have have eaten an NtPath, which could already have tokens
                 if path.tokens.is_none() {
-                    path.tokens = Some(tokens);
+                    path.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtPath(path)
             }
             NonterminalKind::Meta => {
-                let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?;
+                let (mut attr, tokens) =
+                    self.collect_tokens_no_attrs(|this| this.parse_attr_item())?;
                 // We may have eaten a nonterminal, which could already have tokens
                 if attr.tokens.is_none() {
-                    attr.tokens = Some(tokens);
+                    attr.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtMeta(P(attr))
             }
             NonterminalKind::TT => token::NtTT(self.parse_token_tree()),
             NonterminalKind::Vis => {
-                let (mut vis, tokens) =
-                    self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?;
+                let (mut vis, tokens) = self
+                    .collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?;
                 // We may have etan an `NtVis`, which could already have tokens
                 if vis.tokens.is_none() {
-                    vis.tokens = Some(tokens);
+                    vis.tokens = Some(tokens.to_tokenstream());
                 }
                 token::NtVis(vis)
             }
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 2c0133a24dcb1..88ff2547d5490 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -831,102 +831,107 @@ impl<'a> Parser<'a> {
         let mut etc_span = None;
 
         while self.token != token::CloseDelim(token::Brace) {
-            let attrs = match self.parse_outer_attributes() {
-                Ok(attrs) => attrs,
-                Err(err) => {
-                    if let Some(mut delayed) = delayed_err {
+            let field_pat = self.parse_outer_attributes(|this, attrs| {
+                let lo = this.token.span;
+
+                // check that a comma comes after every field
+                if !ate_comma {
+                    let err = this.struct_span_err(this.prev_token.span, "expected `,`");
+                    if let Some(delayed) = delayed_err.as_mut() {
                         delayed.emit();
                     }
                     return Err(err);
                 }
-            };
-            let lo = self.token.span;
-
-            // check that a comma comes after every field
-            if !ate_comma {
-                let err = self.struct_span_err(self.prev_token.span, "expected `,`");
-                if let Some(mut delayed) = delayed_err {
-                    delayed.emit();
-                }
-                return Err(err);
-            }
-            ate_comma = false;
+                ate_comma = false;
 
-            if self.check(&token::DotDot) || self.token == token::DotDotDot {
-                etc = true;
-                let mut etc_sp = self.token.span;
+                if this.check(&token::DotDot) || this.token == token::DotDotDot {
+                    etc = true;
+                    let mut etc_sp = this.token.span;
 
-                self.recover_one_fewer_dotdot();
-                self.bump(); // `..` || `...`
+                    this.recover_one_fewer_dotdot();
+                    this.bump(); // `..` || `...`
 
-                if self.token == token::CloseDelim(token::Brace) {
-                    etc_span = Some(etc_sp);
-                    break;
-                }
-                let token_str = super::token_descr(&self.token);
-                let msg = &format!("expected `}}`, found {}", token_str);
-                let mut err = self.struct_span_err(self.token.span, msg);
-
-                err.span_label(self.token.span, "expected `}`");
-                let mut comma_sp = None;
-                if self.token == token::Comma {
-                    // Issue #49257
-                    let nw_span = self.sess.source_map().span_until_non_whitespace(self.token.span);
-                    etc_sp = etc_sp.to(nw_span);
-                    err.span_label(
-                        etc_sp,
-                        "`..` must be at the end and cannot have a trailing comma",
-                    );
-                    comma_sp = Some(self.token.span);
-                    self.bump();
-                    ate_comma = true;
-                }
-
-                etc_span = Some(etc_sp.until(self.token.span));
-                if self.token == token::CloseDelim(token::Brace) {
-                    // If the struct looks otherwise well formed, recover and continue.
-                    if let Some(sp) = comma_sp {
-                        err.span_suggestion_short(
-                            sp,
-                            "remove this comma",
-                            String::new(),
-                            Applicability::MachineApplicable,
-                        );
+                    if this.token == token::CloseDelim(token::Brace) {
+                        etc_span = Some(etc_sp);
+                        return Ok(None);
                     }
-                    err.emit();
-                    break;
-                } else if self.token.is_ident() && ate_comma {
-                    // Accept fields coming after `..,`.
-                    // This way we avoid "pattern missing fields" errors afterwards.
-                    // We delay this error until the end in order to have a span for a
-                    // suggested fix.
-                    if let Some(mut delayed_err) = delayed_err {
-                        delayed_err.emit();
-                        return Err(err);
-                    } else {
-                        delayed_err = Some(err);
+                    let token_str = super::token_descr(&this.token);
+                    let msg = &format!("expected `}}`, found {}", token_str);
+                    let mut err = this.struct_span_err(this.token.span, msg);
+
+                    err.span_label(this.token.span, "expected `}`");
+                    let mut comma_sp = None;
+                    if this.token == token::Comma {
+                        // Issue #49257
+                        let nw_span =
+                            this.sess.source_map().span_until_non_whitespace(this.token.span);
+                        etc_sp = etc_sp.to(nw_span);
+                        err.span_label(
+                            etc_sp,
+                            "`..` must be at the end and cannot have a trailing comma",
+                        );
+                        comma_sp = Some(this.token.span);
+                        this.bump();
+                        ate_comma = true;
                     }
-                } else {
-                    if let Some(mut err) = delayed_err {
+
+                    etc_span = Some(etc_sp.until(this.token.span));
+                    if this.token == token::CloseDelim(token::Brace) {
+                        // If the struct looks otherwise well formed, recover and continue.
+                        if let Some(sp) = comma_sp {
+                            err.span_suggestion_short(
+                                sp,
+                                "remove this comma",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
                         err.emit();
+                        return Ok(None);
+                    } else if this.token.is_ident() && ate_comma {
+                        // Accept fields coming after `..,`.
+                        // This way we avoid "pattern missing fields" errors afterwards.
+                        // We delay this error until the end in order to have a span for a
+                        // suggested fix.
+                        if let Some(delayed_err) = delayed_err.as_mut() {
+                            delayed_err.emit();
+                            return Err(err);
+                        } else {
+                            delayed_err = Some(err);
+                        }
+                    } else {
+                        if let Some(err) = delayed_err.as_mut() {
+                            err.emit();
+                        }
+                        return Err(err);
                     }
-                    return Err(err);
                 }
-            }
 
-            fields.push(match self.parse_pat_field(lo, attrs) {
-                Ok(field) => field,
+                let field_pat = match this.parse_pat_field(lo, attrs) {
+                    Ok(field) => field,
+                    Err(err) => {
+                        if let Some(delayed_err) = delayed_err.as_mut() {
+                            delayed_err.emit();
+                        }
+                        return Err(err);
+                    }
+                };
+                ate_comma = this.eat(&token::Comma);
+                Ok(Some(field_pat))
+            });
+            match field_pat {
+                Ok(Some(field_pat)) => fields.push(field_pat),
+                Ok(None) => break,
                 Err(err) => {
-                    if let Some(mut delayed_err) = delayed_err {
-                        delayed_err.emit();
+                    if let Some(delayed) = delayed_err.as_mut() {
+                        delayed.emit();
                     }
                     return Err(err);
                 }
-            });
-            ate_comma = self.eat(&token::Comma);
+            }
         }
 
-        if let Some(mut err) = delayed_err {
+        if let Some(err) = delayed_err.as_mut() {
             if let Some(etc_span) = etc_span {
                 err.multipart_suggestion(
                     "move the `..` to the end of the field list",
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index fd1c6b25aec63..56d489f1df4ba 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -33,43 +33,68 @@ impl<'a> Parser<'a> {
     fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> {
         maybe_whole!(self, NtStmt, |x| Some(x));
 
-        let attrs = self.parse_outer_attributes()?;
-        let lo = self.token.span;
-
-        let stmt = if self.eat_keyword(kw::Let) {
-            self.parse_local_mk(lo, attrs.into())?
-        } else if self.is_kw_followed_by_ident(kw::Mut) {
-            self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")?
-        } else if self.is_kw_followed_by_ident(kw::Auto) {
-            self.bump(); // `auto`
-            let msg = "write `let` instead of `auto` to introduce a new variable";
-            self.recover_stmt_local(lo, attrs.into(), msg, "let")?
-        } else if self.is_kw_followed_by_ident(sym::var) {
-            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.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.
-            // Also, we avoid stealing syntax from `parse_item_`.
-            self.parse_stmt_path_start(lo, attrs)?
-        } else if let Some(item) = self.parse_item_common(attrs.clone(), false, true, |_| true)? {
-            // FIXME: Bad copy of attrs
-            self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
-        } else if self.eat(&token::Semi) {
-            // Do not attempt to parse an expression if we're done here.
-            self.error_outer_attrs(&attrs);
-            self.mk_stmt(lo, StmtKind::Empty)
-        } else if self.token != token::CloseDelim(token::Brace) {
-            // Remainder are line-expr stmts.
-            let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?;
-            self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
-        } else {
-            self.error_outer_attrs(&attrs);
-            return Ok(None);
+        let (mut stmt, tokens) = self.parse_outer_attributes_with_tokens(|this, attrs| {
+            let lo = this.token.span;
+
+            let stmt = if this.eat_keyword(kw::Let) {
+                this.parse_local_mk(lo, attrs.into())?
+            } else if this.is_kw_followed_by_ident(kw::Mut) {
+                this.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")?
+            } else if this.is_kw_followed_by_ident(kw::Auto) {
+                this.bump(); // `auto`
+                let msg = "write `let` instead of `auto` to introduce a new variable";
+                this.recover_stmt_local(lo, attrs.into(), msg, "let")?
+            } else if this.is_kw_followed_by_ident(sym::var) {
+                this.bump(); // `var`
+                let msg = "write `let` instead of `var` to introduce a new variable";
+                this.recover_stmt_local(lo, attrs.into(), msg, "let")?
+            } else if this.check_path()
+                && !this.token.is_qpath_start()
+                && !this.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.
+                // Also, we avoid stealing syntax from `parse_item_`.
+                this.parse_stmt_path_start(lo, attrs)?
+            } else if let Some(item) =
+                this.parse_item_common(attrs.clone(), false, true, |_| true)?
+            {
+                // FIXME: Bad copy of attrs
+                this.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
+            } else if this.eat(&token::Semi) {
+                // Do not attempt to parse an expression if we're done here.
+                this.error_outer_attrs(&attrs);
+                this.mk_stmt(lo, StmtKind::Empty)
+            } else if this.token != token::CloseDelim(token::Brace) {
+                // Remainder are line-expr stmts.
+                let e = this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?;
+                this.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
+            } else {
+                this.error_outer_attrs(&attrs);
+                return Ok(None);
+            };
+            Ok(Some(stmt))
+        })?;
+        let old_tokens = match stmt.as_mut() {
+            Some(Stmt { kind: StmtKind::Local(local), .. }) => Some(&mut local.tokens),
+            Some(Stmt { kind: StmtKind::Item(item), .. }) => Some(&mut item.tokens),
+            Some(Stmt { kind: StmtKind::Expr(expr) | StmtKind::Semi(expr), .. }) => {
+                // FIXME: Attribute parsing should do this for us.
+                Some(&mut expr.tokens)
+            }
+            Some(Stmt { kind: StmtKind::Empty, .. }) => None,
+            Some(Stmt { kind: StmtKind::MacCall(mac), .. }) => Some(&mut mac.tokens),
+            None => None,
         };
-        Ok(Some(stmt))
+
+        if let Some(old_tokens) = old_tokens {
+            if old_tokens.is_none() {
+                *old_tokens = tokens;
+            }
+        }
+
+        Ok(stmt)
     }
 
     fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, Stmt> {
@@ -107,7 +132,7 @@ impl<'a> Parser<'a> {
 
         let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof
         {
-            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs }))
+            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
         } else {
             // Since none of the above applied, this is an expression statement macro.
             let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
@@ -219,7 +244,7 @@ impl<'a> Parser<'a> {
             }
         };
         let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
-        Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs }))
+        Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
     }
 
     /// Parses the RHS of a local variable declaration (e.g., '= 14;').
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 7b7c152d8abbf..b3cb938b229e1 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -432,6 +432,8 @@ pub fn make_test(
                 }
             };
 
+            parser.is_rustdoc = true;
+
             loop {
                 match parser.parse_item() {
                     Ok(Some(item)) => {
diff --git a/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.32bit.mir b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.32bit.mir
index 832f18e14c25d..794eee42296d0 100644
--- a/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.32bit.mir
+++ b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.32bit.mir
@@ -1,11 +1,11 @@
 // MIR for `Test::X` 0 mir_map
 
 fn Test::X(_1: usize) -> Test {
-    let mut _0: Test;                    // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
+    let mut _0: Test;                    // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
 
     bb0: {
-        ((_0 as X).0: usize) = move _1;  // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
-        discriminant(_0) = 0;            // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
-        return;                          // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
+        ((_0 as X).0: usize) = move _1;  // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
+        discriminant(_0) = 0;            // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
+        return;                          // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
     }
 }
diff --git a/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.64bit.mir b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.64bit.mir
index 832f18e14c25d..794eee42296d0 100644
--- a/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.64bit.mir
+++ b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.64bit.mir
@@ -1,11 +1,11 @@
 // MIR for `Test::X` 0 mir_map
 
 fn Test::X(_1: usize) -> Test {
-    let mut _0: Test;                    // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
+    let mut _0: Test;                    // return place in scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
 
     bb0: {
-        ((_0 as X).0: usize) = move _1;  // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
-        discriminant(_0) = 0;            // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
-        return;                          // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:13
+        ((_0 as X).0: usize) = move _1;  // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
+        discriminant(_0) = 0;            // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
+        return;                          // scope 0 at $DIR/unusual-item-types.rs:16:5: 16:14
     }
 }
diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout
index 3d7d476cf6c05..fb311a9045b35 100644
--- a/src/test/ui/ast-json/ast-json-noexpand-output.stdout
+++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout
@@ -1 +1 @@
-{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
+{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout
index a4ce6061b4c9d..d1e0f409948c2 100644
--- a/src/test/ui/ast-json/ast-json-output.stdout
+++ b/src/test/ui/ast-json/ast-json-output.stdout
@@ -1 +1 @@
-{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
+{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-parse.rs b/src/test/ui/conditional-compilation/cfg-attr-parse.rs
index 8ca31c118369c..9c7cc3698a628 100644
--- a/src/test/ui/conditional-compilation/cfg-attr-parse.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-parse.rs
@@ -2,10 +2,12 @@
 
 // Completely empty `cfg_attr` input
 #[cfg_attr()] //~ error: malformed `cfg_attr` attribute input
+//~| error: malformed `cfg_attr` attribute input
 struct NoConfigurationPredicate;
 
 // Zero attributes, zero trailing comma (comma manatory here)
 #[cfg_attr(all())] //~ error: expected `,`, found end of `cfg_attr`
+//~^ error: expected `,`, found end of `cfg_attr`
 struct A0C0;
 
 // Zero attributes, one trailing comma
@@ -14,6 +16,7 @@ struct A0C1;
 
 // Zero attributes, two trailing commas
 #[cfg_attr(all(),,)] //~ ERROR expected identifier
+//~| ERROR expected identifier
 struct A0C2;
 
 // One attribute, no trailing comma
@@ -26,6 +29,7 @@ struct A1C1;
 
 // One attribute, two trailing commas
 #[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier
+//~| ERROR expected identifier
 struct A1C2;
 
 // Two attributes, no trailing comma
@@ -38,18 +42,23 @@ struct A2C1;
 
 // Two attributes, two trailing commas
 #[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
+//~| ERROR expected identifier
 struct A2C2;
 
 // Wrong delimiter `[`
 #[cfg_attr[all(),,]]
-//~^ ERROR wrong `cfg_attr` delimiters
-//~| ERROR expected identifier, found `,`
+//~^ error wrong `cfg_attr` delimiters
+//~| error expected identifier, found `,`
+//~| error wrong `cfg_attr` delimiters
+//~| error expected identifier, found `,`
 struct BracketZero;
 
 // Wrong delimiter `{`
 #[cfg_attr{all(),,}]
-//~^ ERROR wrong `cfg_attr` delimiters
-//~| ERROR expected identifier, found `,`
+//~^ error wrong `cfg_attr` delimiters
+//~| error wrong `cfg_attr` delimiters
+//~| error expected identifier, found `,`
+//~| error expected identifier, found `,`
 struct BraceZero;
 
 fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-parse.stderr b/src/test/ui/conditional-compilation/cfg-attr-parse.stderr
index 3a590d3282d46..1713c532c8f51 100644
--- a/src/test/ui/conditional-compilation/cfg-attr-parse.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-parse.stderr
@@ -6,8 +6,16 @@ LL | #[cfg_attr()]
    |
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
+error: malformed `cfg_attr` attribute input
+  --> $DIR/cfg-attr-parse.rs:4:1
+   |
+LL | #[cfg_attr()]
+   | ^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
 error: expected `,`, found end of `cfg_attr` input
-  --> $DIR/cfg-attr-parse.rs:8:17
+  --> $DIR/cfg-attr-parse.rs:9:17
    |
 LL | #[cfg_attr(all())]
    |                 ^ expected `,`
@@ -15,8 +23,26 @@ LL | #[cfg_attr(all())]
    = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
+error: expected `,`, found end of `cfg_attr` input
+  --> $DIR/cfg-attr-parse.rs:9:17
+   |
+LL | #[cfg_attr(all())]
+   |                 ^ expected `,`
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:18:18
+   |
+LL | #[cfg_attr(all(),,)]
+   |                  ^ expected identifier
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
 error: expected identifier, found `,`
-  --> $DIR/cfg-attr-parse.rs:16:18
+  --> $DIR/cfg-attr-parse.rs:18:18
    |
 LL | #[cfg_attr(all(),,)]
    |                  ^ expected identifier
@@ -25,7 +51,7 @@ LL | #[cfg_attr(all(),,)]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: expected identifier, found `,`
-  --> $DIR/cfg-attr-parse.rs:28:28
+  --> $DIR/cfg-attr-parse.rs:31:28
    |
 LL | #[cfg_attr(all(), must_use,,)]
    |                            ^ expected identifier
@@ -34,7 +60,25 @@ LL | #[cfg_attr(all(), must_use,,)]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: expected identifier, found `,`
-  --> $DIR/cfg-attr-parse.rs:40:40
+  --> $DIR/cfg-attr-parse.rs:31:28
+   |
+LL | #[cfg_attr(all(), must_use,,)]
+   |                            ^ expected identifier
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:44:40
+   |
+LL | #[cfg_attr(all(), must_use, deprecated,,)]
+   |                                        ^ expected identifier
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:44:40
    |
 LL | #[cfg_attr(all(), must_use, deprecated,,)]
    |                                        ^ expected identifier
@@ -43,7 +87,27 @@ LL | #[cfg_attr(all(), must_use, deprecated,,)]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: wrong `cfg_attr` delimiters
-  --> $DIR/cfg-attr-parse.rs:44:11
+  --> $DIR/cfg-attr-parse.rs:49:11
+   |
+LL | #[cfg_attr[all(),,]]
+   |           ^^^^^^^^^
+   |
+help: the delimiters should be `(` and `)`
+   |
+LL | #[cfg_attr(all(),,)]
+   |           ^       ^
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:49:18
+   |
+LL | #[cfg_attr[all(),,]]
+   |                  ^ expected identifier
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: wrong `cfg_attr` delimiters
+  --> $DIR/cfg-attr-parse.rs:49:11
    |
 LL | #[cfg_attr[all(),,]]
    |           ^^^^^^^^^
@@ -54,7 +118,7 @@ LL | #[cfg_attr(all(),,)]
    |           ^       ^
 
 error: expected identifier, found `,`
-  --> $DIR/cfg-attr-parse.rs:44:18
+  --> $DIR/cfg-attr-parse.rs:49:18
    |
 LL | #[cfg_attr[all(),,]]
    |                  ^ expected identifier
@@ -63,7 +127,27 @@ LL | #[cfg_attr[all(),,]]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: wrong `cfg_attr` delimiters
-  --> $DIR/cfg-attr-parse.rs:50:11
+  --> $DIR/cfg-attr-parse.rs:57:11
+   |
+LL | #[cfg_attr{all(),,}]
+   |           ^^^^^^^^^
+   |
+help: the delimiters should be `(` and `)`
+   |
+LL | #[cfg_attr(all(),,)]
+   |           ^       ^
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:57:18
+   |
+LL | #[cfg_attr{all(),,}]
+   |                  ^ expected identifier
+   |
+   = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: wrong `cfg_attr` delimiters
+  --> $DIR/cfg-attr-parse.rs:57:11
    |
 LL | #[cfg_attr{all(),,}]
    |           ^^^^^^^^^
@@ -74,7 +158,7 @@ LL | #[cfg_attr(all(),,)]
    |           ^       ^
 
 error: expected identifier, found `,`
-  --> $DIR/cfg-attr-parse.rs:50:18
+  --> $DIR/cfg-attr-parse.rs:57:18
    |
 LL | #[cfg_attr{all(),,}]
    |                  ^ expected identifier
@@ -82,5 +166,5 @@ LL | #[cfg_attr{all(),,}]
    = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
-error: aborting due to 9 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/test/ui/derive-uninhabited-enum-38885.stderr b/src/test/ui/derive-uninhabited-enum-38885.stderr
index 72607629d3c10..1961245c984bb 100644
--- a/src/test/ui/derive-uninhabited-enum-38885.stderr
+++ b/src/test/ui/derive-uninhabited-enum-38885.stderr
@@ -2,7 +2,7 @@ warning: variant is never constructed: `Void`
   --> $DIR/derive-uninhabited-enum-38885.rs:13:5
    |
 LL |     Void(Void),
-   |     ^^^^^^^^^^
+   |     ^^^^^^^^^^^
    |
    = note: `-W dead-code` implied by `-W unused`
 
diff --git a/src/test/ui/discrim/discrim-overflow-2.stderr b/src/test/ui/discrim/discrim-overflow-2.stderr
index 198ebe9eb51f9..4f6adb72c95bf 100644
--- a/src/test/ui/discrim/discrim-overflow-2.stderr
+++ b/src/test/ui/discrim/discrim-overflow-2.stderr
@@ -2,7 +2,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:15:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 127
+   |         ^^^^^ overflowed on value after 127
    |
    = note: explicitly set `OhNo = -128` if that is desired outcome
 
@@ -10,7 +10,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:24:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 255
+   |         ^^^^^ overflowed on value after 255
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -18,7 +18,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:33:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 32767
+   |         ^^^^^ overflowed on value after 32767
    |
    = note: explicitly set `OhNo = -32768` if that is desired outcome
 
@@ -26,7 +26,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:42:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 65535
+   |         ^^^^^ overflowed on value after 65535
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -34,7 +34,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:51:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 2147483647
+   |         ^^^^^ overflowed on value after 2147483647
    |
    = note: explicitly set `OhNo = -2147483648` if that is desired outcome
 
@@ -42,7 +42,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:60:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 4294967295
+   |         ^^^^^ overflowed on value after 4294967295
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -50,7 +50,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:69:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 9223372036854775807
+   |         ^^^^^ overflowed on value after 9223372036854775807
    |
    = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome
 
@@ -58,7 +58,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow-2.rs:78:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 18446744073709551615
+   |         ^^^^^ overflowed on value after 18446744073709551615
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
diff --git a/src/test/ui/discrim/discrim-overflow.stderr b/src/test/ui/discrim/discrim-overflow.stderr
index a2ae4863f9f79..8dd362cfd2e74 100644
--- a/src/test/ui/discrim/discrim-overflow.stderr
+++ b/src/test/ui/discrim/discrim-overflow.stderr
@@ -2,7 +2,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:13:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 127
+   |         ^^^^^ overflowed on value after 127
    |
    = note: explicitly set `OhNo = -128` if that is desired outcome
 
@@ -10,7 +10,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:24:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 255
+   |         ^^^^^ overflowed on value after 255
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -18,7 +18,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:35:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 32767
+   |         ^^^^^ overflowed on value after 32767
    |
    = note: explicitly set `OhNo = -32768` if that is desired outcome
 
@@ -26,7 +26,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:46:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 65535
+   |         ^^^^^ overflowed on value after 65535
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -34,7 +34,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:58:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 2147483647
+   |         ^^^^^ overflowed on value after 2147483647
    |
    = note: explicitly set `OhNo = -2147483648` if that is desired outcome
 
@@ -42,7 +42,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:70:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 4294967295
+   |         ^^^^^ overflowed on value after 4294967295
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
@@ -50,7 +50,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:82:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 9223372036854775807
+   |         ^^^^^ overflowed on value after 9223372036854775807
    |
    = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome
 
@@ -58,7 +58,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/discrim-overflow.rs:94:9
    |
 LL |         OhNo,
-   |         ^^^^ overflowed on value after 18446744073709551615
+   |         ^^^^^ overflowed on value after 18446744073709551615
    |
    = note: explicitly set `OhNo = 0` if that is desired outcome
 
diff --git a/src/test/ui/empty/empty-struct-braces-pat-1.stderr b/src/test/ui/empty/empty-struct-braces-pat-1.stderr
index 3570012fc3752..c5c257e4f5a5a 100644
--- a/src/test/ui/empty/empty-struct-braces-pat-1.stderr
+++ b/src/test/ui/empty/empty-struct-braces-pat-1.stderr
@@ -18,7 +18,7 @@ LL |         XE::XEmpty3 => ()
 LL |     XEmpty3 {},
    |     ------- `XE::XEmpty3` defined here
 LL |     XEmpty4,
-   |     ------- similarly named unit variant `XEmpty4` defined here
+   |     -------- similarly named unit variant `XEmpty4` defined here
    |
 help: use struct pattern syntax instead
    |
diff --git a/src/test/ui/empty/empty-struct-braces-pat-3.stderr b/src/test/ui/empty/empty-struct-braces-pat-3.stderr
index aac872ba0ecca..5b7f1e66822b3 100644
--- a/src/test/ui/empty/empty-struct-braces-pat-3.stderr
+++ b/src/test/ui/empty/empty-struct-braces-pat-3.stderr
@@ -19,7 +19,7 @@ LL |     XEmpty3 {},
    |     ------- `XE::XEmpty3` defined here
 LL |     XEmpty4,
 LL |     XEmpty5(),
-   |     --------- similarly named tuple variant `XEmpty5` defined here
+   |     ---------- similarly named tuple variant `XEmpty5` defined here
    |
 help: use struct pattern syntax instead
    |
@@ -51,7 +51,7 @@ LL |     XEmpty3 {},
    |     ------- `XE::XEmpty3` defined here
 LL |     XEmpty4,
 LL |     XEmpty5(),
-   |     --------- similarly named tuple variant `XEmpty5` defined here
+   |     ---------- similarly named tuple variant `XEmpty5` defined here
    |
 help: use struct pattern syntax instead
    |
diff --git a/src/test/ui/empty/empty-struct-tuple-pat.stderr b/src/test/ui/empty/empty-struct-tuple-pat.stderr
index 7b0d9717e63a2..b5a57d85adda3 100644
--- a/src/test/ui/empty/empty-struct-tuple-pat.stderr
+++ b/src/test/ui/empty/empty-struct-tuple-pat.stderr
@@ -34,9 +34,9 @@ LL |         XE::XEmpty5 => (),
   ::: $DIR/auxiliary/empty-struct.rs:7:5
    |
 LL |     XEmpty4,
-   |     ------- similarly named unit variant `XEmpty4` defined here
+   |     -------- similarly named unit variant `XEmpty4` defined here
 LL |     XEmpty5(),
-   |     --------- `XE::XEmpty5` defined here
+   |     ---------- `XE::XEmpty5` defined here
    |
 help: use the tuple variant pattern syntax instead
    |
diff --git a/src/test/ui/empty/empty-struct-unit-pat.stderr b/src/test/ui/empty/empty-struct-unit-pat.stderr
index 8ee14a3d01d89..ef2d50c6ced07 100644
--- a/src/test/ui/empty/empty-struct-unit-pat.stderr
+++ b/src/test/ui/empty/empty-struct-unit-pat.stderr
@@ -59,7 +59,7 @@ LL |         XE::XEmpty4() => (),
   ::: $DIR/auxiliary/empty-struct.rs:8:5
    |
 LL |     XEmpty5(),
-   |     --------- similarly named tuple variant `XEmpty5` defined here
+   |     ---------- similarly named tuple variant `XEmpty5` defined here
 
 error[E0532]: expected tuple struct or tuple variant, found unit variant `E::Empty4`
   --> $DIR/empty-struct-unit-pat.rs:46:9
@@ -78,7 +78,7 @@ LL |         XE::XEmpty4(..) => (),
   ::: $DIR/auxiliary/empty-struct.rs:8:5
    |
 LL |     XEmpty5(),
-   |     --------- similarly named tuple variant `XEmpty5` defined here
+   |     ---------- similarly named tuple variant `XEmpty5` defined here
 
 error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr
index b5f61e6e991d8..59045bf0957d1 100644
--- a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr
+++ b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr
@@ -23,10 +23,10 @@ LL |   Unit = 1,
    |          ^ disallowed custom discriminant
 LL |
 LL |   Tuple() = 2,
-   |   ----------- tuple variant defined here
+   |   ------------ tuple variant defined here
 LL |
 LL |   Struct{} = 3,
-   |   ------------ struct variant defined here
+   |   ------------- struct variant defined here
    |
    = note: see issue #60553 <https://github.com/rust-lang/rust/issues/60553> for more information
    = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable
diff --git a/src/test/ui/enum/enum-size-variance.stderr b/src/test/ui/enum/enum-size-variance.stderr
index 6012033dc62d4..ff4820f819874 100644
--- a/src/test/ui/enum/enum-size-variance.stderr
+++ b/src/test/ui/enum/enum-size-variance.stderr
@@ -2,7 +2,7 @@ warning: enum variant is more than three times larger (32 bytes) than the next l
   --> $DIR/enum-size-variance.rs:18:5
    |
 LL |     L(i64, i64, i64, i64),
-   |     ^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/enum-size-variance.rs:3:9
diff --git a/src/test/ui/error-codes/E0023.stderr b/src/test/ui/error-codes/E0023.stderr
index a3610099294da..5b648d318d0a1 100644
--- a/src/test/ui/error-codes/E0023.stderr
+++ b/src/test/ui/error-codes/E0023.stderr
@@ -2,7 +2,7 @@ error[E0023]: this pattern has 1 field, but the corresponding tuple variant has
   --> $DIR/E0023.rs:11:9
    |
 LL |     Apple(String, String),
-   |     --------------------- tuple variant defined here
+   |     ---------------------- tuple variant defined here
 ...
 LL |         Fruit::Apple(a) => {},
    |         ^^^^^^^^^^^^^^^ expected 2 fields, found 1
@@ -11,7 +11,7 @@ error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has
   --> $DIR/E0023.rs:12:9
    |
 LL |     Apple(String, String),
-   |     --------------------- tuple variant defined here
+   |     ---------------------- tuple variant defined here
 ...
 LL |         Fruit::Apple(a, b, c) => {},
    |         ^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
@@ -20,7 +20,7 @@ error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has
   --> $DIR/E0023.rs:13:9
    |
 LL |     Pear(u32),
-   |     --------- tuple variant defined here
+   |     ---------- tuple variant defined here
 ...
 LL |         Fruit::Pear(1, 2) => {},
    |         ^^^^^^^^^^^^^^^^^ expected 1 field, found 2
@@ -29,7 +29,7 @@ error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has
   --> $DIR/E0023.rs:14:9
    |
 LL |     Orange((String, String)),
-   |     ------------------------ tuple variant defined here
+   |     ------------------------- tuple variant defined here
 ...
 LL |         Fruit::Orange(a, b) => {},
    |         ^^^^^^^^^^^^^^^^^^^ expected 1 field, found 2
@@ -43,7 +43,7 @@ error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has
   --> $DIR/E0023.rs:15:9
    |
 LL |     Banana(()),
-   |     ---------- tuple variant defined here
+   |     ----------- tuple variant defined here
 ...
 LL |         Fruit::Banana() => {},
    |         ^^^^^^^^^^^^^^^ expected 1 field, found 0
diff --git a/src/test/ui/error-codes/E0370.stderr b/src/test/ui/error-codes/E0370.stderr
index 7fb622ee80bbf..21e8eb88955f4 100644
--- a/src/test/ui/error-codes/E0370.stderr
+++ b/src/test/ui/error-codes/E0370.stderr
@@ -2,7 +2,7 @@ error[E0370]: enum discriminant overflowed
   --> $DIR/E0370.rs:7:5
    |
 LL |     Y,
-   |     ^ overflowed on value after 9223372036854775807
+   |     ^^ overflowed on value after 9223372036854775807
    |
    = note: explicitly set `Y = -9223372036854775808` if that is desired outcome
 
diff --git a/src/test/ui/error-codes/E0618.stderr b/src/test/ui/error-codes/E0618.stderr
index 6ddda3bf8b528..70ba3a8791344 100644
--- a/src/test/ui/error-codes/E0618.stderr
+++ b/src/test/ui/error-codes/E0618.stderr
@@ -2,7 +2,7 @@ error[E0618]: expected function, found enum variant `X::Entry`
   --> $DIR/E0618.rs:6:5
    |
 LL |     Entry,
-   |     ----- `X::Entry` defined here
+   |     ------ `X::Entry` defined here
 ...
 LL |     X::Entry();
    |     ^^^^^^^^--
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.rs b/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.rs
index d44f78d4fab27..4a6f506798ab3 100644
--- a/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.rs
+++ b/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.rs
@@ -8,6 +8,7 @@ extern crate cfg_target_thread_local;
 extern {
     #[cfg_attr(target_thread_local, thread_local)]
     //~^ `cfg(target_thread_local)` is experimental and subject to change
+    //~| `cfg(target_thread_local)` is experimental and subject to change
 
     static FOO: u32;
 }
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.stderr b/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.stderr
index af59c71414773..7ef9fda92cc1d 100644
--- a/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.stderr
+++ b/src/test/ui/feature-gates/feature-gate-cfg-target-thread-local.stderr
@@ -7,6 +7,15 @@ LL |     #[cfg_attr(target_thread_local, thread_local)]
    = note: see issue #29594 <https://github.com/rust-lang/rust/issues/29594> for more information
    = help: add `#![feature(cfg_target_thread_local)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error[E0658]: `cfg(target_thread_local)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg-target-thread-local.rs:9:16
+   |
+LL |     #[cfg_attr(target_thread_local, thread_local)]
+   |                ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #29594 <https://github.com/rust-lang/rust/issues/29594> for more information
+   = help: add `#![feature(cfg_target_thread_local)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/issues/issue-15524.stderr b/src/test/ui/issues/issue-15524.stderr
index 1702dad34ca0c..58142ba66e6fc 100644
--- a/src/test/ui/issues/issue-15524.stderr
+++ b/src/test/ui/issues/issue-15524.stderr
@@ -13,7 +13,7 @@ LL |     A = 1,
    |         - first use of `1`
 ...
 LL |     D,
-   |     ^ enum already has `1`
+   |     ^^ enum already has `1`
 
 error[E0081]: discriminant value `1` already exists
   --> $DIR/issue-15524.rs:11:9
diff --git a/src/test/ui/issues/issue-32004.stderr b/src/test/ui/issues/issue-32004.stderr
index f95afb9c1fdc7..45af4a5237734 100644
--- a/src/test/ui/issues/issue-32004.stderr
+++ b/src/test/ui/issues/issue-32004.stderr
@@ -2,7 +2,7 @@ error[E0532]: expected unit struct, unit variant or constant, found tuple varian
   --> $DIR/issue-32004.rs:10:9
    |
 LL |     Bar(i32),
-   |     -------- `Foo::Bar` defined here
+   |     --------- `Foo::Bar` defined here
 LL |     Baz
    |     --- similarly named unit variant `Baz` defined here
 ...
diff --git a/src/test/ui/issues/issue-47706.stderr b/src/test/ui/issues/issue-47706.stderr
index c84d8ecb4c9e6..634943ac22f86 100644
--- a/src/test/ui/issues/issue-47706.stderr
+++ b/src/test/ui/issues/issue-47706.stderr
@@ -11,7 +11,7 @@ error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
   --> $DIR/issue-47706.rs:27:9
    |
 LL |     Bar(i32),
-   |     -------- takes 1 argument
+   |     --------- takes 1 argument
 ...
 LL | fn foo<F>(f: F)
    |    --- required by a bound in this
diff --git a/src/test/ui/issues/issue-5100.stderr b/src/test/ui/issues/issue-5100.stderr
index de71e1d1a6666..bfdbdf2d2cffc 100644
--- a/src/test/ui/issues/issue-5100.stderr
+++ b/src/test/ui/issues/issue-5100.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-5100.rs:8:9
    |
 LL | enum A { B, C }
-   |          - unit variant defined here
+   |          -- unit variant defined here
 ...
 LL |     match (true, false) {
    |           ------------- this expression has type `(bool, bool)`
diff --git a/src/test/ui/issues/issue-63983.stderr b/src/test/ui/issues/issue-63983.stderr
index eb0428341020f..8c0a9f6ea5d88 100644
--- a/src/test/ui/issues/issue-63983.stderr
+++ b/src/test/ui/issues/issue-63983.stderr
@@ -2,7 +2,7 @@ error[E0532]: expected unit struct, unit variant or constant, found tuple varian
   --> $DIR/issue-63983.rs:8:9
    |
 LL |     Tuple(i32),
-   |     ---------- `MyEnum::Tuple` defined here
+   |     ----------- `MyEnum::Tuple` defined here
 ...
 LL |         MyEnum::Tuple => "",
    |         ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `MyEnum::Tuple(_)`
@@ -11,7 +11,7 @@ error[E0532]: expected unit struct, unit variant or constant, found struct varia
   --> $DIR/issue-63983.rs:10:9
    |
 LL |     Struct{ s: i32 },
-   |     ---------------- `MyEnum::Struct` defined here
+   |     ----------------- `MyEnum::Struct` defined here
 ...
 LL |         MyEnum::Struct => "",
    |         ^^^^^^^^^^^^^^ help: use struct pattern syntax instead: `MyEnum::Struct { s }`
diff --git a/src/test/ui/issues/issue-7867.stderr b/src/test/ui/issues/issue-7867.stderr
index 0d3121d60455d..e3db4feb194dd 100644
--- a/src/test/ui/issues/issue-7867.stderr
+++ b/src/test/ui/issues/issue-7867.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-7867.rs:7:9
    |
 LL | enum A { B, C }
-   |          - unit variant defined here
+   |          -- unit variant defined here
 ...
 LL |     match (true, false) {
    |           ------------- this expression has type `(bool, bool)`
diff --git a/src/test/ui/lint/dead-code/lint-dead-code-4.stderr b/src/test/ui/lint/dead-code/lint-dead-code-4.stderr
index 3905d1a06bdfe..44413090c3236 100644
--- a/src/test/ui/lint/dead-code/lint-dead-code-4.stderr
+++ b/src/test/ui/lint/dead-code/lint-dead-code-4.stderr
@@ -14,7 +14,7 @@ error: variant is never constructed: `X`
   --> $DIR/lint-dead-code-4.rs:15:5
    |
 LL |     X,
-   |     ^
+   |     ^^
 
 error: variant is never constructed: `Y`
   --> $DIR/lint-dead-code-4.rs:16:5
@@ -24,7 +24,7 @@ LL | |         a: String,
 LL | |         b: i32,
 LL | |         c: i32,
 LL | |     },
-   | |_____^
+   | |______^
 
 error: enum is never used: `ABC`
   --> $DIR/lint-dead-code-4.rs:24:6
@@ -36,7 +36,7 @@ error: variant is never constructed: `I`
   --> $DIR/lint-dead-code-4.rs:36:5
    |
 LL |     I,
-   |     ^
+   |     ^^
 
 error: field is never read: `b`
   --> $DIR/lint-dead-code-4.rs:39:9
diff --git a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr
index 519add826273f..6b79f6c67baa0 100644
--- a/src/test/ui/lint/dead-code/lint-dead-code-5.stderr
+++ b/src/test/ui/lint/dead-code/lint-dead-code-5.stderr
@@ -14,13 +14,13 @@ error: variant is never constructed: `Variant5`
   --> $DIR/lint-dead-code-5.rs:13:5
    |
 LL |     Variant5 { _x: isize },
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: variant is never constructed: `Variant6`
   --> $DIR/lint-dead-code-5.rs:14:5
    |
 LL |     Variant6(isize),
-   |     ^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^
 
 error: enum is never used: `Enum3`
   --> $DIR/lint-dead-code-5.rs:35:6
diff --git a/src/test/ui/lint/dead-code/unused-struct-variant.stderr b/src/test/ui/lint/dead-code/unused-struct-variant.stderr
index b93d6d4ac1866..bf0c39b7fb5e1 100644
--- a/src/test/ui/lint/dead-code/unused-struct-variant.stderr
+++ b/src/test/ui/lint/dead-code/unused-struct-variant.stderr
@@ -2,7 +2,7 @@ error: variant is never constructed: `Bar`
   --> $DIR/unused-struct-variant.rs:8:5
    |
 LL |     Bar(B),
-   |     ^^^^^^
+   |     ^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/unused-struct-variant.rs:1:9
diff --git a/src/test/ui/lint/dead-code/unused-variant.stderr b/src/test/ui/lint/dead-code/unused-variant.stderr
index a547f5af4b082..f85efc72c06ec 100644
--- a/src/test/ui/lint/dead-code/unused-variant.stderr
+++ b/src/test/ui/lint/dead-code/unused-variant.stderr
@@ -2,7 +2,7 @@ error: variant is never constructed: `Variant1`
   --> $DIR/unused-variant.rs:5:5
    |
 LL |     Variant1,
-   |     ^^^^^^^^
+   |     ^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/unused-variant.rs:1:9
diff --git a/src/test/ui/malformed/malformed-special-attrs.rs b/src/test/ui/malformed/malformed-special-attrs.rs
index 05b7ebe466662..ad6ee48ec047e 100644
--- a/src/test/ui/malformed/malformed-special-attrs.rs
+++ b/src/test/ui/malformed/malformed-special-attrs.rs
@@ -1,7 +1,9 @@
 #[cfg_attr] //~ ERROR malformed `cfg_attr` attribute
+//~| ERROR malformed `cfg_attr` attribute
 struct S1;
 
 #[cfg_attr = ""] //~ ERROR malformed `cfg_attr` attribute
+//~| ERROR malformed `cfg_attr` attribute
 struct S2;
 
 #[derive] //~ ERROR malformed `derive` attribute
diff --git a/src/test/ui/malformed/malformed-special-attrs.stderr b/src/test/ui/malformed/malformed-special-attrs.stderr
index 6f535e03e6aec..06ae9a1a99975 100644
--- a/src/test/ui/malformed/malformed-special-attrs.stderr
+++ b/src/test/ui/malformed/malformed-special-attrs.stderr
@@ -7,7 +7,23 @@ LL | #[cfg_attr]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: malformed `cfg_attr` attribute input
-  --> $DIR/malformed-special-attrs.rs:4:1
+  --> $DIR/malformed-special-attrs.rs:1:1
+   |
+LL | #[cfg_attr]
+   | ^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: malformed `cfg_attr` attribute input
+  --> $DIR/malformed-special-attrs.rs:5:1
+   |
+LL | #[cfg_attr = ""]
+   | ^^^^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+
+error: malformed `cfg_attr` attribute input
+  --> $DIR/malformed-special-attrs.rs:5:1
    |
 LL | #[cfg_attr = ""]
    | ^^^^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
@@ -15,16 +31,16 @@ LL | #[cfg_attr = ""]
    = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
 
 error: malformed `derive` attribute input
-  --> $DIR/malformed-special-attrs.rs:7:1
+  --> $DIR/malformed-special-attrs.rs:9:1
    |
 LL | #[derive]
    | ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
 
 error: malformed `derive` attribute input
-  --> $DIR/malformed-special-attrs.rs:10:1
+  --> $DIR/malformed-special-attrs.rs:12:1
    |
 LL | #[derive = ""]
    | ^^^^^^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/match/match-pattern-field-mismatch.stderr b/src/test/ui/match/match-pattern-field-mismatch.stderr
index c2298d6fbbf02..e9c44957de987 100644
--- a/src/test/ui/match/match-pattern-field-mismatch.stderr
+++ b/src/test/ui/match/match-pattern-field-mismatch.stderr
@@ -2,7 +2,7 @@ error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has
   --> $DIR/match-pattern-field-mismatch.rs:10:11
    |
 LL |         Rgb(usize, usize, usize),
-   |         ------------------------ tuple variant defined here
+   |         ------------------------- tuple variant defined here
 ...
 LL |           Color::Rgb(_, _) => { }
    |           ^^^^^^^^^^^^^^^^ expected 3 fields, found 2
diff --git a/src/test/ui/namespace/namespace-mix.stderr b/src/test/ui/namespace/namespace-mix.stderr
index ded3173f45b2e..b03f32eab0599 100644
--- a/src/test/ui/namespace/namespace-mix.stderr
+++ b/src/test/ui/namespace/namespace-mix.stderr
@@ -46,9 +46,9 @@ error[E0423]: expected value, found struct variant `m7::V`
   --> $DIR/namespace-mix.rs:100:11
    |
 LL |         V {},
-   |         ---- `m7::V` defined here
+   |         ----- `m7::V` defined here
 LL |         TV(),
-   |         ---- similarly named tuple variant `TV` defined here
+   |         ----- similarly named tuple variant `TV` defined here
 ...
 LL |     check(m7::V);
    |           ^^^^^
@@ -79,7 +79,7 @@ LL |     check(xm7::V);
 LL |         V {},
    |         - `xm7::V` defined here
 LL |         TV(),
-   |         ---- similarly named tuple variant `TV` defined here
+   |         ----- similarly named tuple variant `TV` defined here
    |
 help: use struct literal syntax instead
    |
diff --git a/src/test/ui/parser/issue-62895.rs b/src/test/ui/parser/issue-62895.rs
index 53f17405d79f4..8edcee2e41792 100644
--- a/src/test/ui/parser/issue-62895.rs
+++ b/src/test/ui/parser/issue-62895.rs
@@ -5,7 +5,9 @@ mod _ { //~ ERROR expected identifier
 pub fn g() -> isizee { //~ ERROR cannot find type `isizee` in this scope
 mod _ { //~ ERROR expected identifier
 pub    g() -> is //~ ERROR missing `fn` for function definition
-(), w20);
+(), w20); //~ ERROR mismatched
 }
 (), w20); //~ ERROR expected item, found `;`
+//~^ ERROR mismatched
+
 }
diff --git a/src/test/ui/parser/issue-62895.stderr b/src/test/ui/parser/issue-62895.stderr
index ed4d2340fc5bb..aa796a9fd7f79 100644
--- a/src/test/ui/parser/issue-62895.stderr
+++ b/src/test/ui/parser/issue-62895.stderr
@@ -27,6 +27,24 @@ error: expected item, found `;`
 LL | (), w20);
    |         ^ help: remove this semicolon
 
+error: mismatched closing delimiter: `)`
+  --> $DIR/issue-62895.rs:8:8
+   |
+LL | mod _ {
+   |       - unclosed delimiter
+LL | pub    g() -> is
+LL | (), w20);
+   |        ^ mismatched closing delimiter
+
+error: mismatched closing delimiter: `)`
+  --> $DIR/issue-62895.rs:10:8
+   |
+LL | mod _ {
+   |       - unclosed delimiter
+...
+LL | (), w20);
+   |        ^ mismatched closing delimiter
+
 error[E0412]: cannot find type `isizee` in this scope
   --> $DIR/issue-62895.rs:5:15
    |
@@ -41,7 +59,7 @@ LL | fn v() -> isize {
    |    |
    |    implicitly returns `()` as its body has no tail or `return` expression
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
 Some errors have detailed explanations: E0308, E0412.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/issue-68890.rs b/src/test/ui/parser/issue-68890.rs
index bab4ed7f800c5..e85597b12a240 100644
--- a/src/test/ui/parser/issue-68890.rs
+++ b/src/test/ui/parser/issue-68890.rs
@@ -1,4 +1,9 @@
-enum e{A((?'a a+?+l))}
+enum E{A((?'a a+?+l))}
 //~^ ERROR `?` may only modify trait bounds, not lifetime bounds
 //~| ERROR expected one of `)`, `+`, or `,`
-//~| ERROR expected item, found `)`
+//~| ERROR expected identifier, found `)`
+//~| ERROR cannot find trait
+//~| ERROR use of undeclared lifetime
+//~| WARN trait objects without
+
+fn main() {}
diff --git a/src/test/ui/parser/issue-68890.stderr b/src/test/ui/parser/issue-68890.stderr
index 2a3bf6b41f02e..18f61f8fa1c43 100644
--- a/src/test/ui/parser/issue-68890.stderr
+++ b/src/test/ui/parser/issue-68890.stderr
@@ -1,20 +1,46 @@
 error: `?` may only modify trait bounds, not lifetime bounds
   --> $DIR/issue-68890.rs:1:11
    |
-LL | enum e{A((?'a a+?+l))}
+LL | enum E{A((?'a a+?+l))}
    |           ^
 
 error: expected one of `)`, `+`, or `,`, found `a`
   --> $DIR/issue-68890.rs:1:15
    |
-LL | enum e{A((?'a a+?+l))}
+LL | enum E{A((?'a a+?+l))}
    |               ^ expected one of `)`, `+`, or `,`
 
-error: expected item, found `)`
+error: expected identifier, found `)`
   --> $DIR/issue-68890.rs:1:21
    |
-LL | enum e{A((?'a a+?+l))}
-   |                     ^ expected item
+LL | enum E{A((?'a a+?+l))}
+   |                     ^ expected identifier
 
-error: aborting due to 3 previous errors
+error[E0405]: cannot find trait `l` in this scope
+  --> $DIR/issue-68890.rs:1:19
+   |
+LL | enum E{A((?'a a+?+l))}
+   |                   ^ not found in this scope
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/issue-68890.rs:1:10
+   |
+LL | enum E{A((?'a a+?+l))}
+   |          ^^^^^^^^^^ help: use `dyn`: `dyn (?'a a+?+l`
+   |
+   = note: `#[warn(bare_trait_objects)]` on by default
+
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/issue-68890.rs:1:12
+   |
+LL | enum E{A((?'a a+?+l))}
+   |       -    ^^ undeclared lifetime
+   |       |
+   |       help: consider introducing lifetime `'a` here: `<'a>`
+   |
+   = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes
+
+error: aborting due to 5 previous errors; 1 warning emitted
 
+Some errors have detailed explanations: E0261, E0405.
+For more information about an error, try `rustc --explain E0261`.
diff --git a/src/test/ui/parser/recover-enum.rs b/src/test/ui/parser/recover-enum.rs
index 08dd939e2c023..44f870048b25f 100644
--- a/src/test/ui/parser/recover-enum.rs
+++ b/src/test/ui/parser/recover-enum.rs
@@ -1,11 +1,11 @@
 fn main() {
     enum Test {
-        Very //~ HELP missing `,`
-        Bad(usize) //~ HELP missing `,`
-        //~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `Bad`
-        Stuff { a: usize } //~ HELP missing `,`
-        //~^ ERROR expected one of `,`, `=`, or `}`, found `Stuff`
+        Very //~ HELP try adding a comma
+        Bad(usize) //~ HELP try adding a comma
+        //~^ ERROR expected one of `(`, `,`, `=`, or `{`, found `Bad`
+        Stuff { a: usize } //~ HELP try adding a comma
+        //~^ ERROR expected one of `,` or `=`, found `Stuff`
         Here
-        //~^ ERROR expected one of `,`, `=`, or `}`, found `Here`
+        //~^ ERROR expected one of `,` or `=`, found `Here`
     }
 }
diff --git a/src/test/ui/parser/recover-enum.stderr b/src/test/ui/parser/recover-enum.stderr
index a2b650e4f4eb7..33318e656eb71 100644
--- a/src/test/ui/parser/recover-enum.stderr
+++ b/src/test/ui/parser/recover-enum.stderr
@@ -1,34 +1,34 @@
-error: expected one of `(`, `,`, `=`, `{`, or `}`, found `Bad`
+error: expected one of `(`, `,`, `=`, or `{`, found `Bad`
   --> $DIR/recover-enum.rs:4:9
    |
 LL |         Very
    |             -
    |             |
-   |             expected one of `(`, `,`, `=`, `{`, or `}`
-   |             help: missing `,`
+   |             expected one of `(`, `,`, `=`, or `{`
+   |             help: try adding a comma: `,`
 LL |         Bad(usize)
    |         ^^^ unexpected token
 
-error: expected one of `,`, `=`, or `}`, found `Stuff`
+error: expected one of `,` or `=`, found `Stuff`
   --> $DIR/recover-enum.rs:6:9
    |
 LL |         Bad(usize)
    |                   -
    |                   |
-   |                   expected one of `,`, `=`, or `}`
-   |                   help: missing `,`
+   |                   expected one of `,` or `=`
+   |                   help: try adding a comma: `,`
 LL |
 LL |         Stuff { a: usize }
    |         ^^^^^ unexpected token
 
-error: expected one of `,`, `=`, or `}`, found `Here`
+error: expected one of `,` or `=`, found `Here`
   --> $DIR/recover-enum.rs:8:9
    |
 LL |         Stuff { a: usize }
    |                           -
    |                           |
-   |                           expected one of `,`, `=`, or `}`
-   |                           help: missing `,`
+   |                           expected one of `,` or `=`
+   |                           help: try adding a comma: `,`
 LL |
 LL |         Here
    |         ^^^^ unexpected token
diff --git a/src/test/ui/parser/recover-from-bad-variant.stderr b/src/test/ui/parser/recover-from-bad-variant.stderr
index 89232a519d7b2..9e4b93018ed02 100644
--- a/src/test/ui/parser/recover-from-bad-variant.stderr
+++ b/src/test/ui/parser/recover-from-bad-variant.stderr
@@ -13,7 +13,7 @@ error[E0532]: expected tuple struct or tuple variant, found struct variant `Enum
   --> $DIR/recover-from-bad-variant.rs:10:9
    |
 LL |     Foo { a: usize, b: usize },
-   |     -------------------------- `Enum::Foo` defined here
+   |     --------------------------- `Enum::Foo` defined here
 ...
 LL |         Enum::Foo(a, b) => {}
    |         ^^^^^^^^^^^^^^^ help: use struct pattern syntax instead: `Enum::Foo { a, b }`
diff --git a/src/test/ui/parser/removed-syntax-enum-newtype.rs b/src/test/ui/parser/removed-syntax-enum-newtype.rs
index 518f90b2b8368..669644e74d6f2 100644
--- a/src/test/ui/parser/removed-syntax-enum-newtype.rs
+++ b/src/test/ui/parser/removed-syntax-enum-newtype.rs
@@ -1 +1 @@
-enum e = isize; //~ ERROR expected one of `<`, `where`, or `{`, found `=`
+enum e = isize; //~ ERROR expected `where`, or `{` after enum name, found `=`
diff --git a/src/test/ui/parser/removed-syntax-enum-newtype.stderr b/src/test/ui/parser/removed-syntax-enum-newtype.stderr
index 2daa6249b4ce0..a16ec84a0cdc7 100644
--- a/src/test/ui/parser/removed-syntax-enum-newtype.stderr
+++ b/src/test/ui/parser/removed-syntax-enum-newtype.stderr
@@ -1,8 +1,8 @@
-error: expected one of `<`, `where`, or `{`, found `=`
+error: expected `where`, or `{` after enum name, found `=`
   --> $DIR/removed-syntax-enum-newtype.rs:1:8
    |
 LL | enum e = isize;
-   |        ^ expected one of `<`, `where`, or `{`
+   |        ^ expected `where`, or `{` after enum name
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr b/src/test/ui/parser/tag-variant-disr-non-nullary.stderr
index 79f044a0675b7..a96c0dd962275 100644
--- a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr
+++ b/src/test/ui/parser/tag-variant-disr-non-nullary.stderr
@@ -13,9 +13,9 @@ LL |     Black = 0x000000,
 LL |     White = 0xffffff,
    |             ^^^^^^^^ disallowed custom discriminant
 LL |     Other(usize),
-   |     ------------ tuple variant defined here
+   |     ------------- tuple variant defined here
 LL |     Other2(usize, usize),
-   |     -------------------- tuple variant defined here
+   |     --------------------- tuple variant defined here
    |
    = note: see issue #60553 <https://github.com/rust-lang/rust/issues/60553> for more information
    = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable
diff --git a/src/test/ui/pattern/issue-74539.stderr b/src/test/ui/pattern/issue-74539.stderr
index cbc90b5397d8b..d60693375d545 100644
--- a/src/test/ui/pattern/issue-74539.stderr
+++ b/src/test/ui/pattern/issue-74539.stderr
@@ -22,7 +22,7 @@ error[E0023]: this pattern has 1 field, but the corresponding tuple variant has
   --> $DIR/issue-74539.rs:8:9
    |
 LL |     A(u8, u8),
-   |     --------- tuple variant defined here
+   |     ---------- tuple variant defined here
 ...
 LL |         E::A(x @ ..) => {
    |         ^^^^^^^^^^^^ expected 2 fields, found 1
diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr
index 497c93b29497c..3b1b73b017281 100644
--- a/src/test/ui/pattern/pattern-error-continue.stderr
+++ b/src/test/ui/pattern/pattern-error-continue.stderr
@@ -8,7 +8,7 @@ error[E0532]: expected tuple struct or tuple variant, found unit variant `A::D`
   --> $DIR/pattern-error-continue.rs:18:9
    |
 LL |     B(isize, isize),
-   |     --------------- similarly named tuple variant `B` defined here
+   |     ---------------- similarly named tuple variant `B` defined here
 ...
 LL |         A::D(_) => (),
    |         ^^^-
@@ -19,7 +19,7 @@ error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has
   --> $DIR/pattern-error-continue.rs:17:9
    |
 LL |     B(isize, isize),
-   |     --------------- tuple variant defined here
+   |     ---------------- tuple variant defined here
 ...
 LL |         A::B(_, _, _) => (),
    |         ^^^^^^^^^^^^^ expected 2 fields, found 3
diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.rs b/src/test/ui/proc-macro/issue-75930-derive-cfg.rs
index e0f248c67e8b3..cf4cb0c688b1b 100644
--- a/src/test/ui/proc-macro/issue-75930-derive-cfg.rs
+++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.rs
@@ -1,29 +1,64 @@
 // check-pass
-// compile-flags: -Z span-debug
+// compile-flags: -Z span-debug --error-format human
 // aux-build:test-macros.rs
 
 // Regression test for issue #75930
 // Tests that we cfg-strip all targets before invoking
 // a derive macro
+// We need '--error-format human' to stop compiletest from
+// trying to interpret proc-macro output as JSON messages
+// (a pretty-printed struct may cause a line to start with '{)
 
 #[macro_use]
 extern crate test_macros;
 
+#[print_helper(a)]
+#[cfg_attr(not(FALSE), allow(dead_code))]
+#[print_attr]
 #[derive(Print)]
+#[print_helper(b)]
 struct Foo<#[cfg(FALSE)] A, B> {
     #[cfg(FALSE)] first: String,
-    second: bool,
+    #[cfg_attr(FALSE, deny(warnings))] second: bool,
     third: [u8; {
         #[cfg(FALSE)] struct Bar;
         #[cfg(not(FALSE))] struct Inner;
         #[cfg(FALSE)] let a = 25;
         match true {
             #[cfg(FALSE)] true => {},
-            false => {},
+            #[cfg_attr(not(FALSE), allow(warnings))] false => {},
             _ => {}
         };
+
+        #[print_helper(should_be_removed)]
+        fn removed_fn() {
+            #![cfg(FALSE)]
+        }
+
+        #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() {
+            #![cfg(not(FALSE))]
+            let my_val = true;
+        }
+
+        enum TupleEnum {
+            Foo(
+                #[cfg(FALSE)] u8,
+                #[cfg(FALSE)] bool,
+                #[cfg(not(FALSE))] i32,
+                #[cfg(FALSE)] String, u8
+            )
+        }
+
+        struct TupleStruct(
+            #[cfg(FALSE)] String,
+            #[cfg(not(FALSE))] i32,
+            #[cfg(FALSE)] bool,
+            u8
+        );
+
         0
     }],
+    #[print_helper(d)]
     fourth: B
 }
 
diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
index 0371133a3f705..2fe821612c907 100644
--- a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
+++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
@@ -1,76 +1,1351 @@
-PRINT-DERIVE INPUT (DISPLAY): struct Foo < B >
+PRINT-ATTR INPUT (DISPLAY): #[allow(dead_code)] #[derive(Print)] #[print_helper(b)] #[print_helper(a)]
+struct Foo < #[cfg(FALSE)] A, B >
+{
+    #[cfg(FALSE)] first : String, #[cfg_attr(FALSE, deny(warnings))] second :
+    bool, third :
+    [u8 ;
+     {
+         #[cfg(FALSE)] struct Bar ; #[cfg(not(FALSE))] struct Inner ;
+         #[cfg(FALSE)] let a = 25 ; match true
+         {
+             #[cfg(FALSE)] true => { },
+             #[cfg_attr(not(FALSE), allow(warnings))] false => { }, _ => { }
+         } ; #[print_helper(should_be_removed)] fn removed_fn()
+         { # ! [cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn
+         kept_fn() { # ! [cfg(not(FALSE))] let my_val = true ; } enum
+         TupleEnum
+         {
+             Foo(#[cfg(FALSE)] u8, #[cfg(FALSE)] bool, #[cfg(not(FALSE))] i32,
+                 #[cfg(FALSE)] String, u8)
+         } struct
+         TupleStruct(#[cfg(FALSE)] String, #[cfg(not(FALSE))] i32,
+                     #[cfg(FALSE)] bool, u8) ; 0
+     }], #[print_helper(d)] fourth : B
+}
+PRINT-ATTR INPUT (DEBUG): TokenStream [
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:40 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "allow",
+                span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:29 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "dead_code",
+                        span: $DIR/issue-75930-derive-cfg.rs:16:30: 16:39 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:16:29: 16:40 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:40 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:18:1: 18:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "derive",
+                span: $DIR/issue-75930-derive-cfg.rs:18:3: 18:9 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "Print",
+                        span: $DIR/issue-75930-derive-cfg.rs:18:10: 18:15 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:18:9: 18:16 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:18:2: 18:17 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:19:1: 19:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "print_helper",
+                span: $DIR/issue-75930-derive-cfg.rs:19:3: 19:15 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "b",
+                        span: $DIR/issue-75930-derive-cfg.rs:19:16: 19:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:19:15: 19:18 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:19:2: 19:19 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:15:1: 15:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "print_helper",
+                span: $DIR/issue-75930-derive-cfg.rs:15:3: 15:15 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "a",
+                        span: $DIR/issue-75930-derive-cfg.rs:15:16: 15:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:15:15: 15:18 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:15:2: 15:19 (#0),
+    },
+    Ident {
+        ident: "struct",
+        span: $DIR/issue-75930-derive-cfg.rs:20:1: 20:7 (#0),
+    },
+    Ident {
+        ident: "Foo",
+        span: $DIR/issue-75930-derive-cfg.rs:20:8: 20:11 (#0),
+    },
+    Punct {
+        ch: '<',
+        spacing: Joint,
+        span: $DIR/issue-75930-derive-cfg.rs:20:11: 20:12 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:20:12: 20:13 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "cfg",
+                span: $DIR/issue-75930-derive-cfg.rs:20:14: 20:17 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "FALSE",
+                        span: $DIR/issue-75930-derive-cfg.rs:20:18: 20:23 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:20:17: 20:24 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:20:13: 20:25 (#0),
+    },
+    Ident {
+        ident: "A",
+        span: $DIR/issue-75930-derive-cfg.rs:20:26: 20:27 (#0),
+    },
+    Punct {
+        ch: ',',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:20:27: 20:28 (#0),
+    },
+    Ident {
+        ident: "B",
+        span: $DIR/issue-75930-derive-cfg.rs:20:29: 20:30 (#0),
+    },
+    Punct {
+        ch: '>',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:20:30: 20:31 (#0),
+    },
+    Group {
+        delimiter: Brace,
+        stream: TokenStream [
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:21:5: 21:6 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "cfg",
+                        span: $DIR/issue-75930-derive-cfg.rs:21:7: 21:10 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "FALSE",
+                                span: $DIR/issue-75930-derive-cfg.rs:21:11: 21:16 (#0),
+                            },
+                        ],
+                        span: $DIR/issue-75930-derive-cfg.rs:21:10: 21:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:21:6: 21:18 (#0),
+            },
+            Ident {
+                ident: "first",
+                span: $DIR/issue-75930-derive-cfg.rs:21:19: 21:24 (#0),
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:21:24: 21:25 (#0),
+            },
+            Ident {
+                ident: "String",
+                span: $DIR/issue-75930-derive-cfg.rs:21:26: 21:32 (#0),
+            },
+            Punct {
+                ch: ',',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:21:32: 21:33 (#0),
+            },
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:22:5: 22:6 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "cfg_attr",
+                        span: $DIR/issue-75930-derive-cfg.rs:22:7: 22:15 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "FALSE",
+                                span: $DIR/issue-75930-derive-cfg.rs:22:16: 22:21 (#0),
+                            },
+                            Punct {
+                                ch: ',',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:22:21: 22:22 (#0),
+                            },
+                            Ident {
+                                ident: "deny",
+                                span: $DIR/issue-75930-derive-cfg.rs:22:23: 22:27 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "warnings",
+                                        span: $DIR/issue-75930-derive-cfg.rs:22:28: 22:36 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:22:27: 22:37 (#0),
+                            },
+                        ],
+                        span: $DIR/issue-75930-derive-cfg.rs:22:15: 22:38 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:22:6: 22:39 (#0),
+            },
+            Ident {
+                ident: "second",
+                span: $DIR/issue-75930-derive-cfg.rs:22:40: 22:46 (#0),
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:22:46: 22:47 (#0),
+            },
+            Ident {
+                ident: "bool",
+                span: $DIR/issue-75930-derive-cfg.rs:22:48: 22:52 (#0),
+            },
+            Punct {
+                ch: ',',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:22:52: 22:53 (#0),
+            },
+            Ident {
+                ident: "third",
+                span: $DIR/issue-75930-derive-cfg.rs:23:5: 23:10 (#0),
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:23:10: 23:11 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "u8",
+                        span: $DIR/issue-75930-derive-cfg.rs:23:13: 23:15 (#0),
+                    },
+                    Punct {
+                        ch: ';',
+                        spacing: Alone,
+                        span: $DIR/issue-75930-derive-cfg.rs:23:15: 23:16 (#0),
+                    },
+                    Group {
+                        delimiter: Brace,
+                        stream: TokenStream [
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:24:9: 24:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "cfg",
+                                        span: $DIR/issue-75930-derive-cfg.rs:24:11: 24:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "FALSE",
+                                                span: $DIR/issue-75930-derive-cfg.rs:24:15: 24:20 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:24:14: 24:21 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:24:10: 24:22 (#0),
+                            },
+                            Ident {
+                                ident: "struct",
+                                span: $DIR/issue-75930-derive-cfg.rs:24:23: 24:29 (#0),
+                            },
+                            Ident {
+                                ident: "Bar",
+                                span: $DIR/issue-75930-derive-cfg.rs:24:30: 24:33 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:24:33: 24:34 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:25:9: 25:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "cfg",
+                                        span: $DIR/issue-75930-derive-cfg.rs:25:11: 25:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "not",
+                                                span: $DIR/issue-75930-derive-cfg.rs:25:15: 25:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:25:19: 25:24 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:25:18: 25:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:25:14: 25:26 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:25:10: 25:27 (#0),
+                            },
+                            Ident {
+                                ident: "struct",
+                                span: $DIR/issue-75930-derive-cfg.rs:25:28: 25:34 (#0),
+                            },
+                            Ident {
+                                ident: "Inner",
+                                span: $DIR/issue-75930-derive-cfg.rs:25:35: 25:40 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:25:40: 25:41 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:26:9: 26:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "cfg",
+                                        span: $DIR/issue-75930-derive-cfg.rs:26:11: 26:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "FALSE",
+                                                span: $DIR/issue-75930-derive-cfg.rs:26:15: 26:20 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:26:14: 26:21 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:26:10: 26:22 (#0),
+                            },
+                            Ident {
+                                ident: "let",
+                                span: $DIR/issue-75930-derive-cfg.rs:26:23: 26:26 (#0),
+                            },
+                            Ident {
+                                ident: "a",
+                                span: $DIR/issue-75930-derive-cfg.rs:26:27: 26:28 (#0),
+                            },
+                            Punct {
+                                ch: '=',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:26:29: 26:30 (#0),
+                            },
+                            Literal {
+                                kind: Integer,
+                                symbol: "25",
+                                suffix: None,
+                                span: $DIR/issue-75930-derive-cfg.rs:26:31: 26:33 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:26:33: 26:34 (#0),
+                            },
+                            Ident {
+                                ident: "match",
+                                span: $DIR/issue-75930-derive-cfg.rs:27:9: 27:14 (#0),
+                            },
+                            Ident {
+                                ident: "true",
+                                span: $DIR/issue-75930-derive-cfg.rs:27:15: 27:19 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:13: 28:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:28:15: 28:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:28:19: 28:24 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:28:18: 28:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:14: 28:26 (#0),
+                                    },
+                                    Ident {
+                                        ident: "true",
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:27: 28:31 (#0),
+                                    },
+                                    Punct {
+                                        ch: '=',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:32: 28:34 (#0),
+                                    },
+                                    Punct {
+                                        ch: '>',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:32: 28:34 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Brace,
+                                        stream: TokenStream [],
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:35: 28:37 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:28:37: 28:38 (#0),
+                                    },
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:13: 29:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg_attr",
+                                                span: $DIR/issue-75930-derive-cfg.rs:29:15: 29:23 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "not",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:24: 29:27 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:29:28: 29:33 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:27: 29:34 (#0),
+                                                    },
+                                                    Punct {
+                                                        ch: ',',
+                                                        spacing: Alone,
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:34: 29:35 (#0),
+                                                    },
+                                                    Ident {
+                                                        ident: "allow",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:36: 29:41 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "warnings",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:29:42: 29:50 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:41: 29:51 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:29:23: 29:52 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:14: 29:53 (#0),
+                                    },
+                                    Ident {
+                                        ident: "false",
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:54: 29:59 (#0),
+                                    },
+                                    Punct {
+                                        ch: '=',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:60: 29:62 (#0),
+                                    },
+                                    Punct {
+                                        ch: '>',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:60: 29:62 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Brace,
+                                        stream: TokenStream [],
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:63: 29:65 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:65: 29:66 (#0),
+                                    },
+                                    Ident {
+                                        ident: "_",
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:13: 30:14 (#0),
+                                    },
+                                    Punct {
+                                        ch: '=',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:17 (#0),
+                                    },
+                                    Punct {
+                                        ch: '>',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:17 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Brace,
+                                        stream: TokenStream [],
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:18: 30:20 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:27:20: 31:10 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:31:10: 31:11 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:33:9: 33:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "print_helper",
+                                        span: $DIR/issue-75930-derive-cfg.rs:33:11: 33:23 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "should_be_removed",
+                                                span: $DIR/issue-75930-derive-cfg.rs:33:24: 33:41 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:33:23: 33:42 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:33:10: 33:43 (#0),
+                            },
+                            Ident {
+                                ident: "fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:34:9: 34:11 (#0),
+                            },
+                            Ident {
+                                ident: "removed_fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:34:12: 34:22 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [],
+                                span: $DIR/issue-75930-derive-cfg.rs:34:22: 34:24 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:35:13: 35:14 (#0),
+                                    },
+                                    Punct {
+                                        ch: '!',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:35:14: 35:15 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:35:16: 35:19 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:35:20: 35:25 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:35:19: 35:26 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:35:15: 35:27 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:34:25: 36:10 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:38:9: 38:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "print_helper",
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:11: 38:23 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "c",
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:24: 38:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:23: 38:26 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:10: 38:27 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:38:28: 38:29 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "cfg",
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:30: 38:33 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "not",
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:34: 38:37 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:38:38: 38:43 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:37: 38:44 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:33: 38:45 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:29: 38:46 (#0),
+                            },
+                            Ident {
+                                ident: "fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:38:47: 38:49 (#0),
+                            },
+                            Ident {
+                                ident: "kept_fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:38:50: 38:57 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:57: 38:59 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:13: 39:14 (#0),
+                                    },
+                                    Punct {
+                                        ch: '!',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:14: 39:15 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:39:16: 39:19 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "not",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:39:20: 39:23 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:39:24: 39:29 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:39:23: 39:30 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:39:19: 39:31 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:15: 39:32 (#0),
+                                    },
+                                    Ident {
+                                        ident: "let",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:13: 40:16 (#0),
+                                    },
+                                    Ident {
+                                        ident: "my_val",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:17: 40:23 (#0),
+                                    },
+                                    Punct {
+                                        ch: '=',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:24: 40:25 (#0),
+                                    },
+                                    Ident {
+                                        ident: "true",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:26: 40:30 (#0),
+                                    },
+                                    Punct {
+                                        ch: ';',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:30: 40:31 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:60: 41:10 (#0),
+                            },
+                            Ident {
+                                ident: "enum",
+                                span: $DIR/issue-75930-derive-cfg.rs:43:9: 43:13 (#0),
+                            },
+                            Ident {
+                                ident: "TupleEnum",
+                                span: $DIR/issue-75930-derive-cfg.rs:43:14: 43:23 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "Foo",
+                                        span: $DIR/issue-75930-derive-cfg.rs:44:13: 44:16 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Punct {
+                                                ch: '#',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:45:17: 45:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Bracket,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "cfg",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:45:19: 45:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:45:23: 45:28 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:45:22: 45:29 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:45:18: 45:30 (#0),
+                                            },
+                                            Ident {
+                                                ident: "u8",
+                                                span: $DIR/issue-75930-derive-cfg.rs:45:31: 45:33 (#0),
+                                            },
+                                            Punct {
+                                                ch: ',',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:45:33: 45:34 (#0),
+                                            },
+                                            Punct {
+                                                ch: '#',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:46:17: 46:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Bracket,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "cfg",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:46:19: 46:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:46:23: 46:28 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:46:22: 46:29 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:46:18: 46:30 (#0),
+                                            },
+                                            Ident {
+                                                ident: "bool",
+                                                span: $DIR/issue-75930-derive-cfg.rs:46:31: 46:35 (#0),
+                                            },
+                                            Punct {
+                                                ch: ',',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:46:35: 46:36 (#0),
+                                            },
+                                            Punct {
+                                                ch: '#',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:17: 47:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Bracket,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "cfg",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:47:19: 47:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "not",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:47:23: 47:26 (#0),
+                                                            },
+                                                            Group {
+                                                                delimiter: Parenthesis,
+                                                                stream: TokenStream [
+                                                                    Ident {
+                                                                        ident: "FALSE",
+                                                                        span: $DIR/issue-75930-derive-cfg.rs:47:27: 47:32 (#0),
+                                                                    },
+                                                                ],
+                                                                span: $DIR/issue-75930-derive-cfg.rs:47:26: 47:33 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:47:22: 47:34 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:18: 47:35 (#0),
+                                            },
+                                            Ident {
+                                                ident: "i32",
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:36: 47:39 (#0),
+                                            },
+                                            Punct {
+                                                ch: ',',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:39: 47:40 (#0),
+                                            },
+                                            Punct {
+                                                ch: '#',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:17: 48:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Bracket,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "cfg",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:48:19: 48:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:48:23: 48:28 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:48:22: 48:29 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:18: 48:30 (#0),
+                                            },
+                                            Ident {
+                                                ident: "String",
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:31: 48:37 (#0),
+                                            },
+                                            Punct {
+                                                ch: ',',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:37: 48:38 (#0),
+                                            },
+                                            Ident {
+                                                ident: "u8",
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:39: 48:41 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:44:16: 49:14 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:43:24: 50:10 (#0),
+                            },
+                            Ident {
+                                ident: "struct",
+                                span: $DIR/issue-75930-derive-cfg.rs:52:9: 52:15 (#0),
+                            },
+                            Ident {
+                                ident: "TupleStruct",
+                                span: $DIR/issue-75930-derive-cfg.rs:52:16: 52:27 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:53:13: 53:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:53:15: 53:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:53:19: 53:24 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:53:18: 53:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:53:14: 53:26 (#0),
+                                    },
+                                    Ident {
+                                        ident: "String",
+                                        span: $DIR/issue-75930-derive-cfg.rs:53:27: 53:33 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:53:33: 53:34 (#0),
+                                    },
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:13: 54:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:54:15: 54:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "not",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:54:19: 54:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:54:23: 54:28 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:54:22: 54:29 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:54:18: 54:30 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:14: 54:31 (#0),
+                                    },
+                                    Ident {
+                                        ident: "i32",
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:32: 54:35 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:35: 54:36 (#0),
+                                    },
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:55:13: 55:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:55:15: 55:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:55:19: 55:24 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:55:18: 55:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:55:14: 55:26 (#0),
+                                    },
+                                    Ident {
+                                        ident: "bool",
+                                        span: $DIR/issue-75930-derive-cfg.rs:55:27: 55:31 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:55:31: 55:32 (#0),
+                                    },
+                                    Ident {
+                                        ident: "u8",
+                                        span: $DIR/issue-75930-derive-cfg.rs:56:13: 56:15 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:52:27: 57:10 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:57:10: 57:11 (#0),
+                            },
+                            Literal {
+                                kind: Integer,
+                                symbol: "0",
+                                suffix: None,
+                                span: $DIR/issue-75930-derive-cfg.rs:59:9: 59:10 (#0),
+                            },
+                        ],
+                        span: $DIR/issue-75930-derive-cfg.rs:23:17: 60:6 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:23:12: 60:7 (#0),
+            },
+            Punct {
+                ch: ',',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:60:7: 60:8 (#0),
+            },
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:61:5: 61:6 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "print_helper",
+                        span: $DIR/issue-75930-derive-cfg.rs:61:7: 61:19 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "d",
+                                span: $DIR/issue-75930-derive-cfg.rs:61:20: 61:21 (#0),
+                            },
+                        ],
+                        span: $DIR/issue-75930-derive-cfg.rs:61:19: 61:22 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:61:6: 61:23 (#0),
+            },
+            Ident {
+                ident: "fourth",
+                span: $DIR/issue-75930-derive-cfg.rs:62:5: 62:11 (#0),
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:62:11: 62:12 (#0),
+            },
+            Ident {
+                ident: "B",
+                span: $DIR/issue-75930-derive-cfg.rs:62:13: 62:14 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:20:32: 63:2 (#0),
+    },
+]
+PRINT-DERIVE INPUT (DISPLAY): #[allow(dead_code)] #[print_helper(b)] #[print_helper(a)] struct Foo < B >
 {
     second : bool, third :
     [u8 ;
      {
-         #[cfg(not(FALSE))] struct Inner ; match true
-         { false => { } _ => { } } ; 0
-     }], fourth : B,
+         #[cfg(not(FALSE))] struct Inner ; ; match true
+         { #[allow(warnings)] false => { }, _ => { } } ; #[print_helper(c)]
+         #[cfg(not(FALSE))] fn kept_fn()
+         { # ! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum
+         { Foo(#[cfg(not(FALSE))] i32, u8) } struct
+         TupleStruct(#[cfg(not(FALSE))] i32, u8) ; 0
+     }], #[print_helper(d)] fourth : B
 }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:40 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "allow",
+                span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:29 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "dead_code",
+                        span: $DIR/issue-75930-derive-cfg.rs:16:30: 16:39 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:16:29: 16:40 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:16:24: 16:40 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:19:1: 19:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "print_helper",
+                span: $DIR/issue-75930-derive-cfg.rs:19:3: 19:15 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "b",
+                        span: $DIR/issue-75930-derive-cfg.rs:19:16: 19:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:19:15: 19:18 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:19:2: 19:19 (#0),
+    },
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:15:1: 15:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "print_helper",
+                span: $DIR/issue-75930-derive-cfg.rs:15:3: 15:15 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "a",
+                        span: $DIR/issue-75930-derive-cfg.rs:15:16: 15:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:15:15: 15:18 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:15:2: 15:19 (#0),
+    },
     Ident {
         ident: "struct",
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        span: $DIR/issue-75930-derive-cfg.rs:20:1: 20:7 (#0),
     },
     Ident {
         ident: "Foo",
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        span: $DIR/issue-75930-derive-cfg.rs:20:8: 20:11 (#0),
     },
     Punct {
         ch: '<',
-        spacing: Alone,
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        spacing: Joint,
+        span: $DIR/issue-75930-derive-cfg.rs:20:11: 20:12 (#0),
     },
     Ident {
         ident: "B",
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        span: $DIR/issue-75930-derive-cfg.rs:20:29: 20:30 (#0),
     },
     Punct {
         ch: '>',
         spacing: Alone,
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        span: $DIR/issue-75930-derive-cfg.rs:20:30: 20:31 (#0),
     },
     Group {
         delimiter: Brace,
         stream: TokenStream [
             Ident {
                 ident: "second",
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:22:40: 22:46 (#0),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:22:46: 22:47 (#0),
             },
             Ident {
                 ident: "bool",
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:22:48: 22:52 (#0),
             },
             Punct {
                 ch: ',',
                 spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:22:52: 22:53 (#0),
             },
             Ident {
                 ident: "third",
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:23:5: 23:10 (#0),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:23:10: 23:11 (#0),
             },
             Group {
                 delimiter: Bracket,
                 stream: TokenStream [
                     Ident {
                         ident: "u8",
-                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                        span: $DIR/issue-75930-derive-cfg.rs:23:13: 23:15 (#0),
                     },
                     Punct {
                         ch: ';',
                         spacing: Alone,
-                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                        span: $DIR/issue-75930-derive-cfg.rs:23:15: 23:16 (#0),
                     },
                     Group {
                         delimiter: Brace,
@@ -78,144 +1353,479 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
                             Punct {
                                 ch: '#',
                                 spacing: Alone,
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:25:9: 25:10 (#0),
                             },
                             Group {
                                 delimiter: Bracket,
                                 stream: TokenStream [
                                     Ident {
                                         ident: "cfg",
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:25:11: 25:14 (#0),
                                     },
                                     Group {
                                         delimiter: Parenthesis,
                                         stream: TokenStream [
                                             Ident {
                                                 ident: "not",
-                                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                                span: $DIR/issue-75930-derive-cfg.rs:25:15: 25:18 (#0),
                                             },
                                             Group {
                                                 delimiter: Parenthesis,
                                                 stream: TokenStream [
                                                     Ident {
                                                         ident: "FALSE",
-                                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                                        span: $DIR/issue-75930-derive-cfg.rs:25:19: 25:24 (#0),
                                                     },
                                                 ],
-                                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                                span: $DIR/issue-75930-derive-cfg.rs:25:18: 25:25 (#0),
                                             },
                                         ],
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:25:14: 25:26 (#0),
                                     },
                                 ],
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:25:10: 25:27 (#0),
                             },
                             Ident {
                                 ident: "struct",
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:25:28: 25:34 (#0),
                             },
                             Ident {
                                 ident: "Inner",
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:25:35: 25:40 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:25:40: 25:41 (#0),
                             },
                             Punct {
                                 ch: ';',
                                 spacing: Alone,
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:26:33: 26:34 (#0),
                             },
                             Ident {
                                 ident: "match",
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:27:9: 27:14 (#0),
                             },
                             Ident {
                                 ident: "true",
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:27:15: 27:19 (#0),
                             },
                             Group {
                                 delimiter: Brace,
                                 stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:36: 29:51 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "allow",
+                                                span: $DIR/issue-75930-derive-cfg.rs:29:36: 29:41 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "warnings",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:29:42: 29:50 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:29:41: 29:51 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:36: 29:51 (#0),
+                                    },
                                     Ident {
                                         ident: "false",
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:54: 29:59 (#0),
                                     },
                                     Punct {
                                         ch: '=',
                                         spacing: Joint,
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:60: 29:62 (#0),
                                     },
                                     Punct {
                                         ch: '>',
                                         spacing: Alone,
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:60: 29:62 (#0),
                                     },
                                     Group {
                                         delimiter: Brace,
                                         stream: TokenStream [],
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:63: 29:65 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:29:65: 29:66 (#0),
                                     },
                                     Ident {
                                         ident: "_",
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:13: 30:14 (#0),
                                     },
                                     Punct {
                                         ch: '=',
                                         spacing: Joint,
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:17 (#0),
                                     },
                                     Punct {
                                         ch: '>',
                                         spacing: Alone,
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:17 (#0),
                                     },
                                     Group {
                                         delimiter: Brace,
                                         stream: TokenStream [],
-                                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                        span: $DIR/issue-75930-derive-cfg.rs:30:18: 30:20 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:27:20: 31:10 (#0),
+                            },
+                            Punct {
+                                ch: ';',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:31:10: 31:11 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:38:9: 38:10 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "print_helper",
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:11: 38:23 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "c",
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:24: 38:25 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:23: 38:26 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:10: 38:27 (#0),
+                            },
+                            Punct {
+                                ch: '#',
+                                spacing: Alone,
+                                span: $DIR/issue-75930-derive-cfg.rs:38:28: 38:29 (#0),
+                            },
+                            Group {
+                                delimiter: Bracket,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "cfg",
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:30: 38:33 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "not",
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:34: 38:37 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "FALSE",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:38:38: 38:43 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:38:37: 38:44 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:38:33: 38:45 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:29: 38:46 (#0),
+                            },
+                            Ident {
+                                ident: "fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:38:47: 38:49 (#0),
+                            },
+                            Ident {
+                                ident: "kept_fn",
+                                span: $DIR/issue-75930-derive-cfg.rs:38:50: 38:57 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:57: 38:59 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Joint,
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:13: 39:14 (#0),
+                                    },
+                                    Punct {
+                                        ch: '!',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:14: 39:15 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:39:16: 39:19 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "not",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:39:20: 39:23 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:39:24: 39:29 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:39:23: 39:30 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:39:19: 39:31 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:39:15: 39:32 (#0),
+                                    },
+                                    Ident {
+                                        ident: "let",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:13: 40:16 (#0),
+                                    },
+                                    Ident {
+                                        ident: "my_val",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:17: 40:23 (#0),
+                                    },
+                                    Punct {
+                                        ch: '=',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:24: 40:25 (#0),
+                                    },
+                                    Ident {
+                                        ident: "true",
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:26: 40:30 (#0),
+                                    },
+                                    Punct {
+                                        ch: ';',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:40:30: 40:31 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:38:60: 41:10 (#0),
+                            },
+                            Ident {
+                                ident: "enum",
+                                span: $DIR/issue-75930-derive-cfg.rs:43:9: 43:13 (#0),
+                            },
+                            Ident {
+                                ident: "TupleEnum",
+                                span: $DIR/issue-75930-derive-cfg.rs:43:14: 43:23 (#0),
+                            },
+                            Group {
+                                delimiter: Brace,
+                                stream: TokenStream [
+                                    Ident {
+                                        ident: "Foo",
+                                        span: $DIR/issue-75930-derive-cfg.rs:44:13: 44:16 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Parenthesis,
+                                        stream: TokenStream [
+                                            Punct {
+                                                ch: '#',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:17: 47:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Bracket,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "cfg",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:47:19: 47:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "not",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:47:23: 47:26 (#0),
+                                                            },
+                                                            Group {
+                                                                delimiter: Parenthesis,
+                                                                stream: TokenStream [
+                                                                    Ident {
+                                                                        ident: "FALSE",
+                                                                        span: $DIR/issue-75930-derive-cfg.rs:47:27: 47:32 (#0),
+                                                                    },
+                                                                ],
+                                                                span: $DIR/issue-75930-derive-cfg.rs:47:26: 47:33 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:47:22: 47:34 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:18: 47:35 (#0),
+                                            },
+                                            Ident {
+                                                ident: "i32",
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:36: 47:39 (#0),
+                                            },
+                                            Punct {
+                                                ch: ',',
+                                                spacing: Alone,
+                                                span: $DIR/issue-75930-derive-cfg.rs:47:39: 47:40 (#0),
+                                            },
+                                            Ident {
+                                                ident: "u8",
+                                                span: $DIR/issue-75930-derive-cfg.rs:48:39: 48:41 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:44:16: 49:14 (#0),
+                                    },
+                                ],
+                                span: $DIR/issue-75930-derive-cfg.rs:43:24: 50:10 (#0),
+                            },
+                            Ident {
+                                ident: "struct",
+                                span: $DIR/issue-75930-derive-cfg.rs:52:9: 52:15 (#0),
+                            },
+                            Ident {
+                                ident: "TupleStruct",
+                                span: $DIR/issue-75930-derive-cfg.rs:52:16: 52:27 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [
+                                    Punct {
+                                        ch: '#',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:13: 54:14 (#0),
+                                    },
+                                    Group {
+                                        delimiter: Bracket,
+                                        stream: TokenStream [
+                                            Ident {
+                                                ident: "cfg",
+                                                span: $DIR/issue-75930-derive-cfg.rs:54:15: 54:18 (#0),
+                                            },
+                                            Group {
+                                                delimiter: Parenthesis,
+                                                stream: TokenStream [
+                                                    Ident {
+                                                        ident: "not",
+                                                        span: $DIR/issue-75930-derive-cfg.rs:54:19: 54:22 (#0),
+                                                    },
+                                                    Group {
+                                                        delimiter: Parenthesis,
+                                                        stream: TokenStream [
+                                                            Ident {
+                                                                ident: "FALSE",
+                                                                span: $DIR/issue-75930-derive-cfg.rs:54:23: 54:28 (#0),
+                                                            },
+                                                        ],
+                                                        span: $DIR/issue-75930-derive-cfg.rs:54:22: 54:29 (#0),
+                                                    },
+                                                ],
+                                                span: $DIR/issue-75930-derive-cfg.rs:54:18: 54:30 (#0),
+                                            },
+                                        ],
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:14: 54:31 (#0),
+                                    },
+                                    Ident {
+                                        ident: "i32",
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:32: 54:35 (#0),
+                                    },
+                                    Punct {
+                                        ch: ',',
+                                        spacing: Alone,
+                                        span: $DIR/issue-75930-derive-cfg.rs:54:35: 54:36 (#0),
+                                    },
+                                    Ident {
+                                        ident: "u8",
+                                        span: $DIR/issue-75930-derive-cfg.rs:56:13: 56:15 (#0),
                                     },
                                 ],
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:52:27: 57:10 (#0),
                             },
                             Punct {
                                 ch: ';',
                                 spacing: Alone,
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:57:10: 57:11 (#0),
                             },
                             Literal {
                                 kind: Integer,
                                 symbol: "0",
                                 suffix: None,
-                                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                                span: $DIR/issue-75930-derive-cfg.rs:59:9: 59:10 (#0),
                             },
                         ],
-                        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                        span: $DIR/issue-75930-derive-cfg.rs:23:17: 60:6 (#0),
                     },
                 ],
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:23:12: 60:7 (#0),
             },
             Punct {
                 ch: ',',
                 spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:60:7: 60:8 (#0),
+            },
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/issue-75930-derive-cfg.rs:61:5: 61:6 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "print_helper",
+                        span: $DIR/issue-75930-derive-cfg.rs:61:7: 61:19 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "d",
+                                span: $DIR/issue-75930-derive-cfg.rs:61:20: 61:21 (#0),
+                            },
+                        ],
+                        span: $DIR/issue-75930-derive-cfg.rs:61:19: 61:22 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:61:6: 61:23 (#0),
             },
             Ident {
                 ident: "fourth",
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:62:5: 62:11 (#0),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:62:11: 62:12 (#0),
             },
             Ident {
                 ident: "B",
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
-            },
-            Punct {
-                ch: ',',
-                spacing: Alone,
-                span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+                span: $DIR/issue-75930-derive-cfg.rs:62:13: 62:14 (#0),
             },
         ],
-        span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0),
+        span: $DIR/issue-75930-derive-cfg.rs:20:32: 63:2 (#0),
     },
 ]
diff --git a/src/test/ui/proc-macro/parent-source-spans.stderr b/src/test/ui/proc-macro/parent-source-spans.stderr
index 5ae2583f01c60..47094408d560d 100644
--- a/src/test/ui/proc-macro/parent-source-spans.stderr
+++ b/src/test/ui/proc-macro/parent-source-spans.stderr
@@ -148,7 +148,7 @@ LL |     one!("hello", "world");
   ::: $SRC_DIR/core/src/result.rs:LL:COL
    |
 LL |     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
-   |     --------------------------------------------------- similarly named tuple variant `Ok` defined here
+   |     ---------------------------------------------------- similarly named tuple variant `Ok` defined here
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -164,7 +164,7 @@ LL |     two!("yay", "rust");
   ::: $SRC_DIR/core/src/result.rs:LL:COL
    |
 LL |     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
-   |     --------------------------------------------------- similarly named tuple variant `Ok` defined here
+   |     ---------------------------------------------------- similarly named tuple variant `Ok` defined here
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -180,7 +180,7 @@ LL |     three!("hip", "hop");
   ::: $SRC_DIR/core/src/result.rs:LL:COL
    |
 LL |     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
-   |     --------------------------------------------------- similarly named tuple variant `Ok` defined here
+   |     ---------------------------------------------------- similarly named tuple variant `Ok` defined here
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/src/test/ui/proc-macro/weird-attr-capture.rs b/src/test/ui/proc-macro/weird-attr-capture.rs
new file mode 100644
index 0000000000000..939e2a807a37e
--- /dev/null
+++ b/src/test/ui/proc-macro/weird-attr-capture.rs
@@ -0,0 +1,22 @@
+// check-pass
+//
+// Makes sure we don't ICE when token capturing is triggered
+// in an usual way
+
+macro_rules! as_item {
+    ($($i:item)*) => {$($i)*};
+}
+
+macro_rules! concat_attrs {
+    ($($attrs:tt)*) => {
+        as_item! {
+            $($attrs)*
+            pub struct Foo;
+        }
+    }
+}
+
+concat_attrs! { #[cfg(not(FALSE))] }
+
+
+fn main() {}
diff --git a/src/test/ui/repr/repr-transparent.stderr b/src/test/ui/repr/repr-transparent.stderr
index cbc74fbb6a2cf..accc8a09c536c 100644
--- a/src/test/ui/repr/repr-transparent.stderr
+++ b/src/test/ui/repr/repr-transparent.stderr
@@ -90,9 +90,9 @@ error[E0731]: transparent enum needs exactly one variant, but has 2
 LL | enum TooManyVariants {
    | ^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
 LL |     Foo(String),
-   |     -----------
+   |     ------------
 LL |     Bar,
-   |     --- too many variants in `TooManyVariants`
+   |     ---- too many variants in `TooManyVariants`
 
 error[E0691]: zero-sized field in transparent enum has alignment larger than 1
   --> $DIR/repr-transparent.rs:65:14
diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr
index 32eff15119648..2207983995ad9 100644
--- a/src/test/ui/resolve/privacy-enum-ctor.stderr
+++ b/src/test/ui/resolve/privacy-enum-ctor.stderr
@@ -34,7 +34,7 @@ error[E0423]: expected value, found struct variant `Z::Struct`
 LL | /             Struct {
 LL | |                 s: u8,
 LL | |             },
-   | |_____________- `Z::Struct` defined here
+   | |______________- `Z::Struct` defined here
 ...
 LL |           let _: Z = Z::Struct;
    |                      ^^^^^^^^^ help: use struct literal syntax instead: `Z::Struct { s: val }`
@@ -73,7 +73,7 @@ error[E0423]: expected value, found struct variant `m::E::Struct`
 LL | /         Struct {
 LL | |             s: u8,
 LL | |         },
-   | |_________- `m::E::Struct` defined here
+   | |__________- `m::E::Struct` defined here
 ...
 LL |       let _: E = m::E::Struct;
    |                  ^^^^^^^^^^^^ help: use struct literal syntax instead: `m::E::Struct { s: val }`
@@ -105,7 +105,7 @@ error[E0423]: expected value, found struct variant `E::Struct`
 LL | /         Struct {
 LL | |             s: u8,
 LL | |         },
-   | |_________- `E::Struct` defined here
+   | |__________- `E::Struct` defined here
 ...
 LL |       let _: E = E::Struct;
    |                  ^^^^^^^^^ help: use struct literal syntax instead: `E::Struct { s: val }`
@@ -185,7 +185,7 @@ error[E0423]: expected value, found struct variant `m::n::Z::Struct`
 LL | /             Struct {
 LL | |                 s: u8,
 LL | |             },
-   | |_____________- `m::n::Z::Struct` defined here
+   | |______________- `m::n::Z::Struct` defined here
 ...
 LL |       let _: Z = m::n::Z::Struct;
    |                  ^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `m::n::Z::Struct { s: val }`
@@ -260,7 +260,7 @@ error[E0308]: mismatched types
   --> $DIR/privacy-enum-ctor.rs:27:20
    |
 LL |             Fn(u8),
-   |             ------ fn(u8) -> Z {Z::Fn} defined here
+   |             ------- fn(u8) -> Z {Z::Fn} defined here
 ...
 LL |         let _: Z = Z::Fn;
    |                -   ^^^^^ expected enum `Z`, found fn item
@@ -278,7 +278,7 @@ error[E0618]: expected function, found enum variant `Z::Unit`
   --> $DIR/privacy-enum-ctor.rs:31:17
    |
 LL |             Unit,
-   |             ---- `Z::Unit` defined here
+   |             ----- `Z::Unit` defined here
 ...
 LL |         let _ = Z::Unit();
    |                 ^^^^^^^--
@@ -294,7 +294,7 @@ error[E0308]: mismatched types
   --> $DIR/privacy-enum-ctor.rs:43:16
    |
 LL |         Fn(u8),
-   |         ------ fn(u8) -> E {E::Fn} defined here
+   |         ------- fn(u8) -> E {E::Fn} defined here
 ...
 LL |     let _: E = m::E::Fn;
    |            -   ^^^^^^^^ expected enum `E`, found fn item
@@ -312,7 +312,7 @@ error[E0618]: expected function, found enum variant `m::E::Unit`
   --> $DIR/privacy-enum-ctor.rs:47:16
    |
 LL |         Unit,
-   |         ---- `m::E::Unit` defined here
+   |         ----- `m::E::Unit` defined here
 ...
 LL |     let _: E = m::E::Unit();
    |                ^^^^^^^^^^--
@@ -328,7 +328,7 @@ error[E0308]: mismatched types
   --> $DIR/privacy-enum-ctor.rs:51:16
    |
 LL |         Fn(u8),
-   |         ------ fn(u8) -> E {E::Fn} defined here
+   |         ------- fn(u8) -> E {E::Fn} defined here
 ...
 LL |     let _: E = E::Fn;
    |            -   ^^^^^ expected enum `E`, found fn item
@@ -346,7 +346,7 @@ error[E0618]: expected function, found enum variant `E::Unit`
   --> $DIR/privacy-enum-ctor.rs:55:16
    |
 LL |         Unit,
-   |         ---- `E::Unit` defined here
+   |         ----- `E::Unit` defined here
 ...
 LL |     let _: E = E::Unit();
    |                ^^^^^^^--
diff --git a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
index fbdbb0c9930a6..32421e62b3f52 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
+++ b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
@@ -8,7 +8,7 @@ note: the tuple variant `Tuple` is defined here
   --> $DIR/auxiliary/variants.rs:5:23
    |
 LL |     #[non_exhaustive] Tuple(u32),
-   |                       ^^^^^^^^^^
+   |                       ^^^^^^^^^^^
 
 error[E0603]: unit variant `Unit` is private
   --> $DIR/variant.rs:14:47
@@ -20,7 +20,7 @@ note: the unit variant `Unit` is defined here
   --> $DIR/auxiliary/variants.rs:4:23
    |
 LL |     #[non_exhaustive] Unit,
-   |                       ^^^^
+   |                       ^^^^^
 
 error[E0603]: unit variant `Unit` is private
   --> $DIR/variant.rs:18:32
@@ -32,7 +32,7 @@ note: the unit variant `Unit` is defined here
   --> $DIR/auxiliary/variants.rs:4:23
    |
 LL |     #[non_exhaustive] Unit,
-   |                       ^^^^
+   |                       ^^^^^
 
 error[E0603]: tuple variant `Tuple` is private
   --> $DIR/variant.rs:20:32
@@ -44,7 +44,7 @@ note: the tuple variant `Tuple` is defined here
   --> $DIR/auxiliary/variants.rs:5:23
    |
 LL |     #[non_exhaustive] Tuple(u32),
-   |                       ^^^^^^^^^^
+   |                       ^^^^^^^^^^^
 
 error[E0603]: tuple variant `Tuple` is private
   --> $DIR/variant.rs:26:35
@@ -56,7 +56,7 @@ note: the tuple variant `Tuple` is defined here
   --> $DIR/auxiliary/variants.rs:5:23
    |
 LL |     #[non_exhaustive] Tuple(u32),
-   |                       ^^^^^^^^^^
+   |                       ^^^^^^^^^^^
 
 error[E0639]: cannot create non-exhaustive variant using struct expression
   --> $DIR/variant.rs:8:26
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
index 9b2febb1393e1..408a37c4522cb 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
@@ -2,9 +2,9 @@ error[E0423]: expected value, found struct variant `E::B`
   --> $DIR/fn-or-tuple-struct-without-args.rs:36:16
    |
 LL |     A(usize),
-   |     -------- similarly named tuple variant `A` defined here
+   |     --------- similarly named tuple variant `A` defined here
 LL |     B { a: usize },
-   |     -------------- `E::B` defined here
+   |     --------------- `E::B` defined here
 ...
 LL |     let _: E = E::B;
    |                ^^^^
@@ -130,7 +130,7 @@ error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:35:16
    |
 LL |     A(usize),
-   |     -------- fn(usize) -> E {E::A} defined here
+   |     --------- fn(usize) -> E {E::A} defined here
 ...
 LL |     let _: E = E::A;
    |            -   ^^^^ expected enum `E`, found fn item
diff --git a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr
index b0de3ee42e339..72be43954265d 100644
--- a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr
+++ b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr
@@ -20,7 +20,7 @@ error[E0618]: expected function, found enum variant `Alias::Unit`
   --> $DIR/incorrect-variant-form-through-alias-caught.rs:17:5
    |
 LL | enum Enum { Braced {}, Unit, Tuple() }
-   |                        ---- `Alias::Unit` defined here
+   |                        ----- `Alias::Unit` defined here
 ...
 LL |     Alias::Unit();
    |     ^^^^^^^^^^^--
diff --git a/src/test/ui/variants/variant-size-differences.stderr b/src/test/ui/variants/variant-size-differences.stderr
index 241a757d44523..ef5d3904033de 100644
--- a/src/test/ui/variants/variant-size-differences.stderr
+++ b/src/test/ui/variants/variant-size-differences.stderr
@@ -2,7 +2,7 @@ error: enum variant is more than three times larger (1024 bytes) than the next l
   --> $DIR/variant-size-differences.rs:5:5
    |
 LL |     VBig([u8; 1024]),
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/variant-size-differences.rs:1:9