about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs17
-rw-r--r--compiler/rustc_ast/src/ast_like.rs320
-rw-r--r--compiler/rustc_ast/src/ast_traits.rs436
-rw-r--r--compiler/rustc_ast/src/lib.rs6
-rw-r--r--compiler/rustc_ast_pretty/src/lib.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/mod.rs32
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs11
-rw-r--r--compiler/rustc_expand/src/base.rs62
-rw-r--r--compiler/rustc_expand/src/config.rs8
-rw-r--r--compiler/rustc_expand/src/expand.rs86
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs31
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs2
-rw-r--r--compiler/rustc_lint/src/early.rs3
-rw-r--r--compiler/rustc_parse/src/lib.rs30
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs4
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs6
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs6
-rw-r--r--src/tools/rustfmt/src/attr.rs2
-rw-r--r--src/tools/rustfmt/src/formatting.rs1
-rw-r--r--src/tools/rustfmt/src/modules.rs12
-rw-r--r--src/tools/rustfmt/src/visitor.rs2
24 files changed, 593 insertions, 500 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e8cc9d9d291..5a4c997ed9b 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -929,16 +929,6 @@ pub struct Stmt {
 }
 
 impl Stmt {
-    pub fn tokens(&self) -> Option<&LazyTokenStream> {
-        match self.kind {
-            StmtKind::Local(ref local) => local.tokens.as_ref(),
-            StmtKind::Item(ref item) => item.tokens.as_ref(),
-            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(),
-            StmtKind::Empty => None,
-            StmtKind::MacCall(ref mac) => mac.tokens.as_ref(),
-        }
-    }
-
     pub fn has_trailing_semicolon(&self) -> bool {
         match &self.kind {
             StmtKind::Semi(_) => true,
@@ -2684,13 +2674,6 @@ impl Item {
     }
 }
 
-impl<K: Into<ItemKind>> Item<K> {
-    pub fn into_item(self) -> Item {
-        let Item { attrs, id, span, vis, ident, kind, tokens } = self;
-        Item { attrs, id, span, vis, ident, kind: kind.into(), tokens }
-    }
-}
-
 /// `extern` qualifier on a function item or function type.
 #[derive(Clone, Copy, Encodable, Decodable, Debug)]
 pub enum Extern {
diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs
deleted file mode 100644
index 1a271b0adef..00000000000
--- a/compiler/rustc_ast/src/ast_like.rs
+++ /dev/null
@@ -1,320 +0,0 @@
-use super::ptr::P;
-use super::token::Nonterminal;
-use super::tokenstream::LazyTokenStream;
-use super::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant};
-use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt};
-use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
-use super::{AttrVec, Attribute, Stmt, StmtKind};
-
-use std::fmt;
-use std::marker::PhantomData;
-
-/// An `AstLike` represents an AST node (or some wrapper around
-/// and AST node) which stores some combination of attributes
-/// and tokens.
-pub trait AstLike: Sized + fmt::Debug {
-    /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner
-    /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not
-    /// considered 'custom' attributes
-    ///
-    /// If this is `false`, then this `AstLike` definitely does
-    /// not support 'custom' inner attributes, which enables some optimizations
-    /// during token collection.
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool;
-    fn attrs(&self) -> &[Attribute];
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>;
-}
-
-impl<T: AstLike + 'static> AstLike for P<T> {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
-    fn attrs(&self) -> &[Attribute] {
-        (**self).attrs()
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        (**self).visit_attrs(f);
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        (**self).tokens_mut()
-    }
-}
-
-impl AstLike for crate::token::Nonterminal {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
-    fn attrs(&self) -> &[Attribute] {
-        match self {
-            Nonterminal::NtItem(item) => item.attrs(),
-            Nonterminal::NtStmt(stmt) => stmt.attrs(),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(),
-            Nonterminal::NtPat(_)
-            | Nonterminal::NtTy(_)
-            | Nonterminal::NtMeta(_)
-            | Nonterminal::NtPath(_)
-            | Nonterminal::NtVis(_)
-            | Nonterminal::NtBlock(_)
-            | Nonterminal::NtIdent(..)
-            | Nonterminal::NtLifetime(_) => &[],
-        }
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        match self {
-            Nonterminal::NtItem(item) => item.visit_attrs(f),
-            Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f),
-            Nonterminal::NtPat(_)
-            | Nonterminal::NtTy(_)
-            | Nonterminal::NtMeta(_)
-            | Nonterminal::NtPath(_)
-            | Nonterminal::NtVis(_)
-            | Nonterminal::NtBlock(_)
-            | Nonterminal::NtIdent(..)
-            | Nonterminal::NtLifetime(_) => {}
-        }
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        match self {
-            Nonterminal::NtItem(item) => item.tokens_mut(),
-            Nonterminal::NtStmt(stmt) => stmt.tokens_mut(),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
-            Nonterminal::NtPat(pat) => pat.tokens_mut(),
-            Nonterminal::NtTy(ty) => ty.tokens_mut(),
-            Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
-            Nonterminal::NtPath(path) => path.tokens_mut(),
-            Nonterminal::NtVis(vis) => vis.tokens_mut(),
-            Nonterminal::NtBlock(block) => block.tokens_mut(),
-            Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None,
-        }
-    }
-}
-
-fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
-    crate::mut_visit::visit_clobber(attrs, |attrs| {
-        let mut vec = attrs.into();
-        f(&mut vec);
-        vec.into()
-    });
-}
-
-impl AstLike for StmtKind {
-    // This might be an `StmtKind::Item`, which contains
-    // an item that supports inner attrs
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
-
-    fn attrs(&self) -> &[Attribute] {
-        match self {
-            StmtKind::Local(local) => local.attrs(),
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
-            StmtKind::Item(item) => item.attrs(),
-            StmtKind::Empty => &[],
-            StmtKind::MacCall(mac) => &mac.attrs,
-        }
-    }
-
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        match self {
-            StmtKind::Local(local) => local.visit_attrs(f),
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
-            StmtKind::Item(item) => item.visit_attrs(f),
-            StmtKind::Empty => {}
-            StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
-        }
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        Some(match self {
-            StmtKind::Local(local) => &mut local.tokens,
-            StmtKind::Item(item) => &mut item.tokens,
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens,
-            StmtKind::Empty => return None,
-            StmtKind::MacCall(mac) => &mut mac.tokens,
-        })
-    }
-}
-
-impl AstLike for Stmt {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS;
-
-    fn attrs(&self) -> &[Attribute] {
-        self.kind.attrs()
-    }
-
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        self.kind.visit_attrs(f);
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        self.kind.tokens_mut()
-    }
-}
-
-impl AstLike for Attribute {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
-
-    fn attrs(&self) -> &[Attribute] {
-        &[]
-    }
-    fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        Some(match &mut self.kind {
-            AttrKind::Normal(_, tokens) => tokens,
-            kind @ AttrKind::DocComment(..) => {
-                panic!("Called tokens_mut on doc comment attr {:?}", kind)
-            }
-        })
-    }
-}
-
-impl<T: AstLike> AstLike for Option<T> {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
-
-    fn attrs(&self) -> &[Attribute] {
-        self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[])
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        if let Some(inner) = self.as_mut() {
-            inner.visit_attrs(f);
-        }
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        self.as_mut().and_then(|inner| inner.tokens_mut())
-    }
-}
-
-/// Helper trait for the macros below. Abstracts over
-/// the two types of attribute fields that AST nodes
-/// may have (`Vec<Attribute>` or `AttrVec`)
-trait VecOrAttrVec {
-    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
-}
-
-impl VecOrAttrVec for Vec<Attribute> {
-    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        f(self)
-    }
-}
-
-impl VecOrAttrVec for AttrVec {
-    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        visit_attrvec(self, f)
-    }
-}
-
-macro_rules! derive_has_tokens_and_attrs {
-    (
-        const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs:literal;
-        $($ty:path),*
-    ) => { $(
-        impl AstLike for $ty {
-            const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs;
-
-            fn attrs(&self) -> &[Attribute] {
-                &self.attrs
-            }
-
-            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-                VecOrAttrVec::visit(&mut self.attrs, f)
-            }
-
-            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-                Some(&mut self.tokens)
-            }
-
-        }
-    )* }
-}
-
-macro_rules! derive_has_attrs_no_tokens {
-    ($($ty:path),*) => { $(
-        impl AstLike for $ty {
-            const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
-
-            fn attrs(&self) -> &[Attribute] {
-                &self.attrs
-            }
-
-            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-                VecOrAttrVec::visit(&mut self.attrs, f)
-            }
-
-            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-                None
-            }
-        }
-    )* }
-}
-
-macro_rules! derive_has_tokens_no_attrs {
-    ($($ty:path),*) => { $(
-        impl AstLike for $ty {
-            const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
-
-            fn attrs(&self) -> &[Attribute] {
-                &[]
-            }
-
-            fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
-            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-                Some(&mut self.tokens)
-            }
-        }
-    )* }
-}
-
-// These ast nodes support both active and inert attributes,
-// so they have tokens collected to pass to proc macros
-derive_has_tokens_and_attrs! {
-    // Both `Item` and `AssocItem` can have bodies, which
-    // can contain inner attributes
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
-    Item, AssocItem, ForeignItem
-}
-
-derive_has_tokens_and_attrs! {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
-    Local, MacCallStmt, Expr
-}
-
-// These ast nodes only support inert attributes, so they don't
-// store tokens (since nothing can observe them)
-derive_has_attrs_no_tokens! {
-    FieldDef, Arm, ExprField, PatField, Variant, Param, GenericParam, Crate
-}
-
-// These AST nodes don't support attributes, but can
-// be captured by a `macro_rules!` matcher. Therefore,
-// they need to store tokens.
-derive_has_tokens_no_attrs! {
-    Ty, Block, AttrItem, Pat, Path, Visibility
-}
-
-/// A newtype around an `AstLike` node that implements `AstLike` itself.
-pub struct AstLikeWrapper<Wrapped, Tag> {
-    pub wrapped: Wrapped,
-    pub tag: PhantomData<Tag>,
-}
-
-impl<Wrapped, Tag> AstLikeWrapper<Wrapped, Tag> {
-    pub fn new(wrapped: Wrapped, _tag: Tag) -> AstLikeWrapper<Wrapped, Tag> {
-        AstLikeWrapper { wrapped, tag: Default::default() }
-    }
-}
-
-impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstLikeWrapper<Wrapped, Tag> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AstLikeWrapper")
-            .field("wrapped", &self.wrapped)
-            .field("tag", &self.tag)
-            .finish()
-    }
-}
-
-impl<Wrapped: AstLike, Tag> AstLike for AstLikeWrapper<Wrapped, Tag> {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS;
-    fn attrs(&self) -> &[Attribute] {
-        self.wrapped.attrs()
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        self.wrapped.visit_attrs(f)
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
-        self.wrapped.tokens_mut()
-    }
-}
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
new file mode 100644
index 00000000000..bd401ddbbee
--- /dev/null
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -0,0 +1,436 @@
+//! A set of traits implemented for various AST nodes,
+//! typically those used in AST fragments during macro expansion.
+//! The traits are not implemented exhaustively, only when actually necessary.
+
+use crate::ptr::P;
+use crate::token::Nonterminal;
+use crate::tokenstream::LazyTokenStream;
+use crate::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant};
+use crate::{AssocItem, Expr, ForeignItem, Item, NodeId};
+use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
+use crate::{AttrVec, Attribute, Stmt, StmtKind};
+
+use rustc_span::Span;
+
+use std::fmt;
+use std::marker::PhantomData;
+
+/// A utility trait to reduce boilerplate.
+/// Standard `Deref(Mut)` cannot be reused due to coherence.
+pub trait AstDeref {
+    type Target;
+    fn ast_deref(&self) -> &Self::Target;
+    fn ast_deref_mut(&mut self) -> &mut Self::Target;
+}
+
+macro_rules! impl_not_ast_deref {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl !AstDeref for $T {}
+        )+
+    };
+}
+
+impl_not_ast_deref!(AssocItem, Expr, ForeignItem, Item, Stmt);
+
+impl<T> AstDeref for P<T> {
+    type Target = T;
+    fn ast_deref(&self) -> &Self::Target {
+        self
+    }
+    fn ast_deref_mut(&mut self) -> &mut Self::Target {
+        self
+    }
+}
+
+/// A trait for AST nodes having an ID.
+pub trait HasNodeId {
+    fn node_id(&self) -> NodeId;
+    fn node_id_mut(&mut self) -> &mut NodeId;
+}
+
+macro_rules! impl_has_node_id {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl HasNodeId for $T {
+                fn node_id(&self) -> NodeId {
+                    self.id
+                }
+                fn node_id_mut(&mut self) -> &mut NodeId {
+                    &mut self.id
+                }
+            }
+        )+
+    };
+}
+
+impl_has_node_id!(
+    Arm,
+    AssocItem,
+    Crate,
+    Expr,
+    ExprField,
+    FieldDef,
+    ForeignItem,
+    GenericParam,
+    Item,
+    Param,
+    Pat,
+    PatField,
+    Stmt,
+    Ty,
+    Variant,
+);
+
+impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
+    fn node_id(&self) -> NodeId {
+        self.ast_deref().node_id()
+    }
+    fn node_id_mut(&mut self) -> &mut NodeId {
+        self.ast_deref_mut().node_id_mut()
+    }
+}
+
+/// A trait for AST nodes having a span.
+pub trait HasSpan {
+    fn span(&self) -> Span;
+}
+
+macro_rules! impl_has_span {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl HasSpan for $T {
+                fn span(&self) -> Span {
+                    self.span
+                }
+            }
+        )+
+    };
+}
+
+impl_has_span!(AssocItem, Expr, ForeignItem, Item, Stmt);
+
+impl<T: AstDeref<Target: HasSpan>> HasSpan for T {
+    fn span(&self) -> Span {
+        self.ast_deref().span()
+    }
+}
+
+/// A trait for AST nodes having (or not having) collected tokens.
+pub trait HasTokens {
+    fn tokens(&self) -> Option<&LazyTokenStream>;
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>;
+}
+
+macro_rules! impl_has_tokens {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl HasTokens for $T {
+                fn tokens(&self) -> Option<&LazyTokenStream> {
+                    self.tokens.as_ref()
+                }
+                fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+                    Some(&mut self.tokens)
+                }
+            }
+        )+
+    };
+}
+
+macro_rules! impl_has_tokens_none {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl HasTokens for $T {
+                fn tokens(&self) -> Option<&LazyTokenStream> {
+                    None
+                }
+                fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+                    None
+                }
+            }
+        )+
+    };
+}
+
+impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
+impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant);
+
+impl<T: AstDeref<Target: HasTokens>> HasTokens for T {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        self.ast_deref().tokens()
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        self.ast_deref_mut().tokens_mut()
+    }
+}
+
+impl<T: HasTokens> HasTokens for Option<T> {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        self.as_ref().and_then(|inner| inner.tokens())
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        self.as_mut().and_then(|inner| inner.tokens_mut())
+    }
+}
+
+impl HasTokens for StmtKind {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        match self {
+            StmtKind::Local(local) => local.tokens.as_ref(),
+            StmtKind::Item(item) => item.tokens(),
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens(),
+            StmtKind::Empty => return None,
+            StmtKind::MacCall(mac) => mac.tokens.as_ref(),
+        }
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        match self {
+            StmtKind::Local(local) => Some(&mut local.tokens),
+            StmtKind::Item(item) => item.tokens_mut(),
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.tokens_mut(),
+            StmtKind::Empty => return None,
+            StmtKind::MacCall(mac) => Some(&mut mac.tokens),
+        }
+    }
+}
+
+impl HasTokens for Stmt {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        self.kind.tokens()
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        self.kind.tokens_mut()
+    }
+}
+
+impl HasTokens for Attribute {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        match &self.kind {
+            AttrKind::Normal(_, tokens) => tokens.as_ref(),
+            kind @ AttrKind::DocComment(..) => {
+                panic!("Called tokens on doc comment attr {:?}", kind)
+            }
+        }
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        Some(match &mut self.kind {
+            AttrKind::Normal(_, tokens) => tokens,
+            kind @ AttrKind::DocComment(..) => {
+                panic!("Called tokens_mut on doc comment attr {:?}", kind)
+            }
+        })
+    }
+}
+
+impl HasTokens for Nonterminal {
+    fn tokens(&self) -> Option<&LazyTokenStream> {
+        match self {
+            Nonterminal::NtItem(item) => item.tokens(),
+            Nonterminal::NtStmt(stmt) => stmt.tokens(),
+            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(),
+            Nonterminal::NtPat(pat) => pat.tokens(),
+            Nonterminal::NtTy(ty) => ty.tokens(),
+            Nonterminal::NtMeta(attr_item) => attr_item.tokens(),
+            Nonterminal::NtPath(path) => path.tokens(),
+            Nonterminal::NtVis(vis) => vis.tokens(),
+            Nonterminal::NtBlock(block) => block.tokens(),
+            Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None,
+        }
+    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        match self {
+            Nonterminal::NtItem(item) => item.tokens_mut(),
+            Nonterminal::NtStmt(stmt) => stmt.tokens_mut(),
+            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
+            Nonterminal::NtPat(pat) => pat.tokens_mut(),
+            Nonterminal::NtTy(ty) => ty.tokens_mut(),
+            Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
+            Nonterminal::NtPath(path) => path.tokens_mut(),
+            Nonterminal::NtVis(vis) => vis.tokens_mut(),
+            Nonterminal::NtBlock(block) => block.tokens_mut(),
+            Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None,
+        }
+    }
+}
+
+/// A trait for AST nodes having (or not having) attributes.
+pub trait HasAttrs {
+    /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner
+    /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not
+    /// considered 'custom' attributes.
+    ///
+    /// If this is `false`, then this `HasAttrs` definitely does
+    /// not support 'custom' inner attributes, which enables some optimizations
+    /// during token collection.
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool;
+    fn attrs(&self) -> &[Attribute];
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+}
+
+macro_rules! impl_has_attrs {
+    (const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner:literal, $($T:ty),+ $(,)?) => {
+        $(
+            impl HasAttrs for $T {
+                const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner;
+
+                fn attrs(&self) -> &[Attribute] {
+                    &self.attrs
+                }
+
+                fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+                    VecOrAttrVec::visit(&mut self.attrs, f)
+                }
+            }
+        )+
+    };
+}
+
+macro_rules! impl_has_attrs_none {
+    ($($T:ty),+ $(,)?) => {
+        $(
+            impl HasAttrs for $T {
+                const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
+                fn attrs(&self) -> &[Attribute] {
+                    &[]
+                }
+                fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
+            }
+        )+
+    };
+}
+
+impl_has_attrs!(
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true,
+    AssocItem,
+    ForeignItem,
+    Item,
+);
+impl_has_attrs!(
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false,
+    Arm,
+    Crate,
+    Expr,
+    ExprField,
+    FieldDef,
+    GenericParam,
+    Param,
+    PatField,
+    Variant,
+);
+impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
+
+impl<T: AstDeref<Target: HasAttrs>> HasAttrs for T {
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::Target::SUPPORTS_CUSTOM_INNER_ATTRS;
+    fn attrs(&self) -> &[Attribute] {
+        self.ast_deref().attrs()
+    }
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        self.ast_deref_mut().visit_attrs(f)
+    }
+}
+
+impl<T: HasAttrs> HasAttrs for Option<T> {
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
+    fn attrs(&self) -> &[Attribute] {
+        self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[])
+    }
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        if let Some(inner) = self.as_mut() {
+            inner.visit_attrs(f);
+        }
+    }
+}
+
+impl HasAttrs for StmtKind {
+    // This might be a `StmtKind::Item`, which contains
+    // an item that supports inner attrs.
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
+
+    fn attrs(&self) -> &[Attribute] {
+        match self {
+            StmtKind::Local(local) => &local.attrs,
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
+            StmtKind::Item(item) => item.attrs(),
+            StmtKind::Empty => &[],
+            StmtKind::MacCall(mac) => &mac.attrs,
+        }
+    }
+
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        match self {
+            StmtKind::Local(local) => visit_attrvec(&mut local.attrs, f),
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
+            StmtKind::Item(item) => item.visit_attrs(f),
+            StmtKind::Empty => {}
+            StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
+        }
+    }
+}
+
+impl HasAttrs for Stmt {
+    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS;
+    fn attrs(&self) -> &[Attribute] {
+        self.kind.attrs()
+    }
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        self.kind.visit_attrs(f);
+    }
+}
+
+/// Helper trait for the impls above. Abstracts over
+/// the two types of attribute fields that AST nodes
+/// may have (`Vec<Attribute>` or `AttrVec`).
+trait VecOrAttrVec {
+    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+}
+
+impl VecOrAttrVec for Vec<Attribute> {
+    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        f(self)
+    }
+}
+
+impl VecOrAttrVec for AttrVec {
+    fn visit(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        visit_attrvec(self, f)
+    }
+}
+
+fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
+    crate::mut_visit::visit_clobber(attrs, |attrs| {
+        let mut vec = attrs.into();
+        f(&mut vec);
+        vec.into()
+    });
+}
+
+/// A newtype around an AST node that implements the traits above if the node implements them.
+pub struct AstNodeWrapper<Wrapped, Tag> {
+    pub wrapped: Wrapped,
+    pub tag: PhantomData<Tag>,
+}
+
+impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
+    pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper<Wrapped, Tag> {
+        AstNodeWrapper { wrapped, tag: Default::default() }
+    }
+}
+
+impl<Wrapped, Tag> AstDeref for AstNodeWrapper<Wrapped, Tag> {
+    type Target = Wrapped;
+    fn ast_deref(&self) -> &Self::Target {
+        &self.wrapped
+    }
+    fn ast_deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.wrapped
+    }
+}
+
+impl<Wrapped: fmt::Debug, Tag> fmt::Debug for AstNodeWrapper<Wrapped, Tag> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("AstNodeWrapper")
+            .field("wrapped", &self.wrapped)
+            .field("tag", &self.tag)
+            .finish()
+    }
+}
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index a7c23dbb79c..12467169192 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -8,6 +8,7 @@
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(deny(warnings)))
 )]
+#![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(const_default_impls)]
 #![feature(const_trait_impl)]
@@ -16,6 +17,7 @@
 #![feature(label_break_value)]
 #![feature(let_chains)]
 #![feature(min_specialization)]
+#![feature(negative_impls)]
 #![feature(nll)]
 #![feature(slice_internals)]
 #![feature(stmt_expr_attributes)]
@@ -33,7 +35,7 @@ pub mod util {
 }
 
 pub mod ast;
-pub mod ast_like;
+pub mod ast_traits;
 pub mod attr;
 pub mod entry;
 pub mod expand;
@@ -45,7 +47,7 @@ pub mod tokenstream;
 pub mod visit;
 
 pub use self::ast::*;
-pub use self::ast_like::{AstLike, AstLikeWrapper};
+pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs
index 517ab30b2a4..95beccbb728 100644
--- a/compiler/rustc_ast_pretty/src/lib.rs
+++ b/compiler/rustc_ast_pretty/src/lib.rs
@@ -1,5 +1,7 @@
+#![feature(associated_type_bounds)]
 #![feature(crate_visibility_modifier)]
 #![feature(box_patterns)]
+#![feature(with_negative_coherence)]
 #![recursion_limit = "256"]
 
 mod helpers;
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
index ac9e7d06c4e..d2e2fd520cd 100644
--- a/compiler/rustc_ast_pretty/src/pprust/mod.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -4,12 +4,42 @@ mod tests;
 pub mod state;
 pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State};
 
-use rustc_ast as ast;
 use rustc_ast::token::{Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::{self as ast, AstDeref};
 
 use std::borrow::Cow;
 
+pub trait AstPrettyPrint {
+    fn pretty_print(&self) -> String;
+}
+
+impl<T: AstDeref<Target: AstPrettyPrint>> AstPrettyPrint for T {
+    fn pretty_print(&self) -> String {
+        self.ast_deref().pretty_print()
+    }
+}
+
+macro_rules! impl_ast_pretty_print {
+    ($($T:ty => $method:ident),+ $(,)?) => {
+        $(
+            impl AstPrettyPrint for $T {
+                fn pretty_print(&self) -> String {
+                    State::new().$method(self)
+                }
+            }
+        )+
+    };
+}
+
+impl_ast_pretty_print! {
+    ast::Item => item_to_string,
+    ast::AssocItem => assoc_item_to_string,
+    ast::ForeignItem => foreign_item_to_string,
+    ast::Expr => expr_to_string,
+    ast::Stmt => stmt_to_string,
+}
+
 pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
     State::new().nonterminal_to_string(nt)
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index c02cdc29561..f520b541124 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -858,6 +858,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         Self::to_string(|s| s.print_item(i))
     }
 
+    fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
+        Self::to_string(|s| s.print_assoc_item(i))
+    }
+
+    fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
+        Self::to_string(|s| s.print_foreign_item(i))
+    }
+
     fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
         Self::to_string(|s| s.print_generic_params(generic_params))
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 2a35dd1006e..23c5a92e352 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -19,7 +19,7 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
+    crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
         let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
@@ -496,7 +496,7 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_assoc_item(&mut self, item: &ast::AssocItem) {
+    crate fn print_assoc_item(&mut self, item: &ast::AssocItem) {
         let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index 7637bf7edc8..4278fedfee9 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -7,7 +7,7 @@ use rustc_ast::tokenstream::CanSynthesizeMissingTokens;
 use rustc_ast::visit::Visitor;
 use rustc_ast::NodeId;
 use rustc_ast::{mut_visit, visit};
-use rustc_ast::{AstLike, Attribute};
+use rustc_ast::{Attribute, HasAttrs, HasTokens};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_expand::config::StripUnconfigured;
 use rustc_expand::configure;
@@ -125,7 +125,7 @@ impl<'ast> visit::Visitor<'ast> for CfgFinder {
 }
 
 impl CfgEval<'_, '_> {
-    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
+    fn configure<T: HasAttrs + HasTokens>(&mut self, node: T) -> Option<T> {
         self.cfg.configure(node)
     }
 
@@ -173,13 +173,8 @@ impl CfgEval<'_, '_> {
             }
             _ => unreachable!(),
         };
-        let nt = annotatable.into_nonterminal();
 
-        let mut orig_tokens = rustc_parse::nt_to_tokenstream(
-            &nt,
-            &self.cfg.sess.parse_sess,
-            CanSynthesizeMissingTokens::No,
-        );
+        let mut orig_tokens = annotatable.to_tokens(&self.cfg.sess.parse_sess);
 
         // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`)
         // to `None`-delimited groups containing the corresponding tokens. This
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 2b30ec601a0..48b5502c20f 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -6,14 +6,14 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Nonterminal};
 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_ast::{self as ast, Attribute, HasAttrs, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::BuiltinLintDiagnostics;
-use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
+use rustc_parse::{self, parser, to_token_stream, MACRO_ARGUMENTS};
 use rustc_session::{parse::ParseSess, Limit, Session};
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_span::edition::Edition;
@@ -109,17 +109,20 @@ impl Annotatable {
         }
     }
 
-    pub fn into_nonterminal(self) -> Nonterminal {
+    pub fn to_tokens(&self, sess: &ParseSess) -> TokenStream {
         match self {
-            Annotatable::Item(item) => token::NtItem(item),
-            Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => {
-                token::NtItem(P(item.and_then(ast::AssocItem::into_item)))
+            Annotatable::Item(node) => to_token_stream(node, sess, CanSynthesizeMissingTokens::No),
+            Annotatable::TraitItem(node) | Annotatable::ImplItem(node) => {
+                to_token_stream(node, sess, CanSynthesizeMissingTokens::No)
             }
-            Annotatable::ForeignItem(item) => {
-                token::NtItem(P(item.and_then(ast::ForeignItem::into_item)))
+            Annotatable::ForeignItem(node) => {
+                to_token_stream(node, sess, CanSynthesizeMissingTokens::No)
             }
-            Annotatable::Stmt(stmt) => token::NtStmt(stmt),
-            Annotatable::Expr(expr) => token::NtExpr(expr),
+            Annotatable::Stmt(node) => {
+                assert!(!matches!(node.kind, ast::StmtKind::Empty));
+                to_token_stream(node, sess, CanSynthesizeMissingTokens::No)
+            }
+            Annotatable::Expr(node) => to_token_stream(node, sess, CanSynthesizeMissingTokens::No),
             Annotatable::Arm(..)
             | Annotatable::ExprField(..)
             | Annotatable::PatField(..)
@@ -131,10 +134,6 @@ impl Annotatable {
         }
     }
 
-    crate fn into_tokens(self, sess: &ParseSess) -> TokenStream {
-        nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No)
-    }
-
     pub fn expect_item(self) -> P<ast::Item> {
         match self {
             Annotatable::Item(i) => i,
@@ -1380,16 +1379,7 @@ pub fn parse_macro_name_and_helper_attrs(
 /// asserts in old versions of those crates and their wide use in the ecosystem.
 /// See issue #73345 for more details.
 /// FIXME(#73933): Remove this eventually.
-pub(crate) fn pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseSess) -> bool {
-    let item = match nt {
-        Nonterminal::NtItem(item) => item,
-        Nonterminal::NtStmt(stmt) => match &stmt.kind {
-            ast::StmtKind::Item(item) => item,
-            _ => return false,
-        },
-        _ => return false,
-    };
-
+fn pretty_printing_compatibility_hack(item: &Item, sess: &ParseSess) -> bool {
     let name = item.ident.name;
     if name == sym::ProceduralMasqueradeDummyType {
         if let ast::ItemKind::Enum(enum_def, _) = &item.kind {
@@ -1411,3 +1401,27 @@ pub(crate) fn pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseS
     }
     false
 }
+
+pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &ParseSess) -> bool {
+    let item = match ann {
+        Annotatable::Item(item) => item,
+        Annotatable::Stmt(stmt) => match &stmt.kind {
+            ast::StmtKind::Item(item) => item,
+            _ => return false,
+        },
+        _ => return false,
+    };
+    pretty_printing_compatibility_hack(item, sess)
+}
+
+pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseSess) -> bool {
+    let item = match nt {
+        Nonterminal::NtItem(item) => item,
+        Nonterminal::NtStmt(stmt) => match &stmt.kind {
+            ast::StmtKind::Item(item) => item,
+            _ => return false,
+        },
+        _ => return false,
+    };
+    pretty_printing_compatibility_hack(item, sess)
+}
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index c91125105d7..0b8cb07a64a 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -6,7 +6,7 @@ use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use rustc_ast::tokenstream::{DelimSpan, Spacing};
 use rustc_ast::tokenstream::{LazyTokenStream, TokenTree};
 use rustc_ast::NodeId;
-use rustc_ast::{self as ast, AstLike, AttrStyle, Attribute, MetaItem};
+use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -246,7 +246,7 @@ macro_rules! configure {
 }
 
 impl<'a> StripUnconfigured<'a> {
-    pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> {
+    pub fn configure<T: HasAttrs + HasTokens>(&self, mut node: T) -> Option<T> {
         self.process_cfg_attrs(&mut node);
         if self.in_cfg(node.attrs()) {
             self.try_configure_tokens(&mut node);
@@ -256,7 +256,7 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
-    fn try_configure_tokens<T: AstLike>(&self, node: &mut T) {
+    fn try_configure_tokens<T: HasTokens>(&self, node: &mut T) {
         if self.config_tokens {
             if let Some(Some(tokens)) = node.tokens_mut() {
                 let attr_annotated_tokens = tokens.create_token_stream();
@@ -330,7 +330,7 @@ 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>(&self, node: &mut T) {
+    fn process_cfg_attrs<T: HasAttrs>(&self, node: &mut T) {
         node.visit_attrs(|attrs| {
             attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
         });
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 5bd89f3f42f..a390e7a466d 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -11,7 +11,8 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind};
+use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrStyle, ExprKind, ForeignItemKind};
+use rustc_ast::{HasAttrs, HasNodeId};
 use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind};
 use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
 use rustc_ast_pretty::pprust;
@@ -678,12 +679,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                     )
                                 ) =>
                         {
-                            rustc_parse::fake_token_stream(
-                                &self.cx.sess.parse_sess,
-                                &item.into_nonterminal(),
-                            )
+                            rustc_parse::fake_token_stream(&self.cx.sess.parse_sess, item_inner)
                         }
-                        _ => item.into_tokens(&self.cx.sess.parse_sess),
+                        _ => item.to_tokens(&self.cx.sess.parse_sess),
                     };
                     let attr_item = attr.unwrap_normal_item();
                     if let MacArgs::Eq(..) = attr_item.args {
@@ -998,13 +996,12 @@ enum AddSemicolon {
 
 /// A trait implemented for all `AstFragment` nodes and providing all pieces
 /// of functionality used by `InvocationCollector`.
-trait InvocationCollectorNode: AstLike {
+trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
     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!()
     }
@@ -1040,9 +1037,6 @@ impl InvocationCollectorNode for P<ast::Item> {
     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)
     }
@@ -1142,7 +1136,7 @@ impl InvocationCollectorNode for P<ast::Item> {
 }
 
 struct TraitItemTag;
-impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> {
+impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag> {
     type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
     const KIND: AstFragmentKind = AstFragmentKind::TraitItems;
     fn to_annotatable(self) -> Annotatable {
@@ -1151,9 +1145,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag>
     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)
     }
@@ -1170,7 +1161,7 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag>
 }
 
 struct ImplItemTag;
-impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> {
+impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag> {
     type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
     const KIND: AstFragmentKind = AstFragmentKind::ImplItems;
     fn to_annotatable(self) -> Annotatable {
@@ -1179,9 +1170,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag>
     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)
     }
@@ -1205,9 +1193,6 @@ impl InvocationCollectorNode for P<ast::ForeignItem> {
     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)
     }
@@ -1231,9 +1216,6 @@ impl InvocationCollectorNode for ast::Variant {
     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)
     }
@@ -1247,9 +1229,6 @@ impl InvocationCollectorNode for ast::FieldDef {
     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)
     }
@@ -1263,9 +1242,6 @@ impl InvocationCollectorNode for ast::PatField {
     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)
     }
@@ -1279,9 +1255,6 @@ impl InvocationCollectorNode for ast::ExprField {
     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)
     }
@@ -1295,9 +1268,6 @@ impl InvocationCollectorNode for ast::Param {
     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)
     }
@@ -1311,9 +1281,6 @@ impl InvocationCollectorNode for ast::GenericParam {
     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)
     }
@@ -1327,9 +1294,6 @@ impl InvocationCollectorNode for ast::Arm {
     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)
     }
@@ -1344,9 +1308,6 @@ impl InvocationCollectorNode for ast::Stmt {
     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)
     }
@@ -1403,9 +1364,6 @@ impl InvocationCollectorNode for ast::Crate {
     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)
     }
@@ -1420,9 +1378,6 @@ impl InvocationCollectorNode for P<ast::Ty> {
     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)
     }
@@ -1447,9 +1402,6 @@ impl InvocationCollectorNode for P<ast::Pat> {
     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)
     }
@@ -1475,9 +1427,6 @@ impl InvocationCollectorNode for P<ast::Expr> {
     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"
     }
@@ -1497,7 +1446,7 @@ impl InvocationCollectorNode for P<ast::Expr> {
 }
 
 struct OptExprTag;
-impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> {
+impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> {
     type OutputTy = Option<P<ast::Expr>>;
     type AttrsTy = ast::AttrVec;
     const KIND: AstFragmentKind = AstFragmentKind::OptExpr;
@@ -1507,9 +1456,6 @@ impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> {
     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)
@@ -1584,7 +1530,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// legacy derive helpers (helpers written before derives that introduce them).
     fn take_first_attr(
         &self,
-        item: &mut impl AstLike,
+        item: &mut impl HasAttrs,
     ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
         let mut attr = None;
 
@@ -1680,7 +1626,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
     fn expand_cfg_true(
         &mut self,
-        node: &mut impl AstLike,
+        node: &mut impl HasAttrs,
         attr: ast::Attribute,
         pos: usize,
     ) -> bool {
@@ -1695,7 +1641,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         res
     }
 
-    fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) {
+    fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: ast::Attribute, pos: usize) {
         node.visit_attrs(|attrs| {
             attrs.splice(pos..pos, self.cfg().expand_cfg_attr(attr, false));
         });
@@ -1733,7 +1679,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 }
                 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))
+                        assign_id!(this, node.node_id_mut(), || node.noop_flat_map(this))
                     }) {
                         Ok(output) => output,
                         Err(returned_node) => {
@@ -1781,7 +1727,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                     })
                 }
                 None => {
-                    assign_id!(self, node.id(), || node.noop_visit(self))
+                    assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
                 }
             };
         }
@@ -1794,11 +1740,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
     }
 
     fn flat_map_trait_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag))
+        self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag))
     }
 
     fn flat_map_impl_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag))
+        self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag))
     }
 
     fn flat_map_foreign_item(
@@ -1889,7 +1835,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
     }
 
     fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        self.flat_map_node(AstLikeWrapper::new(node, OptExprTag))
+        self.flat_map_node(AstNodeWrapper::new(node, OptExprTag))
     }
 
     fn visit_block(&mut self, node: &mut P<ast::Block>) {
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index 8e1966a0711..6c74d462fb8 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -4,10 +4,9 @@ use crate::proc_macro_server;
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token;
-use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorGuaranteed;
-use rustc_parse::nt_to_tokenstream;
 use rustc_parse::parser::ForceCollect;
 use rustc_span::profiling::SpannedEventArgRecorder;
 use rustc_span::{Span, DUMMY_SP};
@@ -87,25 +86,17 @@ impl MultiItemModifier for ProcMacroDerive {
     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         // We need special handling for statement items
         // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
-        let mut is_stmt = false;
-        let item = match item {
-            Annotatable::Item(item) => token::NtItem(item),
-            Annotatable::Stmt(stmt) => {
-                is_stmt = true;
-                assert!(stmt.is_item());
-
-                // A proc macro can't observe the fact that we're passing
-                // them an `NtStmt` - it can only see the underlying tokens
-                // of the wrapped item
-                token::NtStmt(stmt)
-            }
-            _ => unreachable!(),
-        };
-        let input = if crate::base::pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess)
-        {
-            TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
+        let is_stmt = matches!(item, Annotatable::Stmt(..));
+        let hack = crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess);
+        let input = if hack {
+            let nt = match item {
+                Annotatable::Item(item) => token::NtItem(item),
+                Annotatable::Stmt(stmt) => token::NtStmt(stmt),
+                _ => unreachable!(),
+            };
+            TokenTree::token(token::Interpolated(Lrc::new(nt)), DUMMY_SP).into()
         } else {
-            nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
+            item.to_tokens(&ecx.sess.parse_sess)
         };
 
         let stream = {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index b7230cec3e4..c0c786e4712 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -184,7 +184,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
                     delimiter: pm::Delimiter::None,
                     stream,
                     span: DelimSpan::from_single(span),
-                    flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()),
+                    flatten: crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()),
                 })
             }
 
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index b4262f184c8..3ea68aea3cb 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -18,8 +18,7 @@ use crate::context::{EarlyContext, LintContext, LintStore};
 use crate::passes::{EarlyLintPass, EarlyLintPassObject};
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self as ast_visit, Visitor};
-use rustc_ast::AstLike;
-use rustc_ast::{self as ast, walk_list};
+use rustc_ast::{self as ast, walk_list, HasAttrs};
 use rustc_middle::ty::RegisteredTools;
 use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
 use rustc_session::Session;
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 28c2a63db27..423cddd88ee 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -17,17 +17,17 @@ use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{self, AttributesData, CanSynthesizeMissingTokens, LazyTokenStream};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use rustc_ast::tokenstream::{Spacing, TokenStream};
-use rustc_ast::AstLike;
 use rustc_ast::Attribute;
 use rustc_ast::{AttrItem, MetaItem};
-use rustc_ast_pretty::pprust;
+use rustc_ast::{HasAttrs, HasSpan, HasTokens};
+use rustc_ast_pretty::pprust::{self, AstPrettyPrint};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::{FileName, SourceFile, Span};
 
+use std::fmt;
 use std::path::Path;
-use std::str;
 
 pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments");
 
@@ -244,6 +244,20 @@ pub fn parse_in<'a, T>(
 // NOTE(Centril): The following probably shouldn't be here but it acknowledges the
 // fact that architecturally, we are using parsing (read on below to understand why).
 
+pub fn to_token_stream(
+    node: &(impl HasAttrs + HasSpan + HasTokens + AstPrettyPrint + fmt::Debug),
+    sess: &ParseSess,
+    synthesize_tokens: CanSynthesizeMissingTokens,
+) -> TokenStream {
+    if let Some(tokens) = prepend_attrs(&node.attrs(), node.tokens()) {
+        return tokens;
+    } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) {
+        return fake_token_stream(sess, node);
+    } else {
+        panic!("Missing tokens for nt {:?} at {:?}: {:?}", node, node.span(), node.pretty_print());
+    }
+}
+
 pub fn nt_to_tokenstream(
     nt: &Nonterminal,
     sess: &ParseSess,
@@ -298,7 +312,7 @@ pub fn nt_to_tokenstream(
     if let Some(tokens) = tokens {
         return tokens;
     } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) {
-        return fake_token_stream(sess, nt);
+        return nt_fake_token_stream(sess, nt);
     } else {
         panic!(
             "Missing tokens for nt {:?} at {:?}: {:?}",
@@ -322,7 +336,13 @@ fn prepend_attrs(attrs: &[Attribute], tokens: Option<&LazyTokenStream>) -> Optio
     Some(wrapped.to_tokenstream())
 }
 
-pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream {
+pub fn fake_token_stream(sess: &ParseSess, node: &(impl AstPrettyPrint + HasSpan)) -> TokenStream {
+    let source = node.pretty_print();
+    let filename = FileName::macro_expansion_source_code(&source);
+    parse_stream_from_source_str(filename, source, sess, Some(node.span()))
+}
+
+fn nt_fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream {
     let source = pprust::nonterminal_to_string(nt);
     let filename = FileName::macro_expansion_source_code(&source);
     parse_stream_from_source_str(filename, source, sess, Some(nt.span()))
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 66db5bf9d7c..6c750ff428f 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing};
 use rustc_ast::{self as ast};
-use rustc_ast::{AstLike, AttrVec, Attribute};
+use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens};
 use rustc_errors::PResult;
 use rustc_span::{sym, Span};
 
@@ -192,7 +192,7 @@ impl<'a> Parser<'a> {
     /// This restriction shouldn't be an issue in practice,
     /// since this function is used to record the tokens for
     /// a parsed AST item, which always has matching delimiters.
-    pub fn collect_tokens_trailing_token<R: AstLike>(
+    pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
         &mut self,
         attrs: AttrWrapper,
         force_collect: ForceCollect,
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 63112f23605..f32ee9fc978 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -25,9 +25,9 @@ use rustc_ast::tokenstream::{self, DelimSpan, Spacing};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::AttrId;
 use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern};
+use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern};
 use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacArgsEq, MacDelimiter, Mutability, StrLit};
-use rustc_ast::{Unsafe, Visibility, VisibilityKind};
+use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::PResult;
@@ -1389,7 +1389,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn collect_tokens_no_attrs<R: AstLike>(
+    pub fn collect_tokens_no_attrs<R: HasAttrs + HasTokens>(
         &mut self,
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
     ) -> PResult<'a, R> {
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 6974f318f94..e215b6872bf 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -1,6 +1,6 @@
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, NonterminalKind, Token};
-use rustc_ast::AstLike;
+use rustc_ast::HasTokens;
 use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
 use rustc_span::symbol::{kw, Ident};
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index ac693597662..56ebac0953b 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -13,10 +13,8 @@ use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, TokenKind};
 use rustc_ast::util::classify;
-use rustc_ast::{
-    AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle,
-};
-use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt};
+use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
+use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
 use rustc_span::source_map::{BytePos, Span};
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index befe12ae2c4..41ba9a847e6 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -1,7 +1,7 @@
 //! Format attributes and meta items.
 
 use rustc_ast::ast;
-use rustc_ast::AstLike;
+use rustc_ast::HasAttrs;
 use rustc_span::{symbol::sym, Span, Symbol};
 
 use self::doc_comment::DocCommentFormatter;
diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs
index ca93955a549..ebadf3dd598 100644
--- a/src/tools/rustfmt/src/formatting.rs
+++ b/src/tools/rustfmt/src/formatting.rs
@@ -5,7 +5,6 @@ use std::io::{self, Write};
 use std::time::{Duration, Instant};
 
 use rustc_ast::ast;
-use rustc_ast::AstLike;
 use rustc_span::Span;
 
 use self::newline_style::apply_newline_style;
diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs
index a65dc66f797..81da724329f 100644
--- a/src/tools/rustfmt/src/modules.rs
+++ b/src/tools/rustfmt/src/modules.rs
@@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
 
 use rustc_ast::ast;
 use rustc_ast::visit::Visitor;
-use rustc_ast::AstLike;
 use rustc_span::symbol::{self, sym, Symbol};
 use rustc_span::Span;
 use thiserror::Error;
@@ -50,19 +49,10 @@ impl<'a> Module<'a> {
             ast_mod_kind,
         }
     }
-}
 
-impl<'a> AstLike for Module<'a> {
-    const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
-    fn attrs(&self) -> &[ast::Attribute] {
+    pub(crate) fn attrs(&self) -> &[ast::Attribute] {
         &self.inner_attr
     }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<ast::Attribute>)) {
-        f(&mut self.inner_attr)
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyTokenStream>> {
-        unimplemented!()
-    }
 }
 
 /// Maps each module to the corresponding file.
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index f04fb2e0446..9a0e0752c12 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -1,7 +1,7 @@
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 
-use rustc_ast::{ast, token::Delimiter, visit, AstLike};
+use rustc_ast::{ast, token::Delimiter, visit};
 use rustc_data_structures::sync::Lrc;
 use rustc_span::{symbol, BytePos, Pos, Span};