about summary refs log tree commit diff
path: root/compiler/rustc_expand/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_expand/src')
-rw-r--r--compiler/rustc_expand/src/base.rs51
-rw-r--r--compiler/rustc_expand/src/config.rs178
-rw-r--r--compiler/rustc_expand/src/expand.rs1428
-rw-r--r--compiler/rustc_expand/src/lib.rs5
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs28
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs4
-rw-r--r--compiler/rustc_expand/src/module.rs14
-rw-r--r--compiler/rustc_expand/src/parse/tests.rs29
-rw-r--r--compiler/rustc_expand/src/placeholders.rs21
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs28
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs173
12 files changed, 1192 insertions, 771 deletions
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index b630bc1f473..258320aeb63 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
@@ -48,6 +48,7 @@ pub enum Annotatable {
     Param(ast::Param),
     FieldDef(ast::FieldDef),
     Variant(ast::Variant),
+    Crate(ast::Crate),
 }
 
 impl Annotatable {
@@ -66,6 +67,7 @@ impl Annotatable {
             Annotatable::Param(ref p) => p.span,
             Annotatable::FieldDef(ref sf) => sf.span,
             Annotatable::Variant(ref v) => v.span,
+            Annotatable::Crate(ref c) => c.span,
         }
     }
 
@@ -84,6 +86,7 @@ impl Annotatable {
             Annotatable::Param(p) => p.visit_attrs(f),
             Annotatable::FieldDef(sf) => sf.visit_attrs(f),
             Annotatable::Variant(v) => v.visit_attrs(f),
+            Annotatable::Crate(c) => c.visit_attrs(f),
         }
     }
 
@@ -102,6 +105,7 @@ impl Annotatable {
             Annotatable::Param(p) => visitor.visit_param(p),
             Annotatable::FieldDef(sf) => visitor.visit_field_def(sf),
             Annotatable::Variant(v) => visitor.visit_variant(v),
+            Annotatable::Crate(c) => visitor.visit_crate(c),
         }
     }
 
@@ -122,7 +126,8 @@ impl Annotatable {
             | Annotatable::GenericParam(..)
             | Annotatable::Param(..)
             | Annotatable::FieldDef(..)
-            | Annotatable::Variant(..) => panic!("unexpected annotatable"),
+            | Annotatable::Variant(..)
+            | Annotatable::Crate(..) => panic!("unexpected annotatable"),
         }
     }
 
@@ -220,6 +225,13 @@ impl Annotatable {
             _ => panic!("expected variant"),
         }
     }
+
+    pub fn expect_crate(self) -> ast::Crate {
+        match self {
+            Annotatable::Crate(krate) => krate,
+            _ => panic!("expected krate"),
+        }
+    }
 }
 
 /// Result of an expansion that may need to be retried.
@@ -419,6 +431,11 @@ pub trait MacResult {
     fn make_variants(self: Box<Self>) -> Option<SmallVec<[ast::Variant; 1]>> {
         None
     }
+
+    fn make_crate(self: Box<Self>) -> Option<ast::Crate> {
+        // Fn-like macros cannot produce a crate.
+        unreachable!()
+    }
 }
 
 macro_rules! make_MacEager {
@@ -903,8 +920,25 @@ pub trait ResolverExpand {
     /// we generated proc macros harnesses, so that we can map
     /// HIR proc macros items back to their harness items.
     fn declare_proc_macro(&mut self, id: NodeId);
+
+    /// Tools registered with `#![register_tool]` and used by tool attributes and lints.
+    fn registered_tools(&self) -> &FxHashSet<Ident>;
 }
 
+pub trait LintStoreExpand {
+    fn pre_expansion_lint(
+        &self,
+        sess: &Session,
+        registered_tools: &FxHashSet<Ident>,
+        node_id: NodeId,
+        attrs: &[Attribute],
+        items: &[P<Item>],
+        name: &str,
+    );
+}
+
+type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>;
+
 #[derive(Clone, Default)]
 pub struct ModuleData {
     /// Path to the module starting from the crate name, like `my_crate::foo::bar`.
@@ -939,9 +973,6 @@ pub struct ExpansionData {
     pub is_trailing_mac: bool,
 }
 
-type OnExternModLoaded<'a> =
-    Option<&'a dyn Fn(Ident, Vec<Attribute>, Vec<P<Item>>, Span) -> (Vec<Attribute>, Vec<P<Item>>)>;
-
 /// One of these is made during expansion and incrementally updated as we go;
 /// when a macro expansion occurs, the resulting nodes have the `backtrace()
 /// -> expn_data` of their expansion context stored into their span.
@@ -956,10 +987,8 @@ pub struct ExtCtxt<'a> {
     /// (or during eager expansion, but that's a hack).
     pub force_mode: bool,
     pub expansions: FxHashMap<Span, Vec<String>>,
-    /// Called directly after having parsed an external `mod foo;` in expansion.
-    ///
-    /// `Ident` is the module name.
-    pub(super) extern_mod_loaded: OnExternModLoaded<'a>,
+    /// Used for running pre-expansion lints on freshly loaded modules.
+    pub(super) lint_store: LintStoreExpandDyn<'a>,
     /// When we 'expand' an inert attribute, we leave it
     /// in the AST, but insert it here so that we know
     /// not to expand it again.
@@ -971,14 +1000,14 @@ impl<'a> ExtCtxt<'a> {
         sess: &'a Session,
         ecfg: expand::ExpansionConfig<'a>,
         resolver: &'a mut dyn ResolverExpand,
-        extern_mod_loaded: OnExternModLoaded<'a>,
+        lint_store: LintStoreExpandDyn<'a>,
     ) -> ExtCtxt<'a> {
         ExtCtxt {
             sess,
             ecfg,
             reduced_recursion_limit: None,
             resolver,
-            extern_mod_loaded,
+            lint_store,
             root_path: PathBuf::new(),
             current_expansion: ExpansionData {
                 id: LocalExpnId::ROOT,
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 1b123520961..5fa7ffd554e 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -171,7 +171,7 @@ fn get_features(
             }
 
             if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() {
-                if allowed.iter().all(|f| name.as_str() != *f) {
+                if allowed.iter().all(|f| name.as_str() != f) {
                     struct_span_err!(
                         span_handler,
                         mi.span(),
@@ -238,7 +238,7 @@ macro_rules! configure {
 }
 
 impl<'a> StripUnconfigured<'a> {
-    pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> {
+    pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> {
         self.process_cfg_attrs(&mut node);
         if self.in_cfg(node.attrs()) {
             self.try_configure_tokens(&mut node);
@@ -248,7 +248,7 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
-    fn try_configure_tokens<T: AstLike>(&mut self, node: &mut T) {
+    fn try_configure_tokens<T: AstLike>(&self, node: &mut T) {
         if self.config_tokens {
             if let Some(Some(tokens)) = node.tokens_mut() {
                 let attr_annotated_tokens = tokens.create_token_stream();
@@ -257,10 +257,7 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
-    fn configure_krate_attrs(
-        &mut self,
-        mut attrs: Vec<ast::Attribute>,
-    ) -> Option<Vec<ast::Attribute>> {
+    fn configure_krate_attrs(&self, mut attrs: Vec<ast::Attribute>) -> Option<Vec<ast::Attribute>> {
         attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
         if self.in_cfg(&attrs) { Some(attrs) } else { None }
     }
@@ -269,7 +266,7 @@ impl<'a> StripUnconfigured<'a> {
     /// This is only used during the invocation of `derive` proc-macros,
     /// which require that we cfg-expand their entire input.
     /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method
-    fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream {
+    fn configure_tokens(&self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream {
         fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool {
             stream.0.iter().all(|(tree, _spacing)| match tree {
                 AttrAnnotatedTokenTree::Attributes(_) => false,
@@ -325,12 +322,16 @@ impl<'a> StripUnconfigured<'a> {
     /// Gives compiler warnings if any `cfg_attr` does not contain any
     /// attributes and is in the original source code. Gives compiler errors if
     /// the syntax of any `cfg_attr` is incorrect.
-    fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) {
+    fn process_cfg_attrs<T: AstLike>(&self, node: &mut T) {
         node.visit_attrs(|attrs| {
             attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
         });
     }
 
+    fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
+        if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] }
+    }
+
     /// Parse and expand a single `cfg_attr` attribute into a list of attributes
     /// when the configuration predicate is true, or otherwise expand into an
     /// empty list of attributes.
@@ -338,11 +339,7 @@ impl<'a> StripUnconfigured<'a> {
     /// Gives a compiler warning when the `cfg_attr` contains no attributes and
     /// is in the original source file. Gives a compiler error if the syntax of
     /// the attribute is incorrect.
-    fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
-        if !attr.has_name(sym::cfg_attr) {
-            return vec![attr];
-        }
-
+    crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> {
         let (cfg_predicate, expanded_attrs) =
             match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
                 None => return vec![],
@@ -351,78 +348,109 @@ impl<'a> StripUnconfigured<'a> {
 
         // Lint on zero attributes in source.
         if expanded_attrs.is_empty() {
-            return vec![attr];
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::UNUSED_ATTRIBUTES,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`#[cfg_attr]` does not expand to any attributes",
+            );
         }
 
         if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
             return vec![];
         }
 
-        // We call `process_cfg_attr` recursively in case there's a
-        // `cfg_attr` inside of another `cfg_attr`. E.g.
-        //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
-        expanded_attrs
-            .into_iter()
-            .flat_map(|(item, span)| {
-                let orig_tokens = attr.tokens().to_tokenstream();
-
-                // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
-                // and producing an attribute of the form `#[attr]`. We
-                // have captured tokens for `attr` itself, but we need to
-                // synthesize tokens for the wrapper `#` and `[]`, which
-                // we do below.
-
-                // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
-                // for `attr` when we expand it to `#[attr]`
-                let mut orig_trees = orig_tokens.trees();
-                let pound_token = match orig_trees.next().unwrap() {
-                    TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
-                    _ => panic!("Bad tokens for attribute {:?}", attr),
-                };
-                let pound_span = pound_token.span;
-
-                let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
-                if attr.style == AttrStyle::Inner {
-                    // For inner attributes, we do the same thing for the `!` in `#![some_attr]`
-                    let bang_token = match orig_trees.next().unwrap() {
-                        TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
-                        _ => panic!("Bad tokens for attribute {:?}", attr),
-                    };
-                    trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
-                }
-                // We don't really have a good span to use for the syntheized `[]`
-                // in `#[attr]`, so just use the span of the `#` token.
-                let bracket_group = AttrAnnotatedTokenTree::Delimited(
-                    DelimSpan::from_single(pound_span),
-                    DelimToken::Bracket,
-                    item.tokens
-                        .as_ref()
-                        .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
-                        .create_token_stream(),
-                );
-                trees.push((bracket_group, Spacing::Alone));
-                let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
-                self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span))
-            })
-            .collect()
+        if recursive {
+            // We call `process_cfg_attr` recursively in case there's a
+            // `cfg_attr` inside of another `cfg_attr`. E.g.
+            //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
+            expanded_attrs
+                .into_iter()
+                .flat_map(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item)))
+                .collect()
+        } else {
+            expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect()
+        }
+    }
+
+    fn expand_cfg_attr_item(
+        &self,
+        attr: &Attribute,
+        (item, item_span): (ast::AttrItem, Span),
+    ) -> Attribute {
+        let orig_tokens = attr.tokens().to_tokenstream();
+
+        // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
+        // and producing an attribute of the form `#[attr]`. We
+        // have captured tokens for `attr` itself, but we need to
+        // synthesize tokens for the wrapper `#` and `[]`, which
+        // we do below.
+
+        // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
+        // for `attr` when we expand it to `#[attr]`
+        let mut orig_trees = orig_tokens.trees();
+        let pound_token = match orig_trees.next().unwrap() {
+            TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
+            _ => panic!("Bad tokens for attribute {:?}", attr),
+        };
+        let pound_span = pound_token.span;
+
+        let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
+        if attr.style == AttrStyle::Inner {
+            // For inner attributes, we do the same thing for the `!` in `#![some_attr]`
+            let bang_token = match orig_trees.next().unwrap() {
+                TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
+                _ => panic!("Bad tokens for attribute {:?}", attr),
+            };
+            trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
+        }
+        // We don't really have a good span to use for the syntheized `[]`
+        // in `#[attr]`, so just use the span of the `#` token.
+        let bracket_group = AttrAnnotatedTokenTree::Delimited(
+            DelimSpan::from_single(pound_span),
+            DelimToken::Bracket,
+            item.tokens
+                .as_ref()
+                .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
+                .create_token_stream(),
+        );
+        trees.push((bracket_group, Spacing::Alone));
+        let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
+        let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span);
+        if attr.has_name(sym::crate_type) {
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`crate_type` within an `#![cfg_attr] attribute is deprecated`",
+            );
+        }
+        if attr.has_name(sym::crate_name) {
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`crate_name` within an `#![cfg_attr] attribute is deprecated`",
+            );
+        }
+        attr
     }
 
     /// Determines if a node with the given attributes should be included in this configuration.
     fn in_cfg(&self, attrs: &[Attribute]) -> bool {
-        attrs.iter().all(|attr| {
-            if !is_cfg(attr) {
+        attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
+    }
+
+    crate fn cfg_true(&self, attr: &Attribute) -> bool {
+        let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
+            Ok(meta_item) => meta_item,
+            Err(mut err) => {
+                err.emit();
                 return true;
             }
-            let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
-                Ok(meta_item) => meta_item,
-                Err(mut err) => {
-                    err.emit();
-                    return true;
-                }
-            };
-            parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
-                attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
-            })
+        };
+        parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
+            attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
         })
     }
 
@@ -444,7 +472,7 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
-    pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
+    pub fn configure_expr(&self, expr: &mut P<ast::Expr>) {
         for attr in expr.attrs.iter() {
             self.maybe_emit_expr_attr_err(attr);
         }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 89dbd64ed81..9a4daa6d750 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1,6 +1,5 @@
 use crate::base::*;
 use crate::config::StripUnconfigured;
-use crate::configure;
 use crate::hygiene::SyntaxContext;
 use crate::mbe::macro_rules::annotate_err_with_kind;
 use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
@@ -12,15 +11,13 @@ use rustc_ast::ptr::P;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs, MacCall};
-use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
-use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
+use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind};
+use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
+use rustc_ast::{NodeId, PatKind, StmtKind, TyKind};
 use rustc_ast_pretty::pprust;
-use rustc_attr::is_builtin_attr;
 use rustc_data_structures::map_in_place::MapInPlace;
-use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, FatalError, PResult};
+use rustc_errors::{Applicability, PResult};
 use rustc_feature::Features;
 use rustc_parse::parser::{
     AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma,
@@ -33,8 +30,8 @@ use rustc_session::Limit;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{FileName, LocalExpnId, Span};
 
-use smallvec::{smallvec, SmallVec};
-use std::ops::DerefMut;
+use smallvec::SmallVec;
+use std::ops::Deref;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::{iter, mem};
@@ -109,6 +106,10 @@ macro_rules! ast_fragments {
                 }
             })*
 
+            fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy {
+                T::fragment_to_output(self)
+            }
+
             pub fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) {
                 match self {
                     AstFragment::OptExpr(opt_expr) => {
@@ -178,10 +179,10 @@ ast_fragments! {
     Arms(SmallVec<[ast::Arm; 1]>) {
         "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms;
     }
-    Fields(SmallVec<[ast::ExprField; 1]>) {
+    ExprFields(SmallVec<[ast::ExprField; 1]>) {
         "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields;
     }
-    FieldPats(SmallVec<[ast::PatField; 1]>) {
+    PatFields(SmallVec<[ast::PatField; 1]>) {
         "field pattern";
         many fn flat_map_pat_field;
         fn visit_pat_field();
@@ -196,7 +197,7 @@ ast_fragments! {
     Params(SmallVec<[ast::Param; 1]>) {
         "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params;
     }
-    StructFields(SmallVec<[ast::FieldDef; 1]>) {
+    FieldDefs(SmallVec<[ast::FieldDef; 1]>) {
         "field";
         many fn flat_map_field_def;
         fn visit_field_def();
@@ -205,6 +206,7 @@ ast_fragments! {
     Variants(SmallVec<[ast::Variant; 1]>) {
         "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants;
     }
+    Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; }
 }
 
 pub enum SupportsMacroExpansion {
@@ -227,15 +229,14 @@ impl AstFragmentKind {
             AstFragmentKind::Items
             | AstFragmentKind::TraitItems
             | AstFragmentKind::ImplItems
-            | AstFragmentKind::ForeignItems => {
-                SupportsMacroExpansion::Yes { supports_inner_attrs: true }
-            }
+            | AstFragmentKind::ForeignItems
+            | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true },
             AstFragmentKind::Arms
-            | AstFragmentKind::Fields
-            | AstFragmentKind::FieldPats
+            | AstFragmentKind::ExprFields
+            | AstFragmentKind::PatFields
             | AstFragmentKind::GenericParams
             | AstFragmentKind::Params
-            | AstFragmentKind::StructFields
+            | AstFragmentKind::FieldDefs
             | AstFragmentKind::Variants => SupportsMacroExpansion::No,
         }
     }
@@ -249,11 +250,11 @@ impl AstFragmentKind {
             AstFragmentKind::Arms => {
                 AstFragment::Arms(items.map(Annotatable::expect_arm).collect())
             }
-            AstFragmentKind::Fields => {
-                AstFragment::Fields(items.map(Annotatable::expect_expr_field).collect())
+            AstFragmentKind::ExprFields => {
+                AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect())
             }
-            AstFragmentKind::FieldPats => {
-                AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).collect())
+            AstFragmentKind::PatFields => {
+                AstFragment::PatFields(items.map(Annotatable::expect_pat_field).collect())
             }
             AstFragmentKind::GenericParams => {
                 AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect())
@@ -261,8 +262,8 @@ impl AstFragmentKind {
             AstFragmentKind::Params => {
                 AstFragment::Params(items.map(Annotatable::expect_param).collect())
             }
-            AstFragmentKind::StructFields => {
-                AstFragment::StructFields(items.map(Annotatable::expect_field_def).collect())
+            AstFragmentKind::FieldDefs => {
+                AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect())
             }
             AstFragmentKind::Variants => {
                 AstFragment::Variants(items.map(Annotatable::expect_variant).collect())
@@ -288,6 +289,9 @@ impl AstFragmentKind {
             AstFragmentKind::OptExpr => {
                 AstFragment::OptExpr(items.next().map(Annotatable::expect_expr))
             }
+            AstFragmentKind::Crate => {
+                AstFragment::Crate(items.next().expect("expected exactly one crate").expect_crate())
+            }
             AstFragmentKind::Pat | AstFragmentKind::Ty => {
                 panic!("patterns and types aren't annotatable")
             }
@@ -312,10 +316,10 @@ pub enum InvocationKind {
         pos: usize,
         item: Annotatable,
         // Required for resolving derive helper attributes.
-        derives: Vec<Path>,
+        derives: Vec<ast::Path>,
     },
     Derive {
-        path: Path,
+        path: ast::Path,
         item: Annotatable,
     },
 }
@@ -359,9 +363,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         MacroExpander { cx, monotonic }
     }
 
-    // FIXME: Avoid visiting the crate as a `Mod` item,
-    // make crate a first class expansion target instead.
-    pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
+    pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate {
         let file_path = match self.cx.source_map().span_to_filename(krate.span) {
             FileName::Real(name) => name
                 .into_local_path()
@@ -375,52 +377,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             file_path_stack: vec![file_path],
             dir_path,
         });
-
-        let krate_item = AstFragment::Items(smallvec![P(ast::Item {
-            attrs: krate.attrs,
-            span: krate.span,
-            kind: ast::ItemKind::Mod(
-                Unsafe::No,
-                ModKind::Loaded(krate.items, Inline::Yes, krate.span)
-            ),
-            ident: Ident::empty(),
-            id: ast::DUMMY_NODE_ID,
-            vis: ast::Visibility {
-                span: krate.span.shrink_to_lo(),
-                kind: ast::VisibilityKind::Public,
-                tokens: None,
-            },
-            tokens: None,
-        })]);
-
-        match self.fully_expand_fragment(krate_item).make_items().pop().map(P::into_inner) {
-            Some(ast::Item {
-                attrs,
-                kind: ast::ItemKind::Mod(_, ModKind::Loaded(items, ..)),
-                ..
-            }) => {
-                krate.attrs = attrs;
-                krate.items = items;
-            }
-            None => {
-                // Resolution failed so we return an empty expansion
-                krate.attrs = vec![];
-                krate.items = vec![];
-            }
-            Some(ast::Item { span, kind, .. }) => {
-                krate.attrs = vec![];
-                krate.items = vec![];
-                self.cx.span_err(
-                    span,
-                    &format!(
-                        "expected crate top-level item to be a module after macro expansion, found {} {}",
-                        kind.article(), kind.descr()
-                    ),
-                );
-                // FIXME: this workaround issue #84569
-                FatalError.raise();
-            }
-        };
+        let krate = self.fully_expand_fragment(AstFragment::Crate(krate)).make_crate();
+        assert_eq!(krate.id, ast::CRATE_NODE_ID);
         self.cx.trace_macros_diag();
         krate
     }
@@ -708,26 +666,32 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 SyntaxExtensionKind::Attr(expander) => {
                     self.gate_proc_macro_input(&item);
                     self.gate_proc_macro_attr_item(span, &item);
-                    let mut fake_tokens = false;
-                    if let Annotatable::Item(item_inner) = &item {
-                        if let ItemKind::Mod(_, mod_kind) = &item_inner.kind {
-                            // FIXME: Collect tokens and use them instead of generating
-                            // fake ones. These are unstable, so it needs to be
-                            // fixed prior to stabilization
-                            // Fake tokens when we are invoking an inner attribute, and:
-                            fake_tokens = matches!(attr.style, ast::AttrStyle::Inner) &&
-                                // We are invoking an attribute on the crate root, or an outline
-                                // module
-                                (item_inner.ident.name.is_empty() || !matches!(mod_kind, ast::ModKind::Loaded(_, Inline::Yes, _)));
-                        }
-                    }
-                    let tokens = if fake_tokens {
-                        rustc_parse::fake_token_stream(
+                    let tokens = match &item {
+                        // FIXME: Collect tokens and use them instead of generating
+                        // fake ones. These are unstable, so it needs to be
+                        // fixed prior to stabilization
+                        // Fake tokens when we are invoking an inner attribute, and
+                        // we are invoking it on an out-of-line module or crate.
+                        Annotatable::Crate(krate) => rustc_parse::fake_token_stream_for_crate(
                             &self.cx.sess.parse_sess,
-                            &item.into_nonterminal(),
-                        )
-                    } else {
-                        item.into_tokens(&self.cx.sess.parse_sess)
+                            krate,
+                        ),
+                        Annotatable::Item(item_inner)
+                            if matches!(attr.style, AttrStyle::Inner)
+                                && matches!(
+                                    item_inner.kind,
+                                    ItemKind::Mod(
+                                        _,
+                                        ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _),
+                                    )
+                                ) =>
+                        {
+                            rustc_parse::fake_token_stream(
+                                &self.cx.sess.parse_sess,
+                                &item.into_nonterminal(),
+                            )
+                        }
+                        _ => item.into_tokens(&self.cx.sess.parse_sess),
                     };
                     let attr_item = attr.unwrap_normal_item();
                     if let MacArgs::Eq(..) = attr_item.args {
@@ -781,7 +745,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     if let SyntaxExtensionKind::Derive(..) = ext {
                         self.gate_proc_macro_input(&item);
                     }
-                    let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
+                    let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path };
                     let items = match expander.expand(self.cx, span, &meta, item) {
                         ExpandResult::Ready(items) => items,
                         ExpandResult::Retry(item) => {
@@ -804,7 +768,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             Annotatable::Item(_)
             | Annotatable::TraitItem(_)
             | Annotatable::ImplItem(_)
-            | Annotatable::ForeignItem(_) => return,
+            | Annotatable::ForeignItem(_)
+            | Annotatable::Crate(..) => return,
             Annotatable::Stmt(stmt) => {
                 // Attributes are stable on item statements,
                 // but unstable on all other kinds of statements
@@ -842,7 +807,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> {
             fn visit_item(&mut self, item: &'ast ast::Item) {
                 match &item.kind {
-                    ast::ItemKind::Mod(_, mod_kind)
+                    ItemKind::Mod(_, mod_kind)
                         if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) =>
                     {
                         feature_err(
@@ -870,7 +835,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         &mut self,
         toks: TokenStream,
         kind: AstFragmentKind,
-        path: &Path,
+        path: &ast::Path,
         span: Span,
     ) -> AstFragment {
         let mut parser = self.cx.new_parser_from_tts(toks);
@@ -949,19 +914,20 @@ pub fn parse_ast_fragment<'a>(
             RecoverComma::No,
             RecoverColon::Yes,
         )?),
+        AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?),
         AstFragmentKind::Arms
-        | AstFragmentKind::Fields
-        | AstFragmentKind::FieldPats
+        | AstFragmentKind::ExprFields
+        | AstFragmentKind::PatFields
         | AstFragmentKind::GenericParams
         | AstFragmentKind::Params
-        | AstFragmentKind::StructFields
+        | AstFragmentKind::FieldDefs
         | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"),
     })
 }
 
 pub fn ensure_complete_parse<'a>(
     this: &mut Parser<'a>,
-    macro_path: &Path,
+    macro_path: &ast::Path,
     kind_name: &str,
     span: Span,
 ) {
@@ -996,6 +962,581 @@ pub fn ensure_complete_parse<'a>(
     }
 }
 
+/// Wraps a call to `noop_visit_*` / `noop_flat_map_*`
+/// for an AST node that supports attributes
+/// (see the `Annotatable` enum)
+/// This method assigns a `NodeId`, and sets that `NodeId`
+/// as our current 'lint node id'. If a macro call is found
+/// inside this AST node, we will use this AST node's `NodeId`
+/// to emit lints associated with that macro (allowing
+/// `#[allow]` / `#[deny]` to be applied close to
+/// the macro invocation).
+///
+/// Do *not* call this for a macro AST node
+/// (e.g. `ExprKind::MacCall`) - we cannot emit lints
+/// at these AST nodes, since they are removed and
+/// replaced with the result of macro expansion.
+///
+/// All other `NodeId`s are assigned by `visit_id`.
+/// * `self` is the 'self' parameter for the current method,
+/// * `id` is a mutable reference to the `NodeId` field
+///    of the current AST node.
+/// * `closure` is a closure that executes the
+///   `noop_visit_*` / `noop_flat_map_*` method
+///   for the current AST node.
+macro_rules! assign_id {
+    ($self:ident, $id:expr, $closure:expr) => {{
+        let old_id = $self.cx.current_expansion.lint_node_id;
+        if $self.monotonic {
+            debug_assert_eq!(*$id, ast::DUMMY_NODE_ID);
+            let new_id = $self.cx.resolver.next_node_id();
+            *$id = new_id;
+            $self.cx.current_expansion.lint_node_id = new_id;
+        }
+        let ret = ($closure)();
+        $self.cx.current_expansion.lint_node_id = old_id;
+        ret
+    }};
+}
+
+enum AddSemicolon {
+    Yes,
+    No,
+}
+
+/// A trait implemented for all `AstFragment` nodes and providing all pieces
+/// of functionality used by `InvocationCollector`.
+trait InvocationCollectorNode: AstLike {
+    type OutputTy = SmallVec<[Self; 1]>;
+    type AttrsTy: Deref<Target = [ast::Attribute]> = Vec<ast::Attribute>;
+    const KIND: AstFragmentKind;
+    fn to_annotatable(self) -> Annotatable;
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy;
+    fn id(&mut self) -> &mut NodeId;
+    fn descr() -> &'static str {
+        unreachable!()
+    }
+    fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
+        unreachable!()
+    }
+    fn noop_visit<V: MutVisitor>(&mut self, _visitor: &mut V) {
+        unreachable!()
+    }
+    fn is_mac_call(&self) -> bool {
+        false
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        unreachable!()
+    }
+    fn pre_flat_map_node_collect_attr(_cfg: &StripUnconfigured<'_>, _attr: &ast::Attribute) {}
+    fn post_flat_map_node_collect_bang(_output: &mut Self::OutputTy, _add_semicolon: AddSemicolon) {
+    }
+    fn wrap_flat_map_node_noop_flat_map(
+        node: Self,
+        collector: &mut InvocationCollector<'_, '_>,
+        noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
+    ) -> Result<Self::OutputTy, Self> {
+        Ok(noop_flat_map(node, collector))
+    }
+}
+
+impl InvocationCollectorNode for P<ast::Item> {
+    const KIND: AstFragmentKind = AstFragmentKind::Items;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Item(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_items()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_item(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.kind, ItemKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.into_inner();
+        match node.kind {
+            ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+    fn wrap_flat_map_node_noop_flat_map(
+        mut node: Self,
+        collector: &mut InvocationCollector<'_, '_>,
+        noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
+    ) -> Result<Self::OutputTy, Self> {
+        if !matches!(node.kind, ItemKind::Mod(..)) {
+            return Ok(noop_flat_map(node, collector));
+        }
+
+        // Work around borrow checker not seeing through `P`'s deref.
+        let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs));
+        let ItemKind::Mod(_, mod_kind) = &mut node.kind else {
+            unreachable!()
+        };
+
+        let ecx = &mut collector.cx;
+        let (file_path, dir_path, dir_ownership) = match mod_kind {
+            ModKind::Loaded(_, inline, _) => {
+                // Inline `mod foo { ... }`, but we still need to push directories.
+                let (dir_path, dir_ownership) = mod_dir_path(
+                    &ecx.sess,
+                    ident,
+                    &attrs,
+                    &ecx.current_expansion.module,
+                    ecx.current_expansion.dir_ownership,
+                    *inline,
+                );
+                node.attrs = attrs;
+                (None, dir_path, dir_ownership)
+            }
+            ModKind::Unloaded => {
+                // We have an outline `mod foo;` so we need to parse the file.
+                let old_attrs_len = attrs.len();
+                let ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership } =
+                    parse_external_mod(
+                        &ecx.sess,
+                        ident,
+                        span,
+                        &ecx.current_expansion.module,
+                        ecx.current_expansion.dir_ownership,
+                        &mut attrs,
+                    );
+
+                if let Some(lint_store) = ecx.lint_store {
+                    lint_store.pre_expansion_lint(
+                        ecx.sess,
+                        ecx.resolver.registered_tools(),
+                        ecx.current_expansion.lint_node_id,
+                        &attrs,
+                        &items,
+                        ident.name.as_str(),
+                    );
+                }
+
+                *mod_kind = ModKind::Loaded(items, Inline::No, inner_span);
+                node.attrs = attrs;
+                if node.attrs.len() > old_attrs_len {
+                    // If we loaded an out-of-line module and added some inner attributes,
+                    // then we need to re-configure it and re-collect attributes for
+                    // resolution and expansion.
+                    return Err(node);
+                }
+                (Some(file_path), dir_path, dir_ownership)
+            }
+        };
+
+        // Set the module info before we flat map.
+        let mut module = ecx.current_expansion.module.with_dir_path(dir_path);
+        module.mod_path.push(ident);
+        if let Some(file_path) = file_path {
+            module.file_path_stack.push(file_path);
+        }
+
+        let orig_module = mem::replace(&mut ecx.current_expansion.module, Rc::new(module));
+        let orig_dir_ownership =
+            mem::replace(&mut ecx.current_expansion.dir_ownership, dir_ownership);
+
+        let res = Ok(noop_flat_map(node, collector));
+
+        collector.cx.current_expansion.dir_ownership = orig_dir_ownership;
+        collector.cx.current_expansion.module = orig_module;
+        res
+    }
+}
+
+struct TraitItemTag;
+impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> {
+    type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
+    const KIND: AstFragmentKind = AstFragmentKind::TraitItems;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::TraitItem(self.wrapped)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_trait_items()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.wrapped.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_assoc_item(self.wrapped, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let item = self.wrapped.into_inner();
+        match item.kind {
+            AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+struct ImplItemTag;
+impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> {
+    type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
+    const KIND: AstFragmentKind = AstFragmentKind::ImplItems;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::ImplItem(self.wrapped)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_impl_items()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.wrapped.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_assoc_item(self.wrapped, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let item = self.wrapped.into_inner();
+        match item.kind {
+            AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl InvocationCollectorNode for P<ast::ForeignItem> {
+    const KIND: AstFragmentKind = AstFragmentKind::ForeignItems;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::ForeignItem(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_foreign_items()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_foreign_item(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.kind, ForeignItemKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.into_inner();
+        match node.kind {
+            ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl InvocationCollectorNode for ast::Variant {
+    const KIND: AstFragmentKind = AstFragmentKind::Variants;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Variant(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_variants()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_variant(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::FieldDef {
+    const KIND: AstFragmentKind = AstFragmentKind::FieldDefs;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::FieldDef(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_field_defs()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_field_def(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::PatField {
+    const KIND: AstFragmentKind = AstFragmentKind::PatFields;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::PatField(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_pat_fields()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_pat_field(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::ExprField {
+    const KIND: AstFragmentKind = AstFragmentKind::ExprFields;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::ExprField(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_expr_fields()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_expr_field(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::Param {
+    const KIND: AstFragmentKind = AstFragmentKind::Params;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Param(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_params()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_param(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::GenericParam {
+    const KIND: AstFragmentKind = AstFragmentKind::GenericParams;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::GenericParam(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_generic_params()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_generic_param(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::Arm {
+    const KIND: AstFragmentKind = AstFragmentKind::Arms;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Arm(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_arms()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_arm(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for ast::Stmt {
+    type AttrsTy = ast::AttrVec;
+    const KIND: AstFragmentKind = AstFragmentKind::Stmts;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Stmt(P(self))
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_stmts()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
+        noop_flat_map_stmt(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        match &self.kind {
+            StmtKind::MacCall(..) => true,
+            StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)),
+            StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)),
+            StmtKind::Expr(..) => unreachable!(),
+            StmtKind::Local(..) | StmtKind::Empty => false,
+        }
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        // We pull macro invocations (both attributes and fn-like macro calls) out of their
+        // `StmtKind`s and treat them as statement macro invocations, not as items or expressions.
+        let (add_semicolon, mac, attrs) = match self.kind {
+            StmtKind::MacCall(mac) => {
+                let ast::MacCallStmt { mac, style, attrs, .. } = mac.into_inner();
+                (style == MacStmtStyle::Semicolon, mac, attrs)
+            }
+            StmtKind::Item(item) => match item.into_inner() {
+                ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => {
+                    (mac.args.need_semicolon(), mac, attrs.into())
+                }
+                _ => unreachable!(),
+            },
+            StmtKind::Semi(expr) => match expr.into_inner() {
+                ast::Expr { kind: ExprKind::MacCall(mac), attrs, .. } => {
+                    (mac.args.need_semicolon(), mac, attrs)
+                }
+                _ => unreachable!(),
+            },
+            _ => unreachable!(),
+        };
+        (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No })
+    }
+    fn post_flat_map_node_collect_bang(stmts: &mut Self::OutputTy, add_semicolon: AddSemicolon) {
+        // If this is a macro invocation with a semicolon, then apply that
+        // semicolon to the final statement produced by expansion.
+        if matches!(add_semicolon, AddSemicolon::Yes) {
+            if let Some(stmt) = stmts.pop() {
+                stmts.push(stmt.add_trailing_semicolon());
+            }
+        }
+    }
+}
+
+impl InvocationCollectorNode for ast::Crate {
+    type OutputTy = ast::Crate;
+    const KIND: AstFragmentKind = AstFragmentKind::Crate;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Crate(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_crate()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
+        noop_visit_crate(self, visitor)
+    }
+}
+
+impl InvocationCollectorNode for P<ast::Ty> {
+    type OutputTy = P<ast::Ty>;
+    const KIND: AstFragmentKind = AstFragmentKind::Ty;
+    fn to_annotatable(self) -> Annotatable {
+        unreachable!()
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_ty()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
+        noop_visit_ty(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.kind, ast::TyKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.into_inner();
+        match node.kind {
+            TyKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl InvocationCollectorNode for P<ast::Pat> {
+    type OutputTy = P<ast::Pat>;
+    const KIND: AstFragmentKind = AstFragmentKind::Pat;
+    fn to_annotatable(self) -> Annotatable {
+        unreachable!()
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_pat()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
+        noop_visit_pat(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.kind, PatKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.into_inner();
+        match node.kind {
+            PatKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl InvocationCollectorNode for P<ast::Expr> {
+    type OutputTy = P<ast::Expr>;
+    type AttrsTy = ast::AttrVec;
+    const KIND: AstFragmentKind = AstFragmentKind::Expr;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Expr(self)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_expr()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.id
+    }
+    fn descr() -> &'static str {
+        "an expression"
+    }
+    fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
+        noop_visit_expr(self, visitor)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.kind, ExprKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.into_inner();
+        match node.kind {
+            ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+}
+
+struct OptExprTag;
+impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> {
+    type OutputTy = Option<P<ast::Expr>>;
+    type AttrsTy = ast::AttrVec;
+    const KIND: AstFragmentKind = AstFragmentKind::OptExpr;
+    fn to_annotatable(self) -> Annotatable {
+        Annotatable::Expr(self.wrapped)
+    }
+    fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
+        fragment.make_opt_expr()
+    }
+    fn id(&mut self) -> &mut NodeId {
+        &mut self.wrapped.id
+    }
+    fn noop_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy {
+        noop_visit_expr(&mut self.wrapped, visitor);
+        Some(self.wrapped)
+    }
+    fn is_mac_call(&self) -> bool {
+        matches!(self.wrapped.kind, ast::ExprKind::MacCall(..))
+    }
+    fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) {
+        let node = self.wrapped.into_inner();
+        match node.kind {
+            ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
+            _ => unreachable!(),
+        }
+    }
+    fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attribute) {
+        cfg.maybe_emit_expr_attr_err(&attr);
+    }
+}
+
 struct InvocationCollector<'a, 'b> {
     cx: &'a mut ExtCtxt<'b>,
     cfg: StripUnconfigured<'a>,
@@ -1031,7 +1572,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
     fn collect_attr(
         &mut self,
-        (attr, pos, derives): (ast::Attribute, usize, Vec<Path>),
+        (attr, pos, derives): (ast::Attribute, usize, Vec<ast::Path>),
         item: Annotatable,
         kind: AstFragmentKind,
     ) -> AstFragment {
@@ -1042,18 +1583,33 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// its position and derives following it. We have to collect the derives in order to resolve
     /// legacy derive helpers (helpers written before derives that introduce them).
     fn take_first_attr(
-        &mut self,
+        &self,
         item: &mut impl AstLike,
-    ) -> Option<(ast::Attribute, usize, Vec<Path>)> {
+    ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
         let mut attr = None;
 
+        let mut cfg_pos = None;
+        let mut attr_pos = None;
+        for (pos, attr) in item.attrs().iter().enumerate() {
+            if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) {
+                let name = attr.ident().map(|ident| ident.name);
+                if name == Some(sym::cfg) || name == Some(sym::cfg_attr) {
+                    cfg_pos = Some(pos); // a cfg attr found, no need to search anymore
+                    break;
+                } else if attr_pos.is_none()
+                    && !name.map_or(false, rustc_feature::is_builtin_attr_name)
+                {
+                    attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr
+                }
+            }
+        }
+
         item.visit_attrs(|attrs| {
-            attr = attrs
-                .iter()
-                .position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a))
-                .map(|attr_pos| {
-                    let attr = attrs.remove(attr_pos);
-                    let following_derives = attrs[attr_pos..]
+            attr = Some(match (cfg_pos, attr_pos) {
+                (Some(pos), _) => (attrs.remove(pos), pos, Vec::new()),
+                (_, Some(pos)) => {
+                    let attr = attrs.remove(pos);
+                    let following_derives = attrs[pos..]
                         .iter()
                         .filter(|a| a.has_name(sym::derive))
                         .flat_map(|a| a.meta_item_list().unwrap_or_default())
@@ -1067,52 +1623,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                         })
                         .collect();
 
-                    (attr, attr_pos, following_derives)
-                })
+                    (attr, pos, following_derives)
+                }
+                _ => return,
+            });
         });
 
         attr
     }
 
-    fn take_stmt_bang(
-        &mut self,
-        stmt: ast::Stmt,
-    ) -> Result<(bool, MacCall, Vec<ast::Attribute>), ast::Stmt> {
-        match stmt.kind {
-            StmtKind::MacCall(mac) => {
-                let MacCallStmt { mac, style, attrs, .. } = mac.into_inner();
-                Ok((style == MacStmtStyle::Semicolon, mac, attrs.into()))
-            }
-            StmtKind::Item(item) if matches!(item.kind, ItemKind::MacCall(..)) => {
-                match item.into_inner() {
-                    ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => {
-                        Ok((mac.args.need_semicolon(), mac, attrs))
-                    }
-                    _ => unreachable!(),
-                }
-            }
-            StmtKind::Semi(expr) if matches!(expr.kind, ast::ExprKind::MacCall(..)) => {
-                match expr.into_inner() {
-                    ast::Expr { kind: ast::ExprKind::MacCall(mac), attrs, .. } => {
-                        Ok((mac.args.need_semicolon(), mac, attrs.into()))
-                    }
-                    _ => unreachable!(),
-                }
-            }
-            StmtKind::Local(..) | StmtKind::Empty | StmtKind::Item(..) | StmtKind::Semi(..) => {
-                Err(stmt)
-            }
-            StmtKind::Expr(..) => unreachable!(),
-        }
-    }
-
-    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
-        self.cfg.configure(node)
-    }
-
     // Detect use of feature-gated or invalid attributes on macro invocations
     // since they will not be detected after macro expansion.
-    fn check_attributes(&self, attrs: &[ast::Attribute], call: &MacCall) {
+    fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
         let features = self.cx.ecfg.features.unwrap();
         let mut attrs = attrs.iter().peekable();
         let mut span: Option<Span> = None;
@@ -1155,483 +1677,231 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
             }
         }
     }
-}
 
-/// Wraps a call to `noop_visit_*` / `noop_flat_map_*`
-/// for an AST node that supports attributes
-/// (see the `Annotatable` enum)
-/// This method assigns a `NodeId`, and sets that `NodeId`
-/// as our current 'lint node id'. If a macro call is found
-/// inside this AST node, we will use this AST node's `NodeId`
-/// to emit lints associated with that macro (allowing
-/// `#[allow]` / `#[deny]` to be applied close to
-/// the macro invocation).
-///
-/// Do *not* call this for a macro AST node
-/// (e.g. `ExprKind::MacCall`) - we cannot emit lints
-/// at these AST nodes, since they are removed and
-/// replaced with the result of macro expansion.
-///
-/// All other `NodeId`s are assigned by `visit_id`.
-/// * `self` is the 'self' parameter for the current method,
-/// * `id` is a mutable reference to the `NodeId` field
-///    of the current AST node.
-/// * `closure` is a closure that executes the
-///   `noop_visit_*` / `noop_flat_map_*` method
-///   for the current AST node.
-macro_rules! assign_id {
-    ($self:ident, $id:expr, $closure:expr) => {{
-        let old_id = $self.cx.current_expansion.lint_node_id;
-        if $self.monotonic {
-            debug_assert_eq!(*$id, ast::DUMMY_NODE_ID);
-            let new_id = $self.cx.resolver.next_node_id();
-            *$id = new_id;
-            $self.cx.current_expansion.lint_node_id = new_id;
+    fn expand_cfg_true(
+        &mut self,
+        node: &mut impl AstLike,
+        attr: ast::Attribute,
+        pos: usize,
+    ) -> bool {
+        let res = self.cfg.cfg_true(&attr);
+        if res {
+            // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
+            // and some tools like rustdoc and clippy rely on that. Find a way to remove them
+            // while keeping the tools working.
+            self.cx.expanded_inert_attrs.mark(&attr);
+            node.visit_attrs(|attrs| attrs.insert(pos, attr));
         }
-        let ret = ($closure)();
-        $self.cx.current_expansion.lint_node_id = old_id;
-        ret
-    }};
-}
-
-impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
-    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
-        self.cfg.configure_expr(expr);
-        visit_clobber(expr.deref_mut(), |mut expr| {
-            if let Some(attr) = self.take_first_attr(&mut expr) {
-                // Collect the invoc regardless of whether or not attributes are permitted here
-                // expansion will eat the attribute so it won't error later.
-                self.cfg.maybe_emit_expr_attr_err(&attr.0);
-
-                // AstFragmentKind::Expr requires the macro to emit an expression.
-                return self
-                    .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::Expr)
-                    .make_expr()
-                    .into_inner();
-            }
+        res
+    }
 
-            if let ast::ExprKind::MacCall(mac) = expr.kind {
-                self.check_attributes(&expr.attrs, &mac);
-                self.collect_bang(mac, AstFragmentKind::Expr).make_expr().into_inner()
-            } else {
-                assign_id!(self, &mut expr.id, || {
-                    ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
-                });
-                expr
-            }
+    fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) {
+        node.visit_attrs(|attrs| {
+            attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false));
         });
     }
 
-    fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
-        let mut arm = configure!(self, arm);
-
-        if let Some(attr) = self.take_first_attr(&mut arm) {
-            return self
-                .collect_attr(attr, Annotatable::Arm(arm), AstFragmentKind::Arms)
-                .make_arms();
+    fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
+        &mut self,
+        mut node: Node,
+    ) -> Node::OutputTy {
+        loop {
+            return match self.take_first_attr(&mut node) {
+                Some((attr, pos, derives)) => match attr.name_or_empty() {
+                    sym::cfg => {
+                        if self.expand_cfg_true(&mut node, attr, pos) {
+                            continue;
+                        }
+                        Default::default()
+                    }
+                    sym::cfg_attr => {
+                        self.expand_cfg_attr(&mut node, attr, pos);
+                        continue;
+                    }
+                    _ => {
+                        Node::pre_flat_map_node_collect_attr(&self.cfg, &attr);
+                        self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
+                            .make_ast::<Node>()
+                    }
+                },
+                None if node.is_mac_call() => {
+                    let (mac, attrs, add_semicolon) = node.take_mac_call();
+                    self.check_attributes(&attrs, &mac);
+                    let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>();
+                    Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
+                    res
+                }
+                None => {
+                    match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
+                        assign_id!(this, node.id(), || node.noop_flat_map(this))
+                    }) {
+                        Ok(output) => output,
+                        Err(returned_node) => {
+                            node = returned_node;
+                            continue;
+                        }
+                    }
+                }
+            };
         }
-
-        assign_id!(self, &mut arm.id, || noop_flat_map_arm(arm, self))
     }
 
-    fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
-        let mut field = configure!(self, field);
-
-        if let Some(attr) = self.take_first_attr(&mut field) {
-            return self
-                .collect_attr(attr, Annotatable::ExprField(field), AstFragmentKind::Fields)
-                .make_expr_fields();
+    fn visit_node<Node: InvocationCollectorNode<OutputTy = Node> + DummyAstNode>(
+        &mut self,
+        node: &mut Node,
+    ) {
+        loop {
+            return match self.take_first_attr(node) {
+                Some((attr, pos, derives)) => match attr.name_or_empty() {
+                    sym::cfg => {
+                        let span = attr.span;
+                        if self.expand_cfg_true(node, attr, pos) {
+                            continue;
+                        }
+                        let msg =
+                            format!("removing {} is not supported in this position", Node::descr());
+                        self.cx.span_err(span, &msg);
+                        continue;
+                    }
+                    sym::cfg_attr => {
+                        self.expand_cfg_attr(node, attr, pos);
+                        continue;
+                    }
+                    _ => visit_clobber(node, |node| {
+                        self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
+                            .make_ast::<Node>()
+                    }),
+                },
+                None if node.is_mac_call() => {
+                    visit_clobber(node, |node| {
+                        // Do not clobber unless it's actually a macro (uncommon case).
+                        let (mac, attrs, _) = node.take_mac_call();
+                        self.check_attributes(&attrs, &mac);
+                        self.collect_bang(mac, Node::KIND).make_ast::<Node>()
+                    })
+                }
+                None => {
+                    assign_id!(self, node.id(), || node.noop_visit(self))
+                }
+            };
         }
-
-        assign_id!(self, &mut field.id, || noop_flat_map_expr_field(field, self))
     }
+}
 
-    fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
-        let mut fp = configure!(self, fp);
-
-        if let Some(attr) = self.take_first_attr(&mut fp) {
-            return self
-                .collect_attr(attr, Annotatable::PatField(fp), AstFragmentKind::FieldPats)
-                .make_pat_fields();
-        }
-
-        assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self))
+impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
+    fn flat_map_item(&mut self, node: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
+        self.flat_map_node(node)
     }
 
-    fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
-        let mut p = configure!(self, p);
-
-        if let Some(attr) = self.take_first_attr(&mut p) {
-            return self
-                .collect_attr(attr, Annotatable::Param(p), AstFragmentKind::Params)
-                .make_params();
-        }
-
-        assign_id!(self, &mut p.id, || noop_flat_map_param(p, self))
+    fn flat_map_trait_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag))
     }
 
-    fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
-        let mut sf = configure!(self, sf);
-
-        if let Some(attr) = self.take_first_attr(&mut sf) {
-            return self
-                .collect_attr(attr, Annotatable::FieldDef(sf), AstFragmentKind::StructFields)
-                .make_field_defs();
-        }
-
-        assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self))
+    fn flat_map_impl_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag))
     }
 
-    fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
-        let mut variant = configure!(self, variant);
-
-        if let Some(attr) = self.take_first_attr(&mut variant) {
-            return self
-                .collect_attr(attr, Annotatable::Variant(variant), AstFragmentKind::Variants)
-                .make_variants();
-        }
+    fn flat_map_foreign_item(
+        &mut self,
+        node: P<ast::ForeignItem>,
+    ) -> SmallVec<[P<ast::ForeignItem>; 1]> {
+        self.flat_map_node(node)
+    }
 
-        assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self))
+    fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
+        self.flat_map_node(node)
     }
 
-    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        let expr = configure!(self, expr);
-        expr.filter_map(|mut expr| {
-            if let Some(attr) = self.take_first_attr(&mut expr) {
-                self.cfg.maybe_emit_expr_attr_err(&attr.0);
+    fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
+        self.flat_map_node(node)
+    }
 
-                return self
-                    .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr)
-                    .make_opt_expr()
-                    .map(|expr| expr.into_inner());
-            }
+    fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
+        self.flat_map_node(node)
+    }
 
-            if let ast::ExprKind::MacCall(mac) = expr.kind {
-                self.check_attributes(&expr.attrs, &mac);
-                self.collect_bang(mac, AstFragmentKind::OptExpr)
-                    .make_opt_expr()
-                    .map(|expr| expr.into_inner())
-            } else {
-                assign_id!(self, &mut expr.id, || {
-                    Some({
-                        noop_visit_expr(&mut expr, self);
-                        expr
-                    })
-                })
-            }
-        })
+    fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
+        self.flat_map_node(node)
     }
 
-    fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
-        match pat.kind {
-            PatKind::MacCall(_) => {}
-            _ => return noop_visit_pat(pat, self),
-        }
+    fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> {
+        self.flat_map_node(node)
+    }
 
-        visit_clobber(pat, |mut pat| match mem::replace(&mut pat.kind, PatKind::Wild) {
-            PatKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Pat).make_pat(),
-            _ => unreachable!(),
-        });
+    fn flat_map_generic_param(
+        &mut self,
+        node: ast::GenericParam,
+    ) -> SmallVec<[ast::GenericParam; 1]> {
+        self.flat_map_node(node)
     }
 
-    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
-        let mut stmt = configure!(self, stmt);
+    fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
+        self.flat_map_node(node)
+    }
 
-        // We pull macro invocations (both attributes and fn-like macro calls) out of their
-        // `StmtKind`s and treat them as statement macro invocations, not as items or expressions.
+    fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
         // FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
         // changing that requires some compatibility measures.
-        let mut stmt = if !stmt.is_expr() {
-            if let Some(attr) = self.take_first_attr(&mut stmt) {
-                return self
-                    .collect_attr(attr, Annotatable::Stmt(P(stmt)), AstFragmentKind::Stmts)
-                    .make_stmts();
-            }
-
-            match self.take_stmt_bang(stmt) {
-                Ok((add_semicolon, mac, attrs)) => {
-                    self.check_attributes(&attrs, &mac);
-                    let mut stmts = self.collect_bang(mac, AstFragmentKind::Stmts).make_stmts();
-
-                    // If this is a macro invocation with a semicolon, then apply that
-                    // semicolon to the final statement produced by expansion.
-                    if add_semicolon {
-                        if let Some(stmt) = stmts.pop() {
-                            stmts.push(stmt.add_trailing_semicolon());
-                        }
-                    }
-
-                    return stmts;
+        if node.is_expr() {
+            // The only way that we can end up with a `MacCall` expression statement,
+            // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
+            // traiing expression in a block (e.g. `fn foo() { my_macro!() }`).
+            // Record this information, so that we can report a more specific
+            // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
+            // See #78991 for an investigation of treating macros in this position
+            // as statements, rather than expressions, during parsing.
+            return match &node.kind {
+                StmtKind::Expr(expr)
+                    if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) =>
+                {
+                    self.cx.current_expansion.is_trailing_mac = true;
+                    // Don't use `assign_id` for this statement - it may get removed
+                    // entirely due to a `#[cfg]` on the contained expression
+                    let res = noop_flat_map_stmt(node, self);
+                    self.cx.current_expansion.is_trailing_mac = false;
+                    res
                 }
-                Err(stmt) => stmt,
-            }
-        } else {
-            stmt
-        };
-
-        // The only way that we can end up with a `MacCall` expression statement,
-        // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
-        // traiing expression in a block (e.g. `fn foo() { my_macro!() }`).
-        // Record this information, so that we can report a more specific
-        // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
-        // See #78991 for an investigation of treating macros in this position
-        // as statements, rather than expressions, during parsing.
-        let res = match &stmt.kind {
-            StmtKind::Expr(expr)
-                if matches!(**expr, ast::Expr { kind: ast::ExprKind::MacCall(..), .. }) =>
-            {
-                self.cx.current_expansion.is_trailing_mac = true;
-                // Don't use `assign_id` for this statement - it may get removed
-                // entirely due to a `#[cfg]` on the contained expression
-                noop_flat_map_stmt(stmt, self)
-            }
-            _ => assign_id!(self, &mut stmt.id, || noop_flat_map_stmt(stmt, self)),
-        };
-        self.cx.current_expansion.is_trailing_mac = false;
-        res
-    }
-
-    fn visit_block(&mut self, block: &mut P<Block>) {
-        let orig_dir_ownership = mem::replace(
-            &mut self.cx.current_expansion.dir_ownership,
-            DirOwnership::UnownedViaBlock,
-        );
-        noop_visit_block(block, self);
-        self.cx.current_expansion.dir_ownership = orig_dir_ownership;
-    }
-
-    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
-        let mut item = configure!(self, item);
-
-        if let Some(attr) = self.take_first_attr(&mut item) {
-            return self
-                .collect_attr(attr, Annotatable::Item(item), AstFragmentKind::Items)
-                .make_items();
+                _ => assign_id!(self, &mut node.id, || noop_flat_map_stmt(node, self)),
+            };
         }
 
-        let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck.
-        let ident = item.ident;
-        let span = item.span;
-
-        match item.kind {
-            ast::ItemKind::MacCall(ref mac) => {
-                self.check_attributes(&attrs, &mac);
-                item.attrs = attrs;
-                item.and_then(|item| match item.kind {
-                    ItemKind::MacCall(mac) => {
-                        self.collect_bang(mac, AstFragmentKind::Items).make_items()
-                    }
-                    _ => unreachable!(),
-                })
-            }
-            ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::empty() => {
-                let (file_path, dir_path, dir_ownership) = match mod_kind {
-                    ModKind::Loaded(_, inline, _) => {
-                        // Inline `mod foo { ... }`, but we still need to push directories.
-                        let (dir_path, dir_ownership) = mod_dir_path(
-                            &self.cx.sess,
-                            ident,
-                            &attrs,
-                            &self.cx.current_expansion.module,
-                            self.cx.current_expansion.dir_ownership,
-                            *inline,
-                        );
-                        item.attrs = attrs;
-                        (None, dir_path, dir_ownership)
-                    }
-                    ModKind::Unloaded => {
-                        // We have an outline `mod foo;` so we need to parse the file.
-                        let old_attrs_len = attrs.len();
-                        let ParsedExternalMod {
-                            mut items,
-                            inner_span,
-                            file_path,
-                            dir_path,
-                            dir_ownership,
-                        } = parse_external_mod(
-                            &self.cx.sess,
-                            ident,
-                            span,
-                            &self.cx.current_expansion.module,
-                            self.cx.current_expansion.dir_ownership,
-                            &mut attrs,
-                        );
-
-                        if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded {
-                            (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span);
-                        }
-
-                        *mod_kind = ModKind::Loaded(items, Inline::No, inner_span);
-                        item.attrs = attrs;
-                        if item.attrs.len() > old_attrs_len {
-                            // If we loaded an out-of-line module and added some inner attributes,
-                            // then we need to re-configure it and re-collect attributes for
-                            // resolution and expansion.
-                            item = configure!(self, item);
-
-                            if let Some(attr) = self.take_first_attr(&mut item) {
-                                return self
-                                    .collect_attr(
-                                        attr,
-                                        Annotatable::Item(item),
-                                        AstFragmentKind::Items,
-                                    )
-                                    .make_items();
-                            }
-                        }
-                        (Some(file_path), dir_path, dir_ownership)
-                    }
-                };
-
-                // Set the module info before we flat map.
-                let mut module = self.cx.current_expansion.module.with_dir_path(dir_path);
-                module.mod_path.push(ident);
-                if let Some(file_path) = file_path {
-                    module.file_path_stack.push(file_path);
-                }
-
-                let orig_module =
-                    mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
-                let orig_dir_ownership =
-                    mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership);
-
-                let result = assign_id!(self, &mut item.id, || noop_flat_map_item(item, self));
-
-                // Restore the module info.
-                self.cx.current_expansion.dir_ownership = orig_dir_ownership;
-                self.cx.current_expansion.module = orig_module;
-
-                result
-            }
-            _ => {
-                item.attrs = attrs;
-                // The crate root is special - don't assign an ID to it.
-                if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::empty()) {
-                    assign_id!(self, &mut item.id, || noop_flat_map_item(item, self))
-                } else {
-                    noop_flat_map_item(item, self)
-                }
-            }
-        }
+        self.flat_map_node(node)
     }
 
-    fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        let mut item = configure!(self, item);
-
-        if let Some(attr) = self.take_first_attr(&mut item) {
-            return self
-                .collect_attr(attr, Annotatable::TraitItem(item), AstFragmentKind::TraitItems)
-                .make_trait_items();
-        }
-
-        match item.kind {
-            ast::AssocItemKind::MacCall(ref mac) => {
-                self.check_attributes(&item.attrs, &mac);
-                item.and_then(|item| match item.kind {
-                    ast::AssocItemKind::MacCall(mac) => {
-                        self.collect_bang(mac, AstFragmentKind::TraitItems).make_trait_items()
-                    }
-                    _ => unreachable!(),
-                })
-            }
-            _ => {
-                assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
-            }
-        }
+    fn visit_crate(&mut self, node: &mut ast::Crate) {
+        self.visit_node(node)
     }
 
-    fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        let mut item = configure!(self, item);
-
-        if let Some(attr) = self.take_first_attr(&mut item) {
-            return self
-                .collect_attr(attr, Annotatable::ImplItem(item), AstFragmentKind::ImplItems)
-                .make_impl_items();
-        }
-
-        match item.kind {
-            ast::AssocItemKind::MacCall(ref mac) => {
-                self.check_attributes(&item.attrs, &mac);
-                item.and_then(|item| match item.kind {
-                    ast::AssocItemKind::MacCall(mac) => {
-                        self.collect_bang(mac, AstFragmentKind::ImplItems).make_impl_items()
-                    }
-                    _ => unreachable!(),
-                })
-            }
-            _ => {
-                assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
-            }
-        }
+    fn visit_ty(&mut self, node: &mut P<ast::Ty>) {
+        self.visit_node(node)
     }
 
-    fn visit_ty(&mut self, ty: &mut P<ast::Ty>) {
-        match ty.kind {
-            ast::TyKind::MacCall(_) => {}
-            _ => return noop_visit_ty(ty, self),
-        };
-
-        visit_clobber(ty, |mut ty| match mem::replace(&mut ty.kind, ast::TyKind::Err) {
-            ast::TyKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Ty).make_ty(),
-            _ => unreachable!(),
-        });
+    fn visit_pat(&mut self, node: &mut P<ast::Pat>) {
+        self.visit_node(node)
     }
 
-    fn flat_map_foreign_item(
-        &mut self,
-        foreign_item: P<ast::ForeignItem>,
-    ) -> SmallVec<[P<ast::ForeignItem>; 1]> {
-        let mut foreign_item = configure!(self, foreign_item);
-
-        if let Some(attr) = self.take_first_attr(&mut foreign_item) {
-            return self
-                .collect_attr(
-                    attr,
-                    Annotatable::ForeignItem(foreign_item),
-                    AstFragmentKind::ForeignItems,
-                )
-                .make_foreign_items();
-        }
-
-        match foreign_item.kind {
-            ast::ForeignItemKind::MacCall(ref mac) => {
-                self.check_attributes(&foreign_item.attrs, &mac);
-                foreign_item.and_then(|item| match item.kind {
-                    ast::ForeignItemKind::MacCall(mac) => {
-                        self.collect_bang(mac, AstFragmentKind::ForeignItems).make_foreign_items()
-                    }
-                    _ => unreachable!(),
-                })
-            }
-            _ => {
-                assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item(
-                    foreign_item,
-                    self
-                ))
-            }
+    fn visit_expr(&mut self, node: &mut P<ast::Expr>) {
+        // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`.
+        if let Some(attr) = node.attrs.first() {
+            self.cfg.maybe_emit_expr_attr_err(attr);
         }
+        self.visit_node(node)
     }
 
-    fn flat_map_generic_param(
-        &mut self,
-        param: ast::GenericParam,
-    ) -> SmallVec<[ast::GenericParam; 1]> {
-        let mut param = configure!(self, param);
-
-        if let Some(attr) = self.take_first_attr(&mut param) {
-            return self
-                .collect_attr(
-                    attr,
-                    Annotatable::GenericParam(param),
-                    AstFragmentKind::GenericParams,
-                )
-                .make_generic_params();
-        }
+    fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        self.flat_map_node(AstLikeWrapper::new(node, OptExprTag))
+    }
 
-        assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self))
+    fn visit_block(&mut self, node: &mut P<ast::Block>) {
+        let orig_dir_ownership = mem::replace(
+            &mut self.cx.current_expansion.dir_ownership,
+            DirOwnership::UnownedViaBlock,
+        );
+        noop_visit_block(node, self);
+        self.cx.current_expansion.dir_ownership = orig_dir_ownership;
     }
 
-    fn visit_id(&mut self, id: &mut ast::NodeId) {
+    fn visit_id(&mut self, id: &mut NodeId) {
         // We may have already assigned a `NodeId`
         // by calling `assign_id`
         if self.monotonic && *id == ast::DUMMY_NODE_ID {
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 521ca2135c6..dfc07da9169 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -1,9 +1,8 @@
+#![feature(associated_type_bounds)]
+#![feature(associated_type_defaults)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
-#![feature(destructuring_assignment)]
-#![feature(format_args_capture)]
 #![feature(if_let_guard)]
-#![feature(iter_zip)]
 #![feature(let_else)]
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_internals)]
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index a7434d73abe..dd82add08dd 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -584,9 +584,7 @@ fn inner_parse_loop<'root, 'tt>(
                 //
                 // At the beginning of the loop, if we reach the end of the delimited submatcher,
                 // we pop the stack to backtrack out of the descent.
-                seq
-                @
-                (TokenTree::Delimited(..)
+                seq @ (TokenTree::Delimited(..)
                 | TokenTree::Token(Token { kind: DocComment(..), .. })) => {
                     let lower_elts = mem::replace(&mut item.top_elts, Tt(seq));
                     let idx = item.idx;
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index f8491654f39..8065911afb9 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -253,7 +253,7 @@ fn generic_extension<'cx>(
     for (i, lhs) in lhses.iter().enumerate() {
         // try each arm's matchers
         let lhs_tt = match *lhs {
-            mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
+            mbe::TokenTree::Delimited(_, ref delim) => &delim.tts,
             _ => cx.span_bug(sp, "malformed macro lhs"),
         };
 
@@ -353,7 +353,7 @@ fn generic_extension<'cx>(
         for lhs in lhses {
             // try each arm's matchers
             let lhs_tt = match *lhs {
-                mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
+                mbe::TokenTree::Delimited(_, ref delim) => &delim.tts,
                 _ => continue,
             };
             if let Success(_) =
@@ -677,11 +677,11 @@ impl FirstSets {
                         first.replace_with(tt.clone());
                     }
                     TokenTree::Delimited(span, ref delimited) => {
-                        build_recur(sets, &delimited.tts[..]);
+                        build_recur(sets, &delimited.tts);
                         first.replace_with(delimited.open_tt(span));
                     }
                     TokenTree::Sequence(sp, ref seq_rep) => {
-                        let subfirst = build_recur(sets, &seq_rep.tts[..]);
+                        let subfirst = build_recur(sets, &seq_rep.tts);
 
                         match sets.first.entry(sp.entire()) {
                             Entry::Vacant(vac) => {
@@ -748,7 +748,7 @@ impl FirstSets {
                     let subfirst = match self.first.get(&sp.entire()) {
                         Some(&Some(ref subfirst)) => subfirst,
                         Some(&None) => {
-                            subfirst_owned = self.first(&seq_rep.tts[..]);
+                            subfirst_owned = self.first(&seq_rep.tts);
                             &subfirst_owned
                         }
                         None => {
@@ -1027,6 +1027,24 @@ fn check_matcher_core(
                                 ),
                             );
                             err.span_label(sp, format!("not allowed after `{}` fragments", kind));
+
+                            if kind == NonterminalKind::PatWithOr
+                                && sess.edition == Edition::Edition2021
+                                && next_token.is_token(&BinOp(token::BinOpToken::Or))
+                            {
+                                let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                                    span,
+                                    name,
+                                    Some(NonterminalKind::PatParam { inferred: false }),
+                                ));
+                                err.span_suggestion(
+                                    span,
+                                    &format!("try a `pat_param` fragment specifier instead"),
+                                    suggestion,
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+
                             let msg = "allowed there are: ";
                             match possible {
                                 &[] => {}
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 88e1623012b..01a7f726617 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -175,12 +175,12 @@ pub(super) fn transcribe<'a>(
                         ));
                     }
 
-                    LockstepIterSize::Contradiction(ref msg) => {
+                    LockstepIterSize::Contradiction(msg) => {
                         // FIXME: this really ought to be caught at macro definition time... It
                         // happens when two meta-variables are used in the same repetition in a
                         // sequence, but they come from different sequence matchers and repeat
                         // different amounts.
-                        return Err(cx.struct_span_err(seq.span(), &msg[..]));
+                        return Err(cx.struct_span_err(seq.span(), &msg));
                     }
 
                     LockstepIterSize::Constraint(len, _) => {
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 1c0b2a9b487..e9532dbe2ce 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -103,10 +103,10 @@ crate fn mod_dir_path(
             if let DirOwnership::Owned { relative } = &mut dir_ownership {
                 if let Some(ident) = relative.take() {
                     // Remove the relative offset.
-                    dir_path.push(&*ident.as_str());
+                    dir_path.push(ident.as_str());
                 }
             }
-            dir_path.push(&*ident.as_str());
+            dir_path.push(ident.as_str());
 
             (dir_path, dir_ownership)
         }
@@ -170,8 +170,8 @@ fn mod_file_path_from_attr(
 ) -> Option<PathBuf> {
     // Extract path string from first `#[path = "path_string"]` attribute.
     let first_path = attrs.iter().find(|at| at.has_name(sym::path))?;
-    let path_string = match first_path.value_str() {
-        Some(s) => s.as_str(),
+    let path_sym = match first_path.value_str() {
+        Some(s) => s,
         None => {
             // This check is here mainly to catch attempting to use a macro,
             // such as #[path = concat!(...)]. This isn't currently supported
@@ -189,14 +189,16 @@ fn mod_file_path_from_attr(
         }
     };
 
+    let path_str = path_sym.as_str();
+
     // On windows, the base path might have the form
     // `\\?\foo\bar` in which case it does not tolerate
     // mixed `/` and `\` separators, so canonicalize
     // `/` to `\`.
     #[cfg(windows)]
-    let path_string = path_string.replace("/", "\\");
+    let path_str = path_str.replace("/", "\\");
 
-    Some(dir_path.join(&*path_string))
+    Some(dir_path.join(path_str))
 }
 
 /// Returns a path to a module.
diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs
index 6402a81e7c1..4a8236b2cf3 100644
--- a/compiler/rustc_expand/src/parse/tests.rs
+++ b/compiler/rustc_expand/src/parse/tests.rs
@@ -65,24 +65,33 @@ fn string_to_tts_macro() {
         let tts: &[TokenTree] = &tts[..];
 
         match tts {
-            [TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), TokenTree::Token(Token { kind: token::Not, .. }), TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), TokenTree::Delimited(_, macro_delim, macro_tts)]
-                if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" =>
-            {
+            [
+                TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }),
+                TokenTree::Token(Token { kind: token::Not, .. }),
+                TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }),
+                TokenTree::Delimited(_, macro_delim, macro_tts),
+            ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => {
                 let tts = &macro_tts.trees().collect::<Vec<_>>();
                 match &tts[..] {
-                    [TokenTree::Delimited(_, first_delim, first_tts), TokenTree::Token(Token { kind: token::FatArrow, .. }), TokenTree::Delimited(_, second_delim, second_tts)]
-                        if macro_delim == &token::Paren =>
-                    {
+                    [
+                        TokenTree::Delimited(_, first_delim, first_tts),
+                        TokenTree::Token(Token { kind: token::FatArrow, .. }),
+                        TokenTree::Delimited(_, second_delim, second_tts),
+                    ] if macro_delim == &token::Paren => {
                         let tts = &first_tts.trees().collect::<Vec<_>>();
                         match &tts[..] {
-                            [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })]
-                                if first_delim == &token::Paren && name.as_str() == "a" => {}
+                            [
+                                TokenTree::Token(Token { kind: token::Dollar, .. }),
+                                TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
+                            ] if first_delim == &token::Paren && name.as_str() == "a" => {}
                             _ => panic!("value 3: {:?} {:?}", first_delim, first_tts),
                         }
                         let tts = &second_tts.trees().collect::<Vec<_>>();
                         match &tts[..] {
-                            [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })]
-                                if second_delim == &token::Paren && name.as_str() == "a" => {}
+                            [
+                                TokenTree::Token(Token { kind: token::Dollar, .. }),
+                                TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
+                            ] if second_delim == &token::Paren && name.as_str() == "a" => {}
                             _ => panic!("value 4: {:?} {:?}", second_delim, second_tts),
                         }
                     }
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 12b6bc7bbe7..af593e92634 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -46,6 +46,13 @@ pub fn placeholder(
         || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None });
 
     match kind {
+        AstFragmentKind::Crate => AstFragment::Crate(ast::Crate {
+            attrs: Default::default(),
+            items: Default::default(),
+            span,
+            id,
+            is_placeholder: true,
+        }),
         AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()),
         AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())),
         AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item {
@@ -116,7 +123,7 @@ pub fn placeholder(
             span,
             is_placeholder: true,
         }]),
-        AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::ExprField {
+        AstFragmentKind::ExprFields => AstFragment::ExprFields(smallvec![ast::ExprField {
             attrs: Default::default(),
             expr: expr_placeholder(),
             id,
@@ -125,7 +132,7 @@ pub fn placeholder(
             span,
             is_placeholder: true,
         }]),
-        AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::PatField {
+        AstFragmentKind::PatFields => AstFragment::PatFields(smallvec![ast::PatField {
             attrs: Default::default(),
             id,
             ident,
@@ -152,7 +159,7 @@ pub fn placeholder(
             ty: ty(),
             is_placeholder: true,
         }]),
-        AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef {
+        AstFragmentKind::FieldDefs => AstFragment::FieldDefs(smallvec![ast::FieldDef {
             attrs: Default::default(),
             id,
             ident: None,
@@ -354,4 +361,12 @@ impl MutVisitor for PlaceholderExpander {
             _ => noop_visit_ty(ty, self),
         }
     }
+
+    fn visit_crate(&mut self, krate: &mut ast::Crate) {
+        if krate.is_placeholder {
+            *krate = self.remove(krate.id).make_crate();
+        } else {
+            noop_visit_crate(krate, self)
+        }
+    }
 }
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index 3f84979ac05..42c17a60a5d 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -24,8 +24,9 @@ impl base::ProcMacro for BangProcMacro {
         span: Span,
         input: TokenStream,
     ) -> Result<TokenStream, ErrorReported> {
+        let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
         let server = proc_macro_server::Rustc::new(ecx);
-        self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
+        self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
             let mut err = ecx.struct_span_err(span, "proc macro panicked");
             if let Some(s) = e.as_str() {
                 err.help(&format!("message: {}", s));
@@ -48,9 +49,10 @@ impl base::AttrProcMacro for AttrProcMacro {
         annotation: TokenStream,
         annotated: TokenStream,
     ) -> Result<TokenStream, ErrorReported> {
+        let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
         let server = proc_macro_server::Rustc::new(ecx);
         self.client
-            .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
+            .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace)
             .map_err(|e| {
                 let mut err = ecx.struct_span_err(span, "custom attribute panicked");
                 if let Some(s) = e.as_str() {
@@ -97,19 +99,19 @@ impl MultiItemModifier for ProcMacroDerive {
             nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
         };
 
+        let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
         let server = proc_macro_server::Rustc::new(ecx);
-        let stream =
-            match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
-                Ok(stream) => stream,
-                Err(e) => {
-                    let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
-                    if let Some(s) = e.as_str() {
-                        err.help(&format!("message: {}", s));
-                    }
-                    err.emit();
-                    return ExpandResult::Ready(vec![]);
+        let stream = match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
+            Ok(stream) => stream,
+            Err(e) => {
+                let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
+                if let Some(s) = e.as_str() {
+                    err.help(&format!("message: {}", s));
                 }
-            };
+                err.emit();
+                return ExpandResult::Ready(vec![]);
+            }
+        };
 
         let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
         let mut parser =
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 5cb97198765..efbe0b65715 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -1,4 +1,4 @@
-use crate::base::{ExtCtxt, ResolverExpand};
+use crate::base::ExtCtxt;
 
 use rustc_ast as ast;
 use rustc_ast::token::{self, Nonterminal, NtIdent};
@@ -7,7 +7,7 @@ use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::Diagnostic;
+use rustc_errors::{Diagnostic, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::lexer::nfc_normalize;
@@ -53,11 +53,11 @@ impl ToInternal<token::DelimToken> for Delimiter {
     }
 }
 
-impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)>
+impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
     for TokenTree<Group, Punct, Ident, Literal>
 {
     fn from_internal(
-        ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_>),
+        ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>),
     ) -> Self {
         use rustc_ast::token::*;
 
@@ -146,10 +146,10 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)>
             SingleQuote => op!('\''),
 
             Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()),
-            Ident(name, is_raw) => tt!(Ident::new(rustc.sess, name, is_raw)),
+            Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)),
             Lifetime(name) => {
                 let ident = symbol::Ident::new(name, span).without_first_quote();
-                stack.push(tt!(Ident::new(rustc.sess, ident.name, false)));
+                stack.push(tt!(Ident::new(rustc.sess(), ident.name, false)));
                 tt!(Punct::new('\'', true))
             }
             Literal(lit) => tt!(Literal { lit }),
@@ -158,7 +158,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)>
                 for ch in data.as_str().chars() {
                     escaped.extend(ch.escape_debug());
                 }
-                let stream = vec![
+                let stream = [
                     Ident(sym::doc, false),
                     Eq,
                     TokenKind::lit(token::Str, Symbol::intern(&escaped), None),
@@ -181,15 +181,15 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)>
             Interpolated(nt)
                 if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, rustc) =>
             {
-                TokenTree::Ident(Ident::new(rustc.sess, name.name, is_raw, name.span))
+                TokenTree::Ident(Ident::new(rustc.sess(), name.name, is_raw, name.span))
             }
             Interpolated(nt) => {
-                let stream = nt_to_tokenstream(&nt, rustc.sess, CanSynthesizeMissingTokens::No);
+                let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No);
                 TokenTree::Group(Group {
                     delimiter: Delimiter::None,
                     stream,
                     span: DelimSpan::from_single(span),
-                    flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess),
+                    flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()),
                 })
             }
 
@@ -221,7 +221,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
                 let integer = TokenKind::lit(token::Integer, symbol, suffix);
                 let a = tokenstream::TokenTree::token(minus, span);
                 let b = tokenstream::TokenTree::token(integer, span);
-                return vec![a, b].into_iter().collect();
+                return [a, b].into_iter().collect();
             }
             TokenTree::Literal(self::Literal {
                 lit: token::Lit { kind: token::Float, symbol, suffix },
@@ -232,7 +232,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
                 let float = TokenKind::lit(token::Float, symbol, suffix);
                 let a = tokenstream::TokenTree::token(minus, span);
                 let b = tokenstream::TokenTree::token(float, span);
-                return vec![a, b].into_iter().collect();
+                return [a, b].into_iter().collect();
             }
             TokenTree::Literal(self::Literal { lit, span }) => {
                 return tokenstream::TokenTree::token(Literal(lit), span).into();
@@ -273,7 +273,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
 impl ToInternal<rustc_errors::Level> for Level {
     fn to_internal(self) -> rustc_errors::Level {
         match self {
-            Level::Error => rustc_errors::Level::Error,
+            Level::Error => rustc_errors::Level::Error { lint: false },
             Level::Warning => rustc_errors::Level::Warning,
             Level::Note => rustc_errors::Level::Note,
             Level::Help => rustc_errors::Level::Help,
@@ -331,9 +331,9 @@ pub struct Ident {
 
 impl Ident {
     fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident {
-        let sym = nfc_normalize(&sym.as_str());
+        let sym = nfc_normalize(sym.as_str());
         let string = sym.as_str();
-        if !rustc_lexer::is_ident(&string) {
+        if !rustc_lexer::is_ident(string) {
             panic!("`{:?}` is not a valid identifier", string)
         }
         if is_raw && !sym.can_be_raw() {
@@ -355,38 +355,38 @@ pub struct Literal {
     span: Span,
 }
 
-pub(crate) struct Rustc<'a> {
-    resolver: &'a dyn ResolverExpand,
-    sess: &'a ParseSess,
+pub(crate) struct Rustc<'a, 'b> {
+    ecx: &'a mut ExtCtxt<'b>,
     def_site: Span,
     call_site: Span,
     mixed_site: Span,
-    span_debug: bool,
     krate: CrateNum,
     rebased_spans: FxHashMap<usize, Span>,
 }
 
-impl<'a> Rustc<'a> {
-    pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
-        let expn_data = cx.current_expansion.id.expn_data();
+impl<'a, 'b> Rustc<'a, 'b> {
+    pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
+        let expn_data = ecx.current_expansion.id.expn_data();
         Rustc {
-            resolver: cx.resolver,
-            sess: cx.parse_sess(),
-            def_site: cx.with_def_site_ctxt(expn_data.def_site),
-            call_site: cx.with_call_site_ctxt(expn_data.call_site),
-            mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
-            span_debug: cx.ecfg.span_debug,
+            def_site: ecx.with_def_site_ctxt(expn_data.def_site),
+            call_site: ecx.with_call_site_ctxt(expn_data.call_site),
+            mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site),
             krate: expn_data.macro_def_id.unwrap().krate,
             rebased_spans: FxHashMap::default(),
+            ecx,
         }
     }
 
+    fn sess(&self) -> &ParseSess {
+        self.ecx.parse_sess()
+    }
+
     fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal {
         Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) }
     }
 }
 
-impl server::Types for Rustc<'_> {
+impl server::Types for Rustc<'_, '_> {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
     type TokenStreamBuilder = tokenstream::TokenStreamBuilder;
@@ -401,17 +401,20 @@ impl server::Types for Rustc<'_> {
     type Span = Span;
 }
 
-impl server::FreeFunctions for Rustc<'_> {
+impl server::FreeFunctions for Rustc<'_, '_> {
     fn track_env_var(&mut self, var: &str, value: Option<&str>) {
-        self.sess.env_depinfo.borrow_mut().insert((Symbol::intern(var), value.map(Symbol::intern)));
+        self.sess()
+            .env_depinfo
+            .borrow_mut()
+            .insert((Symbol::intern(var), value.map(Symbol::intern)));
     }
 
     fn track_path(&mut self, path: &str) {
-        self.sess.file_depinfo.borrow_mut().insert(Symbol::intern(path));
+        self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
     }
 }
 
-impl server::TokenStream for Rustc<'_> {
+impl server::TokenStream for Rustc<'_, '_> {
     fn new(&mut self) -> Self::TokenStream {
         TokenStream::default()
     }
@@ -422,13 +425,61 @@ impl server::TokenStream for Rustc<'_> {
         parse_stream_from_source_str(
             FileName::proc_macro_source_code(src),
             src.to_string(),
-            self.sess,
+            self.sess(),
             Some(self.call_site),
         )
     }
     fn to_string(&mut self, stream: &Self::TokenStream) -> String {
         pprust::tts_to_string(stream)
     }
+    fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
+        // Parse the expression from our tokenstream.
+        let expr: PResult<'_, _> = try {
+            let mut p = rustc_parse::stream_to_parser(
+                self.sess(),
+                stream.clone(),
+                Some("proc_macro expand expr"),
+            );
+            let expr = p.parse_expr()?;
+            if p.token != token::Eof {
+                p.unexpected()?;
+            }
+            expr
+        };
+        let expr = expr.map_err(|mut err| err.emit())?;
+
+        // Perform eager expansion on the expression.
+        let expr = self
+            .ecx
+            .expander()
+            .fully_expand_fragment(crate::expand::AstFragment::Expr(expr))
+            .make_expr();
+
+        // NOTE: For now, limit `expand_expr` to exclusively expand to literals.
+        // This may be relaxed in the future.
+        // We don't use `nt_to_tokenstream` as the tokenstream currently cannot
+        // be recovered in the general case.
+        match &expr.kind {
+            ast::ExprKind::Lit(l) => {
+                Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into())
+            }
+            ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind {
+                ast::ExprKind::Lit(l) => match l.token {
+                    token::Lit { kind: token::Integer | token::Float, .. } => {
+                        Ok(Self::TokenStream::from_iter([
+                            // FIXME: The span of the `-` token is lost when
+                            // parsing, so we cannot faithfully recover it here.
+                            tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span),
+                            tokenstream::TokenTree::token(token::Literal(l.token), l.span),
+                        ]))
+                    }
+                    _ => Err(()),
+                },
+                _ => Err(()),
+            },
+            _ => Err(()),
+        }
+    }
     fn from_token_tree(
         &mut self,
         tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
@@ -440,7 +491,7 @@ impl server::TokenStream for Rustc<'_> {
     }
 }
 
-impl server::TokenStreamBuilder for Rustc<'_> {
+impl server::TokenStreamBuilder for Rustc<'_, '_> {
     fn new(&mut self) -> Self::TokenStreamBuilder {
         tokenstream::TokenStreamBuilder::new()
     }
@@ -452,7 +503,7 @@ impl server::TokenStreamBuilder for Rustc<'_> {
     }
 }
 
-impl server::TokenStreamIter for Rustc<'_> {
+impl server::TokenStreamIter for Rustc<'_, '_> {
     fn next(
         &mut self,
         iter: &mut Self::TokenStreamIter,
@@ -477,7 +528,7 @@ impl server::TokenStreamIter for Rustc<'_> {
     }
 }
 
-impl server::Group for Rustc<'_> {
+impl server::Group for Rustc<'_, '_> {
     fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group {
         Group {
             delimiter,
@@ -506,7 +557,7 @@ impl server::Group for Rustc<'_> {
     }
 }
 
-impl server::Punct for Rustc<'_> {
+impl server::Punct for Rustc<'_, '_> {
     fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct {
         Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self))
     }
@@ -524,9 +575,9 @@ impl server::Punct for Rustc<'_> {
     }
 }
 
-impl server::Ident for Rustc<'_> {
+impl server::Ident for Rustc<'_, '_> {
     fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
-        Ident::new(self.sess, Symbol::intern(string), is_raw, span)
+        Ident::new(self.sess(), Symbol::intern(string), is_raw, span)
     }
     fn span(&mut self, ident: Self::Ident) -> Self::Span {
         ident.span
@@ -536,10 +587,10 @@ impl server::Ident for Rustc<'_> {
     }
 }
 
-impl server::Literal for Rustc<'_> {
+impl server::Literal for Rustc<'_, '_> {
     fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
         let name = FileName::proc_macro_source_code(s);
-        let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned());
+        let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
 
         let first_span = parser.token.span.data();
         let minus_present = parser.eat(&token::BinOp(token::Minus));
@@ -675,7 +726,7 @@ impl server::Literal for Rustc<'_> {
     }
 }
 
-impl server::SourceFile for Rustc<'_> {
+impl server::SourceFile for Rustc<'_, '_> {
     fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
         Lrc::ptr_eq(file1, file2)
     }
@@ -695,7 +746,7 @@ impl server::SourceFile for Rustc<'_> {
     }
 }
 
-impl server::MultiSpan for Rustc<'_> {
+impl server::MultiSpan for Rustc<'_, '_> {
     fn new(&mut self) -> Self::MultiSpan {
         vec![]
     }
@@ -704,7 +755,7 @@ impl server::MultiSpan for Rustc<'_> {
     }
 }
 
-impl server::Diagnostic for Rustc<'_> {
+impl server::Diagnostic for Rustc<'_, '_> {
     fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
         let mut diag = Diagnostic::new(level.to_internal(), msg);
         diag.set_span(MultiSpan::from_spans(spans));
@@ -720,13 +771,13 @@ impl server::Diagnostic for Rustc<'_> {
         diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None);
     }
     fn emit(&mut self, diag: Self::Diagnostic) {
-        self.sess.span_diagnostic.emit_diagnostic(&diag);
+        self.sess().span_diagnostic.emit_diagnostic(&diag);
     }
 }
 
-impl server::Span for Rustc<'_> {
+impl server::Span for Rustc<'_, '_> {
     fn debug(&mut self, span: Self::Span) -> String {
-        if self.span_debug {
+        if self.ecx.ecfg.span_debug {
             format!("{:?}", span)
         } else {
             format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0)
@@ -742,7 +793,7 @@ impl server::Span for Rustc<'_> {
         self.mixed_site
     }
     fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
-        self.sess.source_map().lookup_char_pos(span.lo()).file
+        self.sess().source_map().lookup_char_pos(span.lo()).file
     }
     fn parent(&mut self, span: Self::Span) -> Option<Self::Span> {
         span.parent_callsite()
@@ -751,11 +802,11 @@ impl server::Span for Rustc<'_> {
         span.source_callsite()
     }
     fn start(&mut self, span: Self::Span) -> LineColumn {
-        let loc = self.sess.source_map().lookup_char_pos(span.lo());
+        let loc = self.sess().source_map().lookup_char_pos(span.lo());
         LineColumn { line: loc.line, column: loc.col.to_usize() }
     }
     fn end(&mut self, span: Self::Span) -> LineColumn {
-        let loc = self.sess.source_map().lookup_char_pos(span.hi());
+        let loc = self.sess().source_map().lookup_char_pos(span.hi());
         LineColumn { line: loc.line, column: loc.col.to_usize() }
     }
     fn before(&mut self, span: Self::Span) -> Self::Span {
@@ -765,8 +816,8 @@ impl server::Span for Rustc<'_> {
         span.shrink_to_hi()
     }
     fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
-        let self_loc = self.sess.source_map().lookup_char_pos(first.lo());
-        let other_loc = self.sess.source_map().lookup_char_pos(second.lo());
+        let self_loc = self.sess().source_map().lookup_char_pos(first.lo());
+        let other_loc = self.sess().source_map().lookup_char_pos(second.lo());
 
         if self_loc.file.name != other_loc.file.name {
             return None;
@@ -778,7 +829,7 @@ impl server::Span for Rustc<'_> {
         span.with_ctxt(at.ctxt())
     }
     fn source_text(&mut self, span: Self::Span) -> Option<String> {
-        self.sess.source_map().span_to_snippet(span).ok()
+        self.sess().source_map().span_to_snippet(span).ok()
     }
     /// Saves the provided span into the metadata of
     /// *the crate we are currently compiling*, which must
@@ -805,10 +856,10 @@ impl server::Span for Rustc<'_> {
     /// since we've loaded `my_proc_macro` from disk in order to execute it).
     /// In this way, we have obtained a span pointing into `my_proc_macro`
     fn save_span(&mut self, span: Self::Span) -> usize {
-        self.sess.save_proc_macro_span(span)
+        self.sess().save_proc_macro_span(span)
     }
     fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span {
-        let (resolver, krate, def_site) = (self.resolver, self.krate, self.def_site);
+        let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site);
         *self.rebased_spans.entry(id).or_insert_with(|| {
             // FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding,
             // replace it with a def-site context until we are encoding it properly.
@@ -821,11 +872,11 @@ impl server::Span for Rustc<'_> {
 fn ident_name_compatibility_hack(
     nt: &Nonterminal,
     orig_span: Span,
-    rustc: &mut Rustc<'_>,
+    rustc: &mut Rustc<'_, '_>,
 ) -> Option<(rustc_span::symbol::Ident, bool)> {
     if let NtIdent(ident, is_raw) = nt {
         if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
-            let source_map = rustc.sess.source_map();
+            let source_map = rustc.sess().source_map();
             let filename = source_map.span_to_filename(orig_span);
             if let FileName::Real(RealFileName::LocalPath(path)) = filename {
                 let matches_prefix = |prefix, filename| {
@@ -846,7 +897,7 @@ fn ident_name_compatibility_hack(
                     let snippet = source_map.span_to_snippet(orig_span);
                     if snippet.as_deref() == Ok("$name") {
                         if time_macros_impl {
-                            rustc.sess.buffer_lint_with_diagnostic(
+                            rustc.sess().buffer_lint_with_diagnostic(
                                 &PROC_MACRO_BACK_COMPAT,
                                 orig_span,
                                 ast::CRATE_NODE_ID,
@@ -871,7 +922,7 @@ fn ident_name_compatibility_hack(
                                         .and_then(|c| c.parse::<u32>().ok())
                                         .map_or(false, |v| v < 40)
                                 {
-                                    rustc.sess.buffer_lint_with_diagnostic(
+                                    rustc.sess().buffer_lint_with_diagnostic(
                                         &PROC_MACRO_BACK_COMPAT,
                                         orig_span,
                                         ast::CRATE_NODE_ID,
@@ -894,7 +945,7 @@ fn ident_name_compatibility_hack(
                             source_map.span_to_filename(rustc.def_site)
                         {
                             if macro_path.to_string_lossy().contains("pin-project-internal-0.") {
-                                rustc.sess.buffer_lint_with_diagnostic(
+                                rustc.sess().buffer_lint_with_diagnostic(
                                     &PROC_MACRO_BACK_COMPAT,
                                     orig_span,
                                     ast::CRATE_NODE_ID,