diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 16b4378e7f7..de44a2031ab 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2912,84 +2912,3 @@ impl TryFrom<ItemKind> for ForeignItemKind {
 }
 
 pub type ForeignItem = Item<ForeignItemKind>;
-
-pub trait HasTokens {
-    /// Called by `Parser::collect_tokens` to store the collected
-    /// tokens inside an AST node
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream);
-}
-
-impl<T: HasTokens + 'static> HasTokens for P<T> {
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        (**self).finalize_tokens(tokens);
-    }
-}
-
-impl<T: HasTokens> HasTokens for Option<T> {
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        if let Some(inner) = self {
-            inner.finalize_tokens(tokens);
-        }
-    }
-}
-
-impl HasTokens for Attribute {
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        match &mut self.kind {
-            AttrKind::Normal(_, attr_tokens) => {
-                if attr_tokens.is_none() {
-                    *attr_tokens = Some(tokens);
-                }
-            }
-            AttrKind::DocComment(..) => {
-                panic!("Called finalize_tokens on doc comment attr {:?}", self)
-            }
-        }
-    }
-}
-
-impl HasTokens for Stmt {
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        let stmt_tokens = match self.kind {
-            StmtKind::Local(ref mut local) => &mut local.tokens,
-            StmtKind::Item(ref mut item) => &mut item.tokens,
-            StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens,
-            StmtKind::Empty => return,
-            StmtKind::MacCall(ref mut mac) => &mut mac.tokens,
-        };
-        if stmt_tokens.is_none() {
-            *stmt_tokens = Some(tokens);
-        }
-    }
-}
-
-macro_rules! derive_has_tokens {
-    ($($ty:path),*) => { $(
-        impl HasTokens for $ty {
-            fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-                if self.tokens.is_none() {
-                    self.tokens = Some(tokens);
-                }
-            }
-        }
-    )* }
-}
-
-derive_has_tokens! {
-    Item, Expr, Ty, AttrItem, Visibility, Path, Block, Pat
-}
-
-macro_rules! derive_has_attrs_no_tokens {
-    ($($ty:path),*) => { $(
-        impl HasTokens for $ty {
-            fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {}
-        }
-    )* }
-}
-
-// These ast nodes only support inert attributes, so they don't
-// store tokens (since nothing can observe them)
-derive_has_attrs_no_tokens! {
-    StructField, Arm,
-    Field, FieldPat, Variant, Param, GenericParam
-}
diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs
new file mode 100644
index 00000000000..6649cda69a0
--- /dev/null
+++ b/compiler/rustc_ast/src/ast_like.rs
@@ -0,0 +1,219 @@
+use super::ptr::P;
+use super::tokenstream::LazyTokenStream;
+use super::{Arm, Field, FieldPat, GenericParam, Param, StructField, Variant};
+use super::{AssocItem, Expr, ForeignItem, Item, Local};
+use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
+use super::{AttrVec, Attribute, Stmt, StmtKind};
+
+/// 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 {
+    fn attrs(&self) -> &[Attribute];
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
+    /// Called by `Parser::collect_tokens` to store the collected
+    /// tokens inside an AST node
+    fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {
+        // This default impl makes this trait easier to implement
+        // in tools like `rust-analyzer`
+        panic!("`finalize_tokens` is not supported!")
+    }
+}
+
+impl<T: AstLike + 'static> AstLike for P<T> {
+    fn attrs(&self) -> &[Attribute] {
+        (**self).attrs()
+    }
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        (**self).visit_attrs(f);
+    }
+    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        (**self).finalize_tokens(tokens)
+    }
+}
+
+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 {
+    fn attrs(&self) -> &[Attribute] {
+        match *self {
+            StmtKind::Local(ref local) => local.attrs(),
+            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
+            StmtKind::Item(ref item) => item.attrs(),
+            StmtKind::Empty => &[],
+            StmtKind::MacCall(ref 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 finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        let stmt_tokens = match self {
+            StmtKind::Local(ref mut local) => &mut local.tokens,
+            StmtKind::Item(ref mut item) => &mut item.tokens,
+            StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens,
+            StmtKind::Empty => return,
+            StmtKind::MacCall(ref mut mac) => &mut mac.tokens,
+        };
+        if stmt_tokens.is_none() {
+            *stmt_tokens = Some(tokens);
+        }
+    }
+}
+
+impl AstLike for Stmt {
+    fn attrs(&self) -> &[Attribute] {
+        self.kind.attrs()
+    }
+
+    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+        self.kind.visit_attrs(f);
+    }
+    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        self.kind.finalize_tokens(tokens)
+    }
+}
+
+impl AstLike for Attribute {
+    fn attrs(&self) -> &[Attribute] {
+        &[]
+    }
+    fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
+    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        match &mut self.kind {
+            AttrKind::Normal(_, attr_tokens) => {
+                if attr_tokens.is_none() {
+                    *attr_tokens = Some(tokens);
+                }
+            }
+            AttrKind::DocComment(..) => {
+                panic!("Called finalize_tokens on doc comment attr {:?}", self)
+            }
+        }
+    }
+}
+
+impl<T: AstLike> AstLike for Option<T> {
+    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 finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        if let Some(inner) = self {
+            inner.finalize_tokens(tokens);
+        }
+    }
+}
+
+/// 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 {
+    ($($ty:path),*) => { $(
+        impl AstLike for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                &self.attrs
+            }
+
+            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+                VecOrAttrVec::visit(&mut self.attrs, f)
+            }
+
+            fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+                if self.tokens.is_none() {
+                    self.tokens = Some(tokens);
+                }
+
+            }
+        }
+    )* }
+}
+
+macro_rules! derive_has_attrs_no_tokens {
+    ($($ty:path),*) => { $(
+        impl AstLike for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                &self.attrs
+            }
+
+            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
+                VecOrAttrVec::visit(&mut self.attrs, f)
+            }
+
+            fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {}
+        }
+    )* }
+}
+
+macro_rules! derive_has_tokens_no_attrs {
+    ($($ty:path),*) => { $(
+        impl AstLike for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                &[]
+            }
+
+            fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {
+            }
+
+            fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+                if self.tokens.is_none() {
+                    self.tokens = Some(tokens);
+                }
+
+            }
+        }
+    )* }
+}
+
+// These AST nodes support both inert and active
+// attributes, so they also have tokens.
+derive_has_tokens_and_attrs! {
+    Item, Expr, Local, AssocItem, ForeignItem
+}
+
+// These ast nodes only support inert attributes, so they don't
+// store tokens (since nothing can observe them)
+derive_has_attrs_no_tokens! {
+    StructField, Arm,
+    Field, FieldPat, Variant, Param, GenericParam
+}
+
+// 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
+}
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 4dcbe4831be..52ac7540f69 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -1,17 +1,15 @@
 //! Functions dealing with attributes and meta items.
 
 use crate::ast;
-use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
-use crate::ast::{Expr, GenericParam, Item, Lit, LitKind, Local, Stmt, StmtKind};
+use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
+use crate::ast::{Lit, LitKind};
 use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Path, PathSegment};
-use crate::mut_visit::visit_clobber;
-use crate::ptr::P;
 use crate::token::{self, CommentKind, Token};
 use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing};
 
 use rustc_index::bit_set::GrowableBitSet;
-use rustc_span::source_map::{BytePos, Spanned};
+use rustc_span::source_map::BytePos;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 
@@ -617,101 +615,3 @@ impl NestedMetaItem {
         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
     }
 }
-
-pub trait HasAttrs: Sized {
-    fn attrs(&self) -> &[Attribute];
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
-}
-
-impl<T: HasAttrs> HasAttrs for Spanned<T> {
-    fn attrs(&self) -> &[Attribute] {
-        self.node.attrs()
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        self.node.visit_attrs(f);
-    }
-}
-
-impl HasAttrs for Vec<Attribute> {
-    fn attrs(&self) -> &[Attribute] {
-        self
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        f(self)
-    }
-}
-
-impl HasAttrs for AttrVec {
-    fn attrs(&self) -> &[Attribute] {
-        self
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        visit_clobber(self, |this| {
-            let mut vec = this.into();
-            f(&mut vec);
-            vec.into()
-        });
-    }
-}
-
-impl<T: HasAttrs + 'static> HasAttrs for P<T> {
-    fn attrs(&self) -> &[Attribute] {
-        (**self).attrs()
-    }
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        (**self).visit_attrs(f);
-    }
-}
-
-impl HasAttrs for StmtKind {
-    fn attrs(&self) -> &[Attribute] {
-        match *self {
-            StmtKind::Local(ref local) => local.attrs(),
-            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
-            StmtKind::Item(ref item) => item.attrs(),
-            StmtKind::Empty => &[],
-            StmtKind::MacCall(ref mac) => mac.attrs.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) => {
-                mac.attrs.visit_attrs(f);
-            }
-        }
-    }
-}
-
-impl HasAttrs for Stmt {
-    fn attrs(&self) -> &[ast::Attribute] {
-        self.kind.attrs()
-    }
-
-    fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-        self.kind.visit_attrs(f);
-    }
-}
-
-macro_rules! derive_has_attrs {
-    ($($ty:path),*) => { $(
-        impl HasAttrs for $ty {
-            fn attrs(&self) -> &[Attribute] {
-                &self.attrs
-            }
-
-            fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
-                self.attrs.visit_attrs(f);
-            }
-        }
-    )* }
-}
-
-derive_has_attrs! {
-    Item, Expr, Local, ast::AssocItem, ast::ForeignItem, ast::StructField, ast::Arm,
-    ast::Field, ast::FieldPat, ast::Variant, ast::Param, GenericParam
-}
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index ddf52caed08..4eaef85043c 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -40,6 +40,7 @@ pub mod util {
 }
 
 pub mod ast;
+pub mod ast_like;
 pub mod attr;
 pub mod crate_disambiguator;
 pub mod entry;
@@ -52,6 +53,7 @@ pub mod tokenstream;
 pub mod visit;
 
 pub use self::ast::*;
+pub use self::ast_like::AstLike;
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index ca304c05cdc..ce8103c0f85 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -3,10 +3,10 @@ use crate::module::DirectoryOwnership;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Nonterminal};
-use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
+use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
 use rustc_ast::visit::{AssocCtxt, Visitor};
-use rustc_ast::{self as ast, Attribute, NodeId, PatKind};
-use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
+use rustc_ast::{self as ast, AstLike, Attribute, NodeId, PatKind};
+use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{DiagnosticBuilder, ErrorReported};
@@ -44,7 +44,7 @@ pub enum Annotatable {
     Variant(ast::Variant),
 }
 
-impl HasAttrs for Annotatable {
+impl AstLike for Annotatable {
     fn attrs(&self) -> &[Attribute] {
         match *self {
             Annotatable::Item(ref item) => &item.attrs,
@@ -80,6 +80,10 @@ impl HasAttrs for Annotatable {
             Annotatable::Variant(v) => v.visit_attrs(f),
         }
     }
+
+    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
+        panic!("Called finalize_tokens on an Annotatable: {:?}", tokens);
+    }
 }
 
 impl Annotatable {
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index ad04fa9a958..7d0becf1f5d 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -2,12 +2,11 @@
 
 use crate::base::Annotatable;
 
-use rustc_ast::attr::HasAttrs;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{DelimToken, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree};
-use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem};
+use rustc_ast::{self as ast, AstLike, AttrItem, Attribute, MetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -205,7 +204,7 @@ pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features)
     let unconfigured_attrs = krate.attrs.clone();
     let diag = &sess.parse_sess.span_diagnostic;
     let err_count = diag.err_count();
-    let features = match strip_unconfigured.configure(krate.attrs) {
+    let features = match strip_unconfigured.configure_krate_attrs(krate.attrs) {
         None => {
             // The entire crate is unconfigured.
             krate.attrs = Vec::new();
@@ -218,7 +217,9 @@ pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features)
             if err_count == diag.err_count() {
                 // Avoid reconfiguring malformed `cfg_attr`s.
                 strip_unconfigured.features = Some(&features);
-                strip_unconfigured.configure(unconfigured_attrs);
+                // Run configuration again, this time with features available
+                // so that we can perform feature-gating.
+                strip_unconfigured.configure_krate_attrs(unconfigured_attrs);
             }
             features
         }
@@ -242,7 +243,7 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
     #the-cfg_attr-attribute>";
 
 impl<'a> StripUnconfigured<'a> {
-    pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
+    pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> {
         self.process_cfg_attrs(&mut node);
         if self.in_cfg(node.attrs()) {
             Some(node)
@@ -252,13 +253,26 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
+    fn configure_krate_attrs(
+        &mut 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 {
+            self.modified = true;
+            None
+        }
+    }
+
     /// Parse and expand all `cfg_attr` attributes into a list of attributes
     /// that are within each `cfg_attr` that has a true configuration predicate.
     ///
     /// 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.
-    pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
+    pub fn process_cfg_attrs<T: AstLike>(&mut 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 10c19ea105e..b474cad1242 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -12,11 +12,11 @@ use rustc_ast::ptr::P;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{AttrItem, AttrStyle, Block, Inline, ItemKind, LitKind, MacArgs};
+use rustc_ast::{AstLike, AttrItem, AttrStyle, Block, Inline, ItemKind, LitKind, MacArgs};
 use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
 use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
 use rustc_ast_pretty::pprust;
-use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
+use rustc_attr::{self as 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;
@@ -1014,7 +1014,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// legacy derive helpers (helpers written before derives that introduce them).
     fn take_first_attr(
         &mut self,
-        item: &mut impl HasAttrs,
+        item: &mut impl AstLike,
     ) -> Option<(ast::Attribute, usize, Vec<Path>)> {
         let mut attr = None;
 
@@ -1045,7 +1045,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         attr
     }
 
-    fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
         self.cfg.configure(node)
     }
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 71017fcde87..4258a4b4237 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -24,7 +24,7 @@ use crate::{
     types::{transparent_newtype_field, CItemKind},
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
 };
-use rustc_ast::attr::{self, HasAttrs};
+use rustc_ast::attr;
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{self as ast, *};
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index cc3bf4095fd..0c7b843831a 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -18,7 +18,7 @@ use crate::context::{EarlyContext, LintContext, LintStore};
 use crate::passes::{EarlyLintPass, EarlyLintPassObject};
 use rustc_ast as ast;
 use rustc_ast::visit as ast_visit;
-use rustc_attr::HasAttrs;
+use rustc_ast::AstLike;
 use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index f155f3a94e5..cea4de72df5 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -8,9 +8,9 @@
 #![feature(box_patterns)]
 
 use rustc_ast as ast;
-use rustc_ast::attr::HasAttrs;
 use rustc_ast::token::{self, Nonterminal};
 use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
+use rustc_ast::AstLike;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Diagnostic, FatalError, Level, PResult};
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index aea7c6b42cf..f45d8d6c7a0 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -3,7 +3,7 @@ use super::{ForceCollect, Parser, TokenCursor, TrailingToken};
 use rustc_ast::token::{self, Token, TokenKind};
 use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing};
-use rustc_ast::HasTokens;
+use rustc_ast::AstLike;
 use rustc_ast::{self as ast};
 use rustc_errors::PResult;
 use rustc_span::{Span, DUMMY_SP};
@@ -59,7 +59,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: HasTokens>(
+    pub fn collect_tokens_trailing_token<R: AstLike>(
         &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 8a097bf481d..4cc2224d27e 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -22,7 +22,7 @@ use rustc_ast::token::{self, DelimToken, Token, TokenKind};
 use rustc_ast::tokenstream::{self, DelimSpan, Spacing};
 use rustc_ast::tokenstream::{TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, HasTokens};
+use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern};
 use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe};
 use rustc_ast::{Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
@@ -1228,7 +1228,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn collect_tokens_no_attrs<R: HasTokens>(
+    pub fn collect_tokens_no_attrs<R: AstLike>(
         &mut self,
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
     ) -> PResult<'a, R> {
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index bb2c7e98861..07746f2390d 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -8,10 +8,10 @@ use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColo
 use crate::maybe_whole;
 
 use rustc_ast as ast;
-use rustc_ast::attr::HasAttrs;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, TokenKind};
 use rustc_ast::util::classify;
+use rustc_ast::AstLike;
 use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID};
 use rustc_errors::{Applicability, PResult};